看完这一篇,ViewPager2基本可以投产了

本文详细介绍了ViewPager2的使用,包括如何展示普通视图和配合Fragment使用,探讨了生命周期感知、NestedScroll机制的配合,以及懒加载、预加载和状态恢复的处理。此外,还分析了ViewPager2的实现原理,如LayoutManager、FragmentStateAdapter等,是学习和应用ViewPager2的全面参考资料。
摘要由CSDN通过智能技术生成

前言

ViewPager2已经出来很长一段时间了,但之前一直都是alpha版本,几次版本迭代中,内容细节变化也挺多,前阵子第一个正式版发布,不巧新冠肺炎爆发,在家里索性把之前的预研Demo整理整理,梳理下内容点写一篇博客,也算把预研工作正式收个尾。

首先先感谢一个我确实记不得的大兄嘚,预研的demo的前身,来自于GitHub,是我很久之前看alpha版本的使用时下载的,只下了一个zip,着实找不到源头了。年前开始预研的时候,我正好发现电脑里面有一个项目,就懒得新建,直接改了一通。

先贴上本文的 demo地址https://github.com/leobert-lan/ViewPager2-Demo/tree/master

本文内容梗概

本文的篇幅会比较长,先给一个大致的内容梗概:

  • 用于展示普通视图,横向和纵向滑动
  • 结合Fragment使用以及Fragment的生命周期
  • 复杂布局下,常见的结构,配合CoordinatorLayout和NestedScroll使用
  • 懒、预加载和状态恢复,主要观测生命周期
  • 实现原理和源码分析

最关键是我写写停停,有时候一篇文章拖个把月,不先放个梗概我自己都会忘了要写啥。

使用介绍

展示普通视图

按照以往用ViewPager的经验,我们会使用到三个东西:

  • ViewPager实例
  • 一个适配器实例
  • 子视图

而使用ViewPager2也是类似的,我们需要一个ViewPager2实例,一个相应的适配器Adapter(RecyclerView.Adapter的子类),子视图和相应的RecyclerView.ViewHolder子类实例。

我们可能对ViewPager2有点或多或少的了解,他是通过RecyclerView做的功能实现,这里先不展开。

package com.example.viewpager2demo;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

/**
 * leobert
 */
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder> {
    private List<Integer> colors = new ArrayList<>();
    {
        colors.add(android.R.color.black);
        colors.add(android.R.color.holo_purple);
        colors.add(android.R.color.holo_blue_dark);
        colors.add(android.R.color.holo_green_light);
    }
    @NonNull
    @Override
    public ViewPagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewPagerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_page, parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewPagerViewHolder holder, int position) {
        holder.mTvTitle.setText("item " + position);
        holder.mContainer.setBackgroundResource(colors.get(position));
    }

    @Override
    public int getItemCount() {
        return colors.size();
    }

    class ViewPagerViewHolder extends RecyclerView.ViewHolder {
        TextView mTvTitle;
        RelativeLayout mContainer;
        public ViewPagerViewHolder(@NonNull View itemView) {
            super(itemView);
            mContainer = itemView.findViewById(R.id.container);
            mTvTitle = itemView.findViewById(R.id.tvTitle);
        }
    }
}

布局都是比较简单的内容,这里不贴了。

使用的时候:

setContentView(R.layout.activity_horizontal_scrolling);
ViewPager2 viewPager2 = findViewById(R.id.viewpager2);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();
viewPager2.setAdapter(viewPagerAdapter);

使用也是很简单,具体可以看demo中的HorizontalScrolling

那么纵向滚动呢?

setContentView(R.layout.activity_horizontal_scrolling);
ViewPager2 viewPager2 = findViewById(R.id.viewpager2);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();
viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
viewPager2.setAdapter(viewPagerAdapter);

只需要设置下方向即可,ViewPager的默认方式是横向的,这点我们可以在源码中找到:

androidx.viewpager2.widget.ViewPager2#initialize方法中,实例化了LayoutManager,并调用了androidx.viewpager2.widget.ViewPager2#setOrientation(android.content.Context, android.util.AttributeSet)

  private void setOrientation(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewPager2);
        if (Build.VERSION.SDK_INT >= 29) {
            saveAttributeDataForStyleable(context, R.styleable.ViewPager2, attrs, a, 0, 0);
        }
        try {
            setOrientation(
                    a.getInt(R.styleable.ViewPager2_android_orientation, ORIENTATION_HORIZONTAL));
        } finally {
            a.recycle();
        }
    }

可以看到默认方向就是横向的。


这里我们简单小结一下:

ViewPager2可以像RecyclerView一样使用,并且可以配滑动方向了。我们只需要一个ViewPager2、一套ViewHolder,一个Adapter,就可以开始使用了。

结合Fragment的使用

结合我们日常工作实际,现在单独使用VP (ViewPager,如无特殊必要,下文都会使用VP来指代滑动控件,语义上如无必要区分ViewPager和ViewPager2,均以VP指代,否则以VP2指代ViewPager2)去呈现View的场景是比较少的,往往是用来显示图片,更多的业务往往会结合生命周期感知、以及需要实现业务“组件化”(这里的组件化指的是其环境相对独立,便于场景移植快速使用),而实际案例中都是使用Fragment去作为业务的承载。

我们知道,VP结合Fragment使用是一个很常见的套路,那么VP2中是否可以呢?当然是可以的,否则不支持向后兼容性迭代,这个VP2就是个笑话了。

言归正传、结合我们使用VP+Fragment的经验,我们会需要VP、FragmentPagerAdapter或者FragmentStatePagerAdapter和一系列的Fragment,限于内容主题和篇幅就放Demo代码了。ok,按照老经验,我们推断这里也会使用到VP2,一个特定的Adapter,按照兼容原则,Fragment应该没有特殊限制。

经过一番阅读,我们知道了Google的工程师们给我们提供的是:

androidx.viewpager2.adapter.FragmentStateAdapter

我们“特定的Adapter”就是这玩意了,先上代码,回头再看细节:

class ViewPagerFragmentStateAdapter extends FragmentStateAdapter {
        public ViewPagerFragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
            super(fragmentActivity);
        }

        public ViewPagerFragmentStateAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
            super(fragmentManager, lifecycle);
        }

        @NonNull
        @Override
        public Fragment createFragment(int position) {
            if (position == 0)
                return RvFragment.Companion.newInstance();
            else
                return PageFragment.newInstance(colors, position - 1);
        }

        @Override
        public int getItemCount() {
            return colors.size() + 1;
        }

    }

这里我摘了一段Demo中的内容,抛去里面的方法具体实现,使用的时候,构造器真正有用的是第二种,给FragmentActivity或者Fragment(这里没有体现),都是取出对应的FragmentManager和Lifecycle的,我们可以扫一眼源码:

 /**
     * @param fragmentActivity if the {@link ViewPager2} lives directly in a
     * {@link FragmentActivity} subclass.
     *
     * @see FragmentStateAdapter#FragmentStateAdapter(Fragment)
     * @see FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
     */
    public FragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
        this(fragmentActivity.getSupportFragmentManager(), fragmentActivity.getLifecycle());
    }

    /**
     * @param fragment if the {@link ViewPager2} lives directly in a {@link Fragment} subclass.
     *
     * @see FragmentStateAdapter#FragmentStateAdapter(FragmentActivity)
     * @see FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
     */
    public FragmentStateAdapter(@NonNull Fragment fragment) {
        this(fragment.getChildFragmentManager(), fragment.getLifecycle());
    }

    /**
     * @param fragmentManager of {@link ViewPager2}'s host
     * @param lifecycle of {@link ViewPager2}'s host
     *
     * @see FragmentStateAdapter#FragmentStateAdapter(FragmentActivity)
     * @see FragmentStateAdapter#FragmentStateAdapter(Fragment)
     */
    public FragmentStateAdapter(@NonNull FragmentManager fragmentManager,
            @NonNull Lifecycle lifecycle) {
        mFragmentManager = fragmentManager;
        mLifecycle = lifecycle;
        super.setHasStableIds(true);
    }

必须要实现的两个抽象方法是:

androidx.viewpager2.adapter.FragmentStateAdapter#createFragment

/**                                                                                            
 * Provide a new Fragment associated with the specified position.                              
 * <p>                                                                                         
 * The adapter will be responsible for the Fragment lifecycle:                                 
 * <ul>                                                                                        
 *     <li>The Fragment will be used to display an item.</li>  
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值