美文网首页Android
ViewPager2快速入门

ViewPager2快速入门

作者: 超级绿茶 | 来源:发表于2020-01-05 08:21 被阅读0次

    ViewPager2是Google用来代替旧有的ViewPager而重新设计的控件,加入了一些旧版所没有的功能,并替换掉了一些我们熟知的方法。以达到使用的便捷和性能的优化。​为了能让以前的项目继续正常运行,Google仍旧保留了旧有的ViewPager,两者的关系相当于ListView和RecyclerView的关系。

    前期准备

    ViewPager2在使用前需要在app的build.gradle中引入:

     implementation 'androidx.viewpager2:viewpager2:1.0.0'
    

    如果项目中需要用到ViewPager2和TabLayout进行联动的话还需要引入:

    implementation 'com.google.android.material:material:1.2.0-alpha03'
    

    滑动方向

    ViewPager2除了常规的左右滑动外还加入了上下滑动。可以通过setOrientation方法指定滑动的方向。

    public void setOrientation(@Orientation int orientation) 
    

    参数orientation的取值范围如下:

    • ViewPager2.ORIENTATION_VERTICAL - 上下滑动
    • ViewPager2.ORIENTATION_HORIZONTAL - 左右滑动

    用RecyclerView.Adapter代替PagerAdapter

    为了便于学习和记忆,谷歌直接使用RecyclerView的Adapter作为ViewPager2的适配器。这样设计好处是可以直接使用各种notifyItem开头的局部刷新方法,同时以前针对ReyclerView的Adapter的各种第三方工具类也能用了。

    viewPager2.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): RecyclerView.ViewHolder {
            // 在此实例化View视图
            val view = LayoutInflater.from(parent.context).inflate(R.layout.item_main_1,parent, false)
            return object : RecyclerView.ViewHolder(view) {}
        }
    
        override fun getItemCount(): Int {
            return listDataHor.size
        }
    
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            // 根据集合指定位置的对象填充视图
            holder.itemView.let {
                it.tvName.text = listDataHor[position].name
                it.tvAge.text = "${listDataHor[position].age}岁"
                it.tvPhone.text = "联系电话:${listDataHor[position].phone}"
            }
        }
    }
    

    Pages must fill the whole ViewPager2 (use match_parent)异常的处理:

    遇到这种异常无非是以下两种情况造成的。

    1. Item的XML布局的外层用了"wrap_content",应改为了"match_parent"
    2. 在onCreateViewHolder方法里实例化View时遇到inflate方法时第二个参数不要传null而是传parent,例如:
    LayoutInflater.from(parent.context).inflate(R.layout.item_main_1, parent, false)
    

    全新的FragmentStateAdapter

    以前除了PageAdapte外还有FragmentPageAdapter和FragmentStatePageAdapter这两个专门用于处理Item项是Fragment的情况。现在ViewPager2中直接用FragmentStateAdapter替代之前的两个适配器类。
    FragmentStateAdapter实际是继承了RecyclerView.Adapter的抽象类,调用的方法也比以前简化了许多:

    // FragementStateAdapter的构造器需要传入一个FragmentActivity的实例
    // 这里是在AppCompatActivity中调用的,所以直接传this
    viewPager2.adapter = object : FragmentStateAdapter(this) {
        override fun getItemCount(): Int {
            // 返回集合的长度,Fragment的数量就是根据这个集合来创建的
            return listDataFragment.size
        }
    
        override fun createFragment(position: Int): Fragment {
            // 实例化Fragment
            return ItemFragment.newInstance()
        }
    }
    

    页面滑动响应registerOnPageChangeCallback

    registerOnPageChangeCallback用于注册ViewPager2的滑动响应事件。在手势拖拽或滑到下一页时都会触发该事件,这次的registerOnPageChangeCallback需要传入的是一个OnPageChangeCallback的抽象类,可以根据需求只重写我们需要的方法,而不必像以前那样每次都要重写三个方法;

    vp2Fragment.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
        override fun onPageScrollStateChanged(state: Int) {
            super.onPageScrollStateChanged(state)
        }
    
        override fun onPageScrolled(position: Int,positionOffset: Float,positionOffsetPixels: Int) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels)
        }
    
        override fun onPageSelected(position: Int) {
            super.onPageSelected(position)
            // 很多情况下在页面切换完成后在此方法进行各种操作
        }
    })
    
    • onPageScrollStateChanged:当滑动状态改变时响应,通过判断state的值来确定当前的滑动状态,ViewPager2定义了三种状态的常量SCROLL_STATE_IDLE、SCROLL_STATE_DRAGGING、SCROLL_STATE_SETTLING
    • onPageScrolled:滑动进行时响应。
    • onPageSelected:滑动完成时响应,这个方法的使用率最高,一般我们会在滑到指定页面时对Fragment进行数据加载或更新等操作。

    因为onPageChangeCallback是抽象类,所以多数情况下更多的是重写onPageSelected方法即可。

    用TabLayoutMediator绑定TabLayout和ViewPager2的互动

    TabLayoutMediator这个类也是新加出来的,使用前需要在build.gradle入material库才可以。TabLayoutMediator的主要是作用就是将TabLayout和ViewPager2进行绑定,然后通过回调类来更新TabLayout的状态;

    TabLayoutMediator(tabLayout, viewPager2,
        TabLayoutMediator.TabConfigurationStrategy { tab, position ->
            // tab:当前处于选中状态的Tab对象
            // position:当前Tab所处的位置
        }
    ).attach() // 不要忘记,否则没效果
    

    ViewPager2在用法上尽量和旧版的ViewPager保持相似,但两者的内部工作原理变化较大,加之ViewPager2出现的时间还不长,是随着androidX一起发布的,所以一些更复杂的用法还有待于以后的更新发掘。在这里写了一个简易的DEMO以供参考:
    MainActivity,kt

    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.util.Log
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import androidx.fragment.app.Fragment
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.LifecycleObserver
    import androidx.lifecycle.OnLifecycleEvent
    import androidx.recyclerview.widget.RecyclerView
    import androidx.viewpager2.adapter.FragmentStateAdapter
    import androidx.viewpager2.widget.ViewPager2
    import com.google.android.material.tabs.TabLayoutMediator
    import kotlinx.android.synthetic.main.activity_main.*
    import kotlinx.android.synthetic.main.item_main_1.view.*
    import java.util.*
    
    data class UserBean(val name: String, val age: Int, val phone: String)
    
    class MainActivity : AppCompatActivity() {
        private val listDataHor = mutableListOf<UserBean>()
        private val listDataFragment = mutableListOf<UserBean>()
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            chkVertical.setOnClickListener {
                // 设置ViewPager的滑动方向
                vp2Normal.orientation = if (chkVertical.isChecked)
                    ViewPager2.ORIENTATION_VERTICAL
                else
                    ViewPager2.ORIENTATION_HORIZONTAL
            }
            initViewPagerWithView()
            initViewPagerWithFragment()
            initTabViewPager()
        }
    
        private fun initViewPagerWithView() {
            vp2Normal.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
                override fun onCreateViewHolder(
                    parent: ViewGroup,
                    viewType: Int
                ): RecyclerView.ViewHolder {
                    val view =
                        LayoutInflater.from(parent.context).inflate(R.layout.item_main_1, parent, false)
                    return object : RecyclerView.ViewHolder(view) {}
                }
    
                override fun getItemCount(): Int {
                    return listDataHor.size
                }
    
                override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
                    holder.itemView.let {
                        it.tvName.text = listDataHor[position].name
                        it.tvAge.text = "${listDataHor[position].age}岁"
                        it.tvPhone.text = "联系电话:${listDataHor[position].phone}"
                    }
                }
            }
            // 生成模拟数据
            lifecycle.addObserver(object : LifecycleObserver {
                @OnLifecycleEvent(Lifecycle.Event.ON_START)
                fun onStart() {
                    refreshListData(listDataHor)
                    vp2Normal.adapter?.notifyDataSetChanged()
                }
            })
        }
    
        private fun refreshListData(listData: MutableList<UserBean>) {
            val arrName = arrayOf("张三", "李四", "王五", "赵六", "孙七", "周八", "吴九", "郑十")
            repeat(10) {
                val name = arrName[Random().nextInt(arrName.size)]
                val age = Random().nextInt(10) + 20
                val user = UserBean(name, age, System.currentTimeMillis().toString())
                listData += user
                Thread.sleep(10)
            }
        }
    
        /**
         * ViewPager2和Fragment的组合
         */
        private fun initViewPagerWithFragment() {
            vp2Fragment.adapter = object : FragmentStateAdapter(this) {
                override fun getItemCount(): Int {
                    return listDataFragment.size
                }
    
                override fun createFragment(position: Int): Fragment {
                    val userInfo = listDataFragment[position].let {
                        "姓名:" + it.name + "\n年龄:" + it.age + "\n手机:" + it.phone
                    }
                    return ItemFragment.newInstance(userInfo)
                }
            }
    
            /**
             * 注册页面滑动时的回调事件
             * OnPageChangeCallback是抽象类,这回终于不用每次都重写三个方法了
             */
            vp2Fragment.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                override fun onPageScrollStateChanged(state: Int) {
                    super.onPageScrollStateChanged(state)
                    Log.i("123", "onPageScrollStateChanged: $state")
                }
    
                override fun onPageScrolled(
                    position: Int,
                    positionOffset: Float,
                    positionOffsetPixels: Int
                ) {
                    super.onPageScrolled(position, positionOffset, positionOffsetPixels)
                    Log.i("123", "onPageScrolled:$position $positionOffset $positionOffsetPixels")
                }
    
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)
                    // 很多情况下在页面切换完成后在此方法进行各种操作
                    Log.i("123", "onPageSelected:$position")
                }
            })
            // 生成模拟数据
            lifecycle.addObserver(object : LifecycleObserver {
                @OnLifecycleEvent(Lifecycle.Event.ON_START)
                fun onStart() {
                    refreshListFragment(listDataFragment)
                    vp2Fragment.adapter?.notifyDataSetChanged()
                }
            })
        }
    
        /**
         * 绑定TabLayout和ViewPager2的双向联动
         */
        private fun initTabViewPager() {
            TabLayoutMediator(tabLayout, vp2Fragment,
                TabLayoutMediator.TabConfigurationStrategy { tab, position ->
                    tab.text = listDataFragment[position].name
                }
            ).attach()
        }
    
        /**
         * 生成模拟数据
         */
        private fun refreshListFragment(listData: MutableList<UserBean>) {
            val arrName = arrayOf("张三", "李四", "王五", "赵六", "孙七", "周八", "吴九", "郑十")
            arrName.forEach {
                val name = it
                val age = Random().nextInt(10) + 20
                val user = UserBean(name, age, System.currentTimeMillis().toString())
                listData += user
                Thread.sleep(10)
            }
        }
    }
    

    ItemFragment.kt

    
    import android.os.Bundle
    import android.util.Log
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import androidx.fragment.app.Fragment
    import kotlinx.android.synthetic.main.fragment_item.view.*
    
    const val TAG = "ItemFragment"
    
    class ItemFragment : Fragment() {
    
        companion object {
            fun newInstance(userInfo: String) = ItemFragment().apply {
                arguments = Bundle().apply { putString("userInfo", userInfo) }
            }
        }
    
        private var userInfo = ""
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            Log.i(TAG, "${this.javaClass.simpleName} :onCreateView")
            val view = inflater.inflate(R.layout.fragment_item, container, false)
            userInfo = arguments?.let { it.getString("userInfo") } ?: ""
            view.tvUserInfo.text = userInfo
            return view
        }
    
        override fun onStart() {
            super.onStart()
            Log.i(TAG, "${this.javaClass.simpleName} $userInfo:onStart")
        }
    
        override fun onPause() {
            super.onPause()
            Log.i(TAG, "${this.javaClass.simpleName} $userInfo:onPause")
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.i(TAG, "${this.javaClass.simpleName} :onDestory")
        }
    }
    

    源码下载:ViewPager2Demo.rar

    点击链接加入QQ群聊:https://jq.qq.com/?_wv=1027&k=5z4fzdT
    或关注微信公众号:口袋里的安卓

    口袋里的安卓

    相关文章

      网友评论

        本文标题:ViewPager2快速入门

        本文链接:https://www.haomeiwen.com/subject/mrcfactx.html