android / views-widgets-samples

Multiple samples showing the best practices in views-widgets on Android.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

从ViewPager迁移到ViewPager2中遇到的问题

ou2356 opened this issue · comments

commented

我的ViewPager2中管理着3个Fragment,其中两个Fragment都有RecyclerView,这两个RecyclerView都管理着同样的实时更新的item数据,但是我发现在ViewPager2中当RecyclerView有item数据实时更新出来时所有的item都会闪烁或者发生奇怪的偏移。但是这一系列的现象在ViewPager中都没有出现到,我对这个现象进行了一系列的追溯,有了一点眉目,但不知道该如何解决这个问题,下面展示3种我调试的现象,欢迎大家来参观讨论,我挺期待用上ViewPager2的:

现象1(ViewPager2的问题现象):

Clipchamp.mp4
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.AppCompat.DayNight"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/view_pager2">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="20dp" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager2"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout"
        app:layout_constraintBottom_toTopOf="@+id/sendMsgEt"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
        viewPager2 = findViewById(R.id.view_pager2);
        tabLayout = findViewById(R.id.tab_layout);
        //禁止ViewPager的左右滑动
        viewPager2.setUserInputEnabled(false);
        viewPager2.setOffscreenPageLimit(3);
        viewPager2.setAdapter(new FragmentStateAdapter(supportFragmentManager, getLifecycle()) {
            @NonNull
            @Override
            public Fragment createFragment(int position) {
                switch (position){
                    case 0 :
                        chatFragment = new mChatFragment();
                        return chatFragment;
                    case 1 :
                        terminalFragment = new mTerminalFragment();
                        return terminalFragment;
                    case 2 :
                        controlFragment = new mControlFragment();
                        return controlFragment;
                    default:
                        return null;
                }
            }
            @Override
            public int getItemCount() {
                return 3;
            }
        });
        tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager2, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                //ViewPager选中时对Tab执行的代码
                switch (position){
                    case 0 :
                        tab.setText("对话模式");
                        break;
                    case 1 :
                        tab.setText("终端模式");
                        break;
                    case 2 :
                        tab.setText("控件模式");
                        break;
                    default: break;
                }
            }
        });
        tabLayoutMediator.attach();
        messageRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                Log.e("RecyclerViewLayout","newTop:"+top+"\tnewBottom:"+bottom);
            }
        });

OnLayoutChangeListener中捕获的日志如下(RecyclerView每新增一个item其中的newBottom都会发生两次改变先是0后面变成1953,我认为导致这个问题的原因就在于newBottom改变成了0):
图片
于是我对令newBottom变成0的代码经行了追溯,发现下面两处有可能是导致newBottom变成0的代码(我能力有限,只能debug后建立断点后一点点往回看,希望理解),令我好奇的是下面两处代码在newBottom变成1953时并没有被执行,这也让我更加肯定下面两处代码把newBottom变成了0:
图片
图片

现象2(把xml布局文件中ViewPager2的layout_height="0dp"修改成固定的"350dp",其它部分代码同上)

Clipchamp.mp4

修改后的xml代码如下

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager2"
        android:layout_width="match_parent"
        android:layout_height="350dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout"
        app:layout_constraintBottom_toTopOf="@+id/sendMsgEt"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

捕获日志如下(可见RecyclerView每新增一个item其中的newBottom只更新了一次,且都为963,可以说没有改变,一切正常,但这样的固定值的布局显然不是我想要的):
图片

现象3(把ViewPager2改回用ViewPager)

Clipchamp.mp4
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.AppCompat.DayNight"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/view_pager">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="20dp" />

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout"
        app:layout_constraintBottom_toTopOf="@+id/sendMsgEt"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
        ViewPager viewPager = findViewById(R.id.view_pager);
        viewPager.setOffscreenPageLimit(3);
        viewPager.setAdapter(new FragmentPagerAdapter(supportFragmentManager) {
            @NonNull
            @Override
            public Fragment getItem(int position) {
                switch (position){
                    case 0 :
                        chatFragment = new mChatFragment();
                        return chatFragment;
                    case 1 :
                        terminalFragment = new mTerminalFragment();
                        return terminalFragment;
                    case 2 :
                        controlFragment = new mControlFragment();
                        return controlFragment;
                    default:
                        return null;
                }
            }
            @Override
            public int getCount() {
                return 3;
            }
            @Override
            public CharSequence getPageTitle(int position){
                switch (position){
                    case 0 :
                        return "对话模式";
                    case 1 :
                        return "终端模式";
                    case 2 :
                        return "控件模式";
                    default:
                        return null;
                }
            }
        });
        TabLayout tabLayout = findViewById(R.id.tab_layout);
        tabLayout.setupWithViewPager(viewPager);

捕获日志如下(一切正常):
图片

最后贴上RecyclerView的item更新代码

    private void refreshMsgUI(){
        ((AppCompatActivity)context).runOnUiThread(()->{
            terminalFragment.recyclerViewAdapter.notifyItemInserted(terminalFragment.recyclerViewAdapter.getItemCount()-1);
            if (isTerScrollToBottom) {
                terminalFragment.messageRecyclerView.scrollToPosition(terminalFragment.recyclerViewAdapter.getItemCount()-1);
            }
            chatFragment.recyclerViewAdapter.notifyItemInserted(chatFragment.recyclerViewAdapter.getItemCount()-1);
            if (isChatScrollToBottom) {
                chatFragment.messageRecyclerView.scrollToPosition(chatFragment.recyclerViewAdapter.getItemCount()-1);
            }
        });
    }