Android

ViewPager2快速入门

2020-01-05  本文已影响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的取值范围如下:

用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)
        // 很多情况下在页面切换完成后在此方法进行各种操作
    }
})

因为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
或关注微信公众号:口袋里的安卓

口袋里的安卓
上一篇下一篇

猜你喜欢

热点阅读