ViewPager2和Fragment的组合使用

2019年11月20号,期待已久的ViewPager2 正式版终于发布了!

一、ViewPager2的新特性

ViewPager2从名字就可以看出来它是ViewPager的升级版,既然是升级版那么它相比ViewPager有哪些新功能和哪些API变化呢?我们接着往下看。

1.ViewPager2新特性

基于RecyclerView实现。这意味着RecyclerView的优点将会被ViewPager2所继承。支持竖直滑动。只需要一个参数就可以改变滑动方向。支持关闭用户输入。通过setUserInputEnabled来设置是否禁止用户滑动页面。支持通过编程方式滚动。通过fakeDragBy(offsetPx)代码模拟用户滑动页面。CompositePageTransformer 支持同时添加多个PageTransformer。支持DiffUtil ,可以添加数据集合改变的item动画。支持RTL (right-to-left)布局。我觉得这个功能对国内开发者来说可能用处不大…

2.相比ViewPager变化的API

ViewPager2相比ViewPager做了哪些改变呢?研究了一番之后我大概列出以下几点:

ViewPager2与ViewPager同是继承自ViewGrop,但是ViewPager2被声明成了final。意味着我们不可能再像ViewPager一样通过继承来修改ViewPager2的代码。FragmentStatePagerAdapter被FragmentStateAdapter 替代PagerAdapter被RecyclerView.Adapter替代addPageChangeListener被registerOnPageChangeCallback。我们知道ViewPager的addPageChangeListener接收的是一个OnPageChangeListener的接口,而这个接口中有三个方法,当想要监听页面变化时需要重写这三个方法。而ViewPager2的registerOnPageChangeCallback方法接收的是一个叫OnPageChangeCallback的抽象类,因此我们可以选择性的重写需要的方法即可。移除了setPargeMargin方法。

以上所罗列的新特性和API可能并不完整,如有疏漏可以留言补充。

二、开启ViewPager2之旅

ViewPager2位于androidx包下,也就是它不像ViewPager一样被内置在系统源码中。因此,使用ViewPager2需要额外的添加依赖库。另外,android support中不包含ViewPager,也就是要使用ViewPager2必须迁移到androidx才可以。

1.添加依赖,目前ViewPager2的最新版本是1.0.0:

dependencies {
    implementation "androidx.viewpager2:viewpager2:1.0.0"
}

2.ViewPager2布局文件:

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/view_pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

3.ViewPager2的Adapter

因为ViewPager2内部封装的是RecyclerView,因此它的Adapter也就是RecyclerView的Adapter。

class MyAdapter : RecyclerView.Adapter<MyAdapter.PagerViewHolder>() {
    private var mList: List<Int> = ArrayList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_page, parent, false)
        return PagerViewHolder(itemView)
    }

   override fun onBindViewHolder(holder: PagerViewHolder, position: Int) {
        holder.bindData(mList[position])
    }

   fun setList(list: List<Int>) {
        mList = list
    }

   override fun getItemCount(): Int {
        return mList.size
    }
	//	ViewHolder需要继承RecycleView.ViewHolder
    class PagerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val mTextView: TextView = itemView.findViewById(R.id.tv_text)
        private var colors = arrayOf("#CCFF99","#41F1E5","#8D41F1","#FF99CC")

   fun bindData(i: Int) {
            mTextView.text = i.toString()
            mTextView.setBackgroundColor(Color.parseColor(colors[i]))
        }
    }
}

item_page中代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <TextView
        android:id="@+id/tv_text"
        android:background="@color/colorPrimaryDark"
        android:layout_width="match_parent"
        android:layout_height="280dp"
        android:gravity="center"
        android:textColor="#ffffff"
        android:textSize="22sp" />
</LinearLayout>

4.在Activity中为ViewPager设置Adapter:

val viewPager2 = findViewById<ViewPager2>(R.id.view_pager)
    val myAdapter = MyAdapter()
    myAdapter.setList(data)
    viewPager2.adapter = myAdapter

很简单就完成了一个ViewPager的功能

5.ViewPager2竖直滑动

接下来我们通过一行代码为其设置竖直滑动

viewPager2.orientation = ViewPager2.ORIENTATION_VERTICAL

竖直滑动用ViewPager是很难实现的,而通过ViewPager2只需要设置一个参数即可。

6.页面滑动事件监听

上文已经提到过了,我们为ViewPager设置页面滑动的监听事件需要重写三个方法,而为ViewPager2设置监听事件只需要重写需要的方法即可,因为ViewPager2中OnPageChangeCallback是一个抽象类。

viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                Toast.makeText(this@MainActivity, "page selected $position", Toast.LENGTH_SHORT).show()
            }
        })

7.setUserInputEnabled与fakeDragBy

我们知道,在使用ViewPager的时候想要禁止用户滑动需要重写ViewPager的onInterceptTouchEvent。而ViewPager2被声明为了final,我们无法再去继承ViewPager2。那么我们应该怎么禁止ViewPager2的滑动呢?其实在ViewPager2中已经为我们提供了这个功能,只需要通过setUserInputEnabled即可实现。

viewPager2.isUserInputEnabled = false

同时ViewPager2新增了一个fakeDragBy的方法。通过这个方法可以来模拟拖拽。在使用fakeDragBy前需要先beginFakeDrag方法来开启模拟拖拽。fakeDragBy会返回一个boolean值,true表示有fake drag正在执行,而返回false表示当前没有fake drag在执行。我们通过代码来尝试下:

fun fakeDragBy(view: View) {
        viewPager2.beginFakeDrag()
        if (viewPager2.fakeDragBy(-310f))
            viewPager2.endFakeDrag()
    }

需要注意到是fakeDragBy接受一个float的参数,当参数值为正数时表示向前一个页面滑动,当值为负数时表示向下一个页面滑动。

三、ViewPager2的PageTransformer

相比ViewPager,ViewPager2的Transformer功能有了很大的扩展。ViewPager2不仅可以通过PageTransformer不仅可以用来设置pageMarge,还可以同时添加多个PageTransformer。接下来我们就来认识下ViewPager2的PageTransformer吧!

1.setPageMarge

在第一章中我们提到了ViewPager2移除了setPageMargin方法,那么怎么为ViewPager2设置页面间距呢?其实在ViewPager2中为我们提供了MarginPageTransformer,我们可以通过ViewPager的setPageTransformer方法来设置页面间距。代码如下:

viewPager2.setPageTransformer(MarginPageTransformer(resources.getDimension(R.dimen.dp_10).toInt()))

上述代码我们为ViewPager2设置了10dp的页面间距。

2.认识CompositePageTransformer

这个时候我们应该有个疑问,为ViewPager2设置了页面间距后如果还想设置页面动画的Transformer怎么办呢?这时候就该CompositePageTransformer出场了。从名字上也可以看出来它是一个组合的PageTransformer。没错,CompositePageTransformer实现了PageTransformer接口,同时在其内部维护了一个List集合,我们可以将多个PageTransformer添加到CompositePageTransformer中。

val compositePageTransformer = CompositePageTransformer()
    compositePageTransformer.addTransformer(ScaleInTransformer())
    compositePageTransformer.addTransformer(MarginPageTransformer(resources.getDimension(R.dimen.dp_10).toInt()))
    viewPager2.setPageTransformer(compositePageTransformer)

上述代码中我们通过CompositePageTransformer为ViewPager设置了MarginPageTransformer和一个页面缩放的ScaleInTransformer。

3.ViewPager2中的PageTransformer

PageTransformer是一个位于ViewPager2中的接口,因此ViewPager2的PageTransformer是独立于ViewPager的,它与ViewPager的PageTransformer没有任何关系。虽然如此,却不必担心。因为ViewPager2的PageTransformer和ViewPager的PageTransformer实现方式一模一样。我们看下上一小节中用到的ScaleInTransformer:

class ScaleInTransformer : ViewPager2.PageTransformer {
    private val mMinScale = DEFAULT_MIN_SCALE
    override fun transformPage(view: View, position: Float) {
        view.elevation = -abs(position)
        val pageWidth = view.width
        val pageHeight = view.height

        view.pivotY = (pageHeight / 2).toFloat()
        view.pivotX = (pageWidth / 2).toFloat()
        if (position < -1) {
            view.scaleX = mMinScale
            view.scaleY = mMinScale
            view.pivotX = pageWidth.toFloat()
        } else if (position <= 1) {
            if (position < 0) {
                val scaleFactor = (1 + position) * (1 - mMinScale) + mMinScale
                view.scaleX = scaleFactor
                view.scaleY = scaleFactor
                view.pivotX = pageWidth * (DEFAULT_CENTER + DEFAULT_CENTER * -position)
            } else {
                val scaleFactor = (1 - position) * (1 - mMinScale) + mMinScale
                view.scaleX = scaleFactor
                view.scaleY = scaleFactor
                view.pivotX = pageWidth * ((1 - position) * DEFAULT_CENTER)
            }
        } else {
            view.pivotX = 0f
            view.scaleX = mMinScale
            view.scaleY = mMinScale
        }
    }

    companion object {

        const val DEFAULT_MIN_SCALE = 0.85f
        const val DEFAULT_CENTER = 0.5f
    }
}

4.ViewPager2的一屏多页效果

ViewPager2的一屏多页实现跟ViewPager并无多大差别。需要把ViewPager2父容器的clipChildren设置为false,同时ViewPager2的clipChildren也设置为false并且将ViewPager2的offscreenPageLimit 设置为2以及为ViewPager2设置marge即可。代码如下:

val constraintLayout = findViewById<ConstraintLayout>(R.id.cl_main)
        constraintLayout.clipChildren = false
        viewPager2.clipChildren = false
        viewPager2.offscreenPageLimit = 2
        val params = viewPager2.layoutParams as ViewGroup.MarginLayoutParams
        params.leftMargin = resources.getDimension(R.dimen.dp_10).toInt() * 2
        params.rightMargin = params.leftMargin
        val compositePageTransformer = CompositePageTransformer()
        compositePageTransformer.addTransformer(ScaleInTransformer())
        compositePageTransformer.addTransformer(MarginPageTransformer(resources.getDimension(R.dimen.dp_10).toInt()))
        viewPager2.setPageTransformer(compositePageTransformer)

四、ViewPager2与Fragment

我们前面也已经提到了ViewPager2中新增的FragmentStateAdapter 替代了ViewPager的FragmentStatePagerAdapter。那么来我们就用ViewPager2来实现一个Activity中嵌套Fragment的实例。

1.Activity的layout中添加ViewPager2

<androidx.viewpager2.widget.ViewPager2
        android:id="@+id/vp_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/rg_tab" />

2.实现FragmentStateAdapter

class AdapterFragmentPager(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {

    private val fragments: SparseArray<BaseFragment> = SparseArray()

    init {
        fragments.put(PAGE_HOME, HomeFragment.getInstance())
        fragments.put(PAGE_FIND, PageFragment.getInstance())
        fragments.put(PAGE_INDICATOR, IndicatorFragment.getInstance())
        fragments.put(PAGE_OTHERS, OthersFragment.getInstance())
    }

    override fun createFragment(position: Int): Fragment {
        return fragments[position]
    }

    override fun getItemCount(): Int {
        return fragments.size()
    }

    companion object {

        const val PAGE_HOME = 0

        const val PAGE_FIND = 1

        const val PAGE_INDICATOR = 2

        const val PAGE_OTHERS = 3

    }
}

3.在Activity中为ViewPager2设置FragmentStateAdapter

vp_fragment.adapter = AdapterFragmentPager(this)
    vp_fragment.offscreenPageLimit = 3
    vp_fragment.isUserInputEnabled=false
  • 8
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: FragmentAndroid中的一个组件,可以在Activity中使用。它可以单独存在,也可以和其他Fragment组合使用ViewPager是一种可以滑动的容器,可以用来显示多个Fragment使用时,可以将多个Fragment添加到ViewPager中,然后通过滑动来切换Fragment。 ### 回答2: FragmentViewPager是Android开发中常用的两个组件,它们可以帮助我们构建具有多个页面的应用程序。Fragment相当于一个子页面,可以嵌入到Activity中,ViewPager则是一个容器可以显示多个Fragment页面,并且可以滑动切换不同页面。以下是这两个组件的使用方法: Fragment使用: 1. 建立Fragment类。Fragment是一个具有生命周期的组件,我们需要继承android.support.v4.app.Fragment或者android.app.Fragment,实现onCreateView方法,添加需要展示的布局。 2. 在Activity中嵌入Fragment。我们可以使用FragmentManager来管理FragmentFragmentManager.beginTransaction() 方法创建一个事务,并且可以添加、删除和替换Fragment。我们可以通过以下代码将该Fragment添加到Activity的布局中: FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); MyFragment fragment = new MyFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit(); 3. 对于一个Fragment的重要生命周期方法:onAttach()和onDetach()通常用于Fragment和宿主Activity之间的通信,onCreate()方法用于初始化,onViewCreated()方法展示展示布局,onActivityCreated()在Activity执行onCreate()方法后触发,并决定 Fragment 是否处于可见状态,onResume()则表示Fragment正在运行中。 ViewPager的使用: 1. 导入ViewPager依赖库:在 build.gradle 文件中添加: dependencies { implementation 'com.android.support:viewpager:28.0.0' } 2. 在布局文件中添加ViewPager: <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" /> 3. 创建FragmentAdapter:继承FragmentPagerAdapter并实现抽象方法,如getItem()和getCount()。它们用于返回一个Fragment页面和页面数量。我们可以像下面这样创建一个FragmentPagerAdapter: class MyFragmentAdapter extends FragmentPagerAdapter { private final List<Fragment> fragments = new ArrayList<>(); private final List<String> fragmentTitles = new ArrayList<>(); public MyFragmentAdapter(FragmentManager fm) { super(fm); } public void addFragment(Fragment fragment, String title) { fragments.add(fragment); fragmentTitles.add(title); } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return fragments.size(); } @Override public CharSequence getPageTitle(int position) { return fragmentTitles.get(position); } } 4. 将FragmentAdapter设置给ViewPager: ViewPager viewPager = findViewById(R.id.viewpager); MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager()); adapter.addFragment(new MyFragment1(), "Fragment 1"); adapter.addFragment(new MyFragment2(), "Fragment 2"); viewPager.setAdapter(adapter); 通过使用FragmentViewPager,我们可以创建一个具有多个页面和子页面的应用程序。 使用FragmentViewPager,我们可以非常便捷的实现滑动切换不同页面,并且每个页面分别是单独的Fragment,方便我们做管理和调用。 ### 回答3: FragmentViewPager是Android开发中非常重要也非常常用的两个组件,它们的配合使用可以实现非常灵活的页面布局和交互效果。 首先,Fragment是一种可以嵌入到Activity中的可重用的UI组件,可以将整个屏幕分解成多个区域,每个区域可以通过不同的Fragment来展示不同的内容。使用Fragment的好处主要在于其较高的可复用性,因为一个Fragment可以在不同的Activity中使用,并且可以动态替换或添加。同时,Fragment还可以处理用户交互事件、生命周期和配置变化等问题。 而ViewPager则是一种可以实现滑动切换多个Fragment的控件,通常会结合FragmentPagerAdapter或者FragmentStatePagerAdapter来使用使用ViewPager可以让App具有更好的用户体验和视差效果。可滑动的ViewPager是一种非常流行的UI设计,尤其适合移动设备。 在使用FragmentPagerAdapter时,每个Fragment被创建后就会保存在内存中,不需要重新创建,因此在使用ViewPager滑动切换Fragment时,非常流畅,缺点是可能出现内存溢出的问题。而使用FragmentStatePagerAdapter则不会将所有的Fragment都保存在内存中,而是只保留一些可见的Fragment,其它的Fragment会被销毁,这样可以避免内存溢出的问题。 总之,FragmentViewPager是Android开发中非常重要的两个组件,它们的灵活使用可以带来非常好的用户体验和交互效果,并且可以有效避免内存溢出等问题。开发人员可以根据实际需求灵活选择使用它们的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值