IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> ViewPager2的使用:基本使用、源码分析、多种切换动画 -> 正文阅读

[移动开发]ViewPager2的使用:基本使用、源码分析、多种切换动画

注:该文章也同步发布到稀土掘金:链接

前言

ViewPager2是ViewPager的改进版本,提供了一些增强功能:

  • 垂直方向的支持
  • 可动态修改Fragment集合
  • 从右到左支持
    当然,这是官方新推的库,后续会获得更好的支持~这篇文章主要从基本使用、增强功能使用、viewpager到viewpager2的升级、通过ViewPager2.PageTransformer实现多种切换动画等几个方面进行介绍。

使用

基本使用

  1. 引入viewpager2库
// module的build.gradle文件
implementation("androidx.viewpager2:viewpager2:1.0.0")
  1. 在布局中添加viewpager2
<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  1. 继承FragmentStateAdapter创建adapter
class DemoAapdater(activity: FragmentActivity) : FragmentStateAdapter(activity) {

    override fun getItemCount(): Int {
        return 6
    }

    override fun createFragment(position: Int): Fragment {
        val demoFragment = DemoFragment()
        demoFragment.arguments = Bundle().apply {
            putInt("TEXT", position)
        }
        return demoFragment
    }
}
  1. 为viewpager2设置adapter
demoAapdater = DemoAapdater(this)
viewPager2.adapter = demoAapdater

通过简单四步,已经可以正常使用viewpager2啦~

配合TabLayout使用

很多业务场景,需要搭配TabLayout使用的。viewpager2对于tablayout的配合也进行了调整:

tabLayout = findViewById(R.id.tabLayout)
TabLayoutMediator(tabLayout, viewPager2) { tab, position ->
    tab.text = "PAGE $position"
}.attach()

增强功能使用

  1. 切换横竖屏滚动
cbDirection.setOnCheckedChangeListener { _, isChecked ->
    if (isChecked) {
        viewPager2.orientation = ViewPager2.ORIENTATION_HORIZONTAL
    } else {
        viewPager2.orientation = ViewPager2.ORIENTATION_VERTICAL
    }
}
  1. 是否允许用户滑动切换
cbScroll.setOnCheckedChangeListener { buttonView, isChecked ->
    viewPager2.isUserInputEnabled = isChecked
}
  1. 通过其他view控制viewpager2的滑动
public boolean fakeDragBy(@SuppressLint("SupportAnnotationUsage") @Px float offsetPxFloat) {
    return mFakeDragger.fakeDragBy(offsetPxFloat);
}

直接通过TabLayoutMediator的TabConfigurationStrategy参数即可为tabitem设置title,跟viewpager的用法完全不同。

源码分析

viewpager2


public final class ViewPager2 extends ViewGroup

private void initialize(Context context, AttributeSet attrs) {
    mAccessibilityProvider = sFeatureEnhancedA11yEnabled
            ? new PageAwareAccessibilityProvider()
            : new BasicAccessibilityProvider();

    mRecyclerView = new RecyclerViewImpl(context);
    mRecyclerView.setId(ViewCompat.generateViewId());
    mRecyclerView.setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);

    mLayoutManager = new LinearLayoutManagerImpl(context);
    mRecyclerView.setLayoutManager(mLayoutManager);
    mRecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
    setOrientation(context, attrs);

    mRecyclerView.setLayoutParams(
            new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    mRecyclerView.addOnChildAttachStateChangeListener(enforceChildFillListener());

    // Create ScrollEventAdapter before attaching PagerSnapHelper to RecyclerView, because the
    // attach process calls PagerSnapHelperImpl.findSnapView, which uses the mScrollEventAdapter
    mScrollEventAdapter = new ScrollEventAdapter(this);
    // Create FakeDrag before attaching PagerSnapHelper, same reason as above
    mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);
    mPagerSnapHelper = new PagerSnapHelperImpl();
    mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
    // Add mScrollEventAdapter after attaching mPagerSnapHelper to mRecyclerView, because we
    // don't want to respond on the events sent out during the attach process
    mRecyclerView.addOnScrollListener(mScrollEventAdapter);
    ....
    ....
    attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
}

首先ViewPager2继承于Viewgroup,看到最后一句:attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());可以发现将RecyclerView添加到ViewPager2的第一个childView。那是不是viewpager2的功能其实就是通过RecyclerView实现的呢?

public void setAdapter(@Nullable @SuppressWarnings("rawtypes") Adapter adapter) {
    final Adapter<?> currentAdapter = mRecyclerView.getAdapter();
    mAccessibilityProvider.onDetachAdapter(currentAdapter);
    unregisterCurrentItemDataSetTracker(currentAdapter);
    mRecyclerView.setAdapter(adapter);
    mCurrentItem = 0;
    restorePendingState();
    mAccessibilityProvider.onAttachAdapter(adapter);
    registerCurrentItemDataSetTracker(adapter);
}

public void setOrientation(@Orientation int orientation) {
    mLayoutManager.setOrientation(orientation);
    mAccessibilityProvider.onSetOrientation();
}

上边抽取了两个函数,setAdapter最终是设置给了mRecyclerView,setOrientation最终也是给了mLayoutManager。这里再次印证了开始的想法~那通过上边的分析,FragmentStateAdapter应该也是继承于RecyclerView.Adapter?

FragmentStateAdapter

public abstract class FragmentStateAdapter extends
        RecyclerView.Adapter<FragmentViewHolder> implements StatefulAdapter

查看源码可以发现,FragmentStateAdapter确实是继承于RecyclerView.Adapter。重新实现了RecyclerView.ViewHolder

public final class FragmentViewHolder extends ViewHolder {
    private FragmentViewHolder(@NonNull FrameLayout container) {
        super(container);
    }

    @NonNull static FragmentViewHolder create(@NonNull ViewGroup parent) {
        FrameLayout container = new FrameLayout(parent.getContext());
        container.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT));
        container.setId(ViewCompat.generateViewId());
        container.setSaveEnabled(false);
        return new FragmentViewHolder(container);
    }

    @NonNull FrameLayout getContainer() {
        return (FrameLayout) itemView;
    }
}

另外,FragmentStateAdapter实现了StatefulAdapter接口:

public interface StatefulAdapter {
    /** Saves adapter state */
    @NonNull Parcelable saveState();

    /** Restores adapter state */
    void restoreState(@NonNull Parcelable savedState);
}

该接口主要定义了两个函数,一个用于保存状态,一个用于恢复状态。主要在ViewPager2中被调用:

@SuppressWarnings("ConstantConditions")
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
    ...

    if (mPendingAdapterState != null) {
        ss.mAdapterState = mPendingAdapterState;
    } else {
        Adapter<?> adapter = mRecyclerView.getAdapter();
        if (adapter instanceof StatefulAdapter) {
            ss.mAdapterState = ((StatefulAdapter) adapter).saveState();
        }
    }

    return ss;
}


private void restorePendingState() {
    ...
    if (mPendingAdapterState != null) {
        if (adapter instanceof StatefulAdapter) {
            ((StatefulAdapter) adapter).restoreState(mPendingAdapterState);
        }
        mPendingAdapterState = null;
    }
    ...
}

ViewPager2会在资源不足回收前保存fragment的状态,以便后续的状态恢复

ViewPager到ViewPager2的迁移

关于这块,官方有详细的说明:官方说明

  • Adapter的变更
  • TabLayout关联的调整:不再通过setupWithViewPager(), 而是使用TabLayoutMediator实例

多种切换动画

Sets a ViewPager2.PageTransformer that will be called for each attached page whenever the scroll position is changed. This allows the application to apply custom property transformations to each page, overriding the default sliding behavior.
Note: setting a ViewPager2.PageTransformer disables data-set change animations to prevent conflicts between the two animation systems. Setting a null transformer will restore data-set change animations.
Params:
transformer – PageTransformer that will modify each page's animation properties
See Also:
MarginPageTransformer, CompositePageTransformer

public void setPageTransformer(@Nullable PageTransformer transformer) 

我们可以通过自定义PageTransformer来实现不同的页面切换效果。官方也给出了两个具体的例子:官方例子
下边给出已实现的一些效果图(参考华为桌面的切换效果),具体实现请看demo

1.gif

2.gif

3.gif

4.gif

5.gif

6.gif

总结

Viewpager2的使用基本介绍到这里,其实官方也提供了一个完整的demo:官方demo,里边还介绍了viewpager2嵌套滚动视图的滑动冲突解决方案,有这方面需求的可以看下具体解决思路。

最后,按照惯例,附上该文章对应的demo:gitee-demo

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-08 13:55:57  更:2021-12-08 13:57:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:23:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码