Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ViewPager2 #124

Open
soapgu opened this issue Mar 21, 2022 · 0 comments
Open

ViewPager2 #124

soapgu opened this issue Mar 21, 2022 · 0 comments
Labels
mvvm This issue or pull request already exists 安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented Mar 21, 2022

  • 安卓的翻页浏览控件

接近相册翻页那样的体验的控件,手机里是非常常用的。但是一直没有用。
正好借这次项目实践的机会体验一把

  • ViewPager2

按历史来说翻页控件应该是ViewPager。现在有了更时新的组件ViewPager2了。我们从最新版本开始学起,啥历史包袱都可以统统扔掉

  • Quick Start

    implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
    在build.gradle中增加如下依赖

  • ViewPager2和RecyclerView之间不清不楚的关系
    在使用过程中,发现ViewPager2和RecyclerView竟然有90%的相似度,直觉上感觉这不是一个巧合
    图片
    通过源码可以发现他们之间采用了适配器模式,RecyclerView就藏在ViewPager2里面

  • Apapter
    ViewPager2的Apapter和RecyclerView是共用的都是RecyclerView.Adapter扩展的
    其中针对ViewPager2有一个FragmentStateAdapter,顾名思义,内容都是以Fragment为子项。针对我的应用来说这个适配器“太重”了,所以我使用RecyclerView一样级别的数据项级别就好

  • BindingAdapter
    为了配合MVVM模式,需要相关的BindingAdapter
    由于ViewPager2和RecyclerView没有直接继承关系,所以还要仿造RecyclerView的BindingAdapter再写一遍(几乎是复制粘贴)

@BindingAdapter(value = {"itemsSource", "itemTemplate", "variableId"} )
    public static <T> void setPager2Items(ViewPager2 viewPager2, List<T> itemsSource, @LayoutRes int itemTemplate, int variableId, CircleIndicator3 indicator) {
        ShadowAdapter<T> adapter;

        @SuppressWarnings("unchecked")
        ShadowAdapter<T> oldAdapter = (ShadowAdapter<T>) viewPager2.getAdapter();
        if (oldAdapter == null) {
            ItemTemplate template = ItemTemplate.of(itemTemplate, variableId);
            adapter = new ShadowAdapter<>(template);
        } else {
            adapter = oldAdapter;
        }
        adapter.setItems(itemsSource);
        if (oldAdapter != adapter) {
            viewPager2.setAdapter(adapter);
        }
    }
<androidx.viewpager2.widget.ViewPager2
                                android:layout_marginLeft="16dp"
                                android:layout_marginRight="16dp"
                                android:layout_marginTop="12dp"
                                app:itemsSource="@{dataContext.nearbySpaces}"
                                app:itemTemplate="@{@layout/item_space}"
                                app:variableId="@{dataContext.variableId}"
                                android:layout_width="match_parent"
                                android:layout_height="112dp" />

其中子项的view我直接用RecyclerView共用的了

运行报错啊!

space365.app E/space365: │ ----------UncaughtException throw--------- : java.lang.IllegalStateException: Pages must fill the whole ViewPager2 (use match_parent)
2022-03-21 15:41:18.640 3863-3863/com.space365.app E/space365: │ 	at androidx.viewpager2.widget.ViewPager2$4.onChildViewAttachedToWindow(ViewPager2.java:277)
2022-03-21 15:41:18.640 3863-3863/com.space365.app E/space365: │ 	at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:7872)
2022-03-21 15:41:18.640 3863-3863/com.space365.app E/space365: │ 	at androidx.recyclerview.widget.RecyclerView$5.addView(RecyclerView.java:893)
2022-03-21 15:41:18.640 3863-3863/com.space365.app E/space365: │ 	at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:107)
2022-03-21 15:41:18.640 3863-3863/com.space365.app E/space365: │ 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:8902)
2022-03-21 15:41:18.640 3863-3863/com.space365.app E/space365: │ 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8860)

看来最外层的View的height必须是“match_parent”,这个和RecyclerView的“固定”高度略有不同
多想一下也能理解,只能再加一个item_space_page的layout

总算正常运行了,布局不用声明,默认就是横向的。
但是好像少了一些什么。
对没有“进度条”显示

  • 进度UI实现

这次我使用第三方控件方案

  • CircleIndicator
    增加依赖implementation 'me.relex:circleindicator:2.1.6'

  • BindingAdapter调整

@BindingAdapter(value = {"itemsSource", "itemTemplate", "variableId","indicator"} ,requireAll = false)
    public static <T> void setPager2Items(ViewPager2 viewPager2, List<T> itemsSource, @LayoutRes int itemTemplate, int variableId, CircleIndicator3 indicator) {
        ShadowAdapter<T> adapter;

        @SuppressWarnings("unchecked")
        ShadowAdapter<T> oldAdapter = (ShadowAdapter<T>) viewPager2.getAdapter();
        if (oldAdapter == null) {
            ItemTemplate template = ItemTemplate.of(itemTemplate, variableId);
            adapter = new ShadowAdapter<>(template);
        } else {
            adapter = oldAdapter;
        }
        adapter.setItems(itemsSource);
        if (oldAdapter != adapter) {
            viewPager2.setAdapter(adapter);
        }

        if( indicator != null ){
            indicator.setViewPager( viewPager2 );
            //adapter.registerAdapterDataObserver(indicator.getAdapterDataObserver());
        }

    }

把CircleIndicator3变量引入

<androidx.viewpager2.widget.ViewPager2
                                android:layout_marginLeft="16dp"
                                android:layout_marginRight="16dp"
                                android:layout_marginTop="12dp"
                                app:itemsSource="@{dataContext.nearbySpaces}"
                                app:itemTemplate="@{@layout/item_space_page}"
                                app:variableId="@{dataContext.variableId}"
                                app:indicator="@{spaceIndicator}"
                                android:layout_width="match_parent"
                                android:layout_height="112dp" />
                            <me.relex.circleindicator.CircleIndicator3
                                android:id="@+id/space_indicator"
                                app:ci_drawable="@color/gray_400"
                                app:ci_drawable_unselected="@color/gray_600"
                                android:layout_width="match_parent"
                                android:layout_height="20dp"/>

layout的声明,把CircleIndicator3作为绑定导入
图片
OK

其他方案

也是一个好方案,但是好吧我承认我想偷懒

另外感谢 @marclee44 冠冠 相关的技术支持,其实这部分输入联合创作

@soapgu soapgu added mvvm This issue or pull request already exists 安卓 安卓 labels Mar 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mvvm This issue or pull request already exists 安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant