今天通過 ViewPager 做一個類似 iOS 中的 UIScrollView 的功能。
Android 不像 iOS 有原生的 UIPageController,也就是提供有圓點的 Indicator ,所以乾脆改成顯示文字了。
- 通過滑動畫面可以切換圖片。
- 換圖片的過程畫面底部的數字會切換,左邊的數字代表當前第幾張,右邊代表總共的圖片數。
ViewPager
這是一個容器類,需要 PagerAdapter 來提供數據,ViewPager 經常和 Fragment 一起使用。
這裡的 PagerAdapter 有點像是 iOS 開發中 UICollectionView 的 Delegate + Datasource。
我們將 Activity 上拿到的 data 交給 Adapter 處理,Adapter 在將 data 和 Fragment 綁定(比如呈現在 Fragment 的畫面上)
MainLayout
在 activity_main.xml 中,上面放一個 ViewPager (id=pager),用來放多個 Fragment(呈現不同的圖片)
下面放一個用來顯示「當下第幾張圖/總共幾張圖」的 textView(id=pageCountTextView)
Fragment Slider
在 fragment_slider.xml 中,定義一個放一張圖片的 Fragment 之後放到 ViewPager 中使用。
其中 imageView 的 ID 就取名叫 ImageView
MainActivity
我們先建立一個 IntArray 用來管理 images
(前面的動手做有提到, Android 的 Resource 都會有個對應的 Int,對 ImageView setImageResource 的時候是通過 Int)
我們實例化 adapter,並且加入一個 pageChangeListener 的監聽,當頁面切換的時候修改 畫面底部的數字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class MainActivity : AppCompatActivity() { lateinit var pager: ViewPager val images: IntArray = intArrayOf(R.drawable.img_1, R.drawable.img_2, R.drawable.img_3, R.drawable.img_4, R.drawable.img_5, R.drawable.img_6) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupView() } private fun setupView() { val adapter: PagerAdapter = SliderPagerAdapter(this, images) pager = findViewById(R.id.pager) pager.adapter = adapter pager.addOnPageChangeListener(object: OnPageChangeListener{ override fun onPageScrollStateChanged(state: Int) { println("on page scroll state changed $state") } override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { val currentPosition = position + 1 pageCountTextView.text = "$currentPosition / ${images.size}" } override fun onPageSelected(position: Int) { println("on page selected $position") } }) } } |
PagerAdapter
四個需要複寫的方法:
- isViewFromObject()
- getCount()
- instantiateItem()
- destoryItem()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class SliderPagerAdapter : PagerAdapter { val context: Context val images: IntArray lateinit var inflator: LayoutInflater constructor(context: Context, images: IntArray) : super(){ this.context = context this.images = images } override fun isViewFromObject(view: View?, `object`: Any?): Boolean { return view == `object` as ConstraintLayout } override fun getCount(): Int { return images.size } override fun instantiateItem(container: ViewGroup?, position: Int): Any { inflator = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val rv: View = inflator.inflate(R.layout.fragment_slider, container,false) rv.imageView.setImageResource(images[position]) container!!.addView(rv) return rv } override fun destroyItem(container: ViewGroup?, position: Int, `object`: Any?) { container!!.removeView(`object` as ConstraintLayout) } } |
筆記
- 問題:findViewByID 可以理解成類似 iOS 通過 XIB 來實例化介面的工具嗎?
- 問題:我在 Activity 建立了一份 images,而之後又將 images 丟給 PagerAdapter 這樣其實在記憶體上算兩份吧。
這不像 UIViewControllerDatasource 經常寫在 ViewController 上,共用一份 images。
可以在這方面多思考一下。
參考
- 官方文件 – ViewPager
- 可以到 Github 上看對應的 SourceCode