滑动ViewPager时TabLayout标签未同步切换焦点问题
问题描述
- 布局中
TabLayout下方放置ViewPager组件,点击Tab标签可正常切换对应页面,但滑动ViewPager完成页面切换后,TabLayout对应标签不会同步切换焦点,必须手动点击对应标签才能完成焦点切换(点击时ViewPager不会触发页面变更) - 复现示例:当前处于第4个页面,左滑后ViewPager已切换到第3个页面,但TabLayout选中焦点未同步移动到第3个标签,手动点击第3个标签才可完成焦点切换
- 预期效果:滑动ViewPager切换页面时,TabLayout标签同步跟随切换焦点
原有实现代码
fragment XML
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.google.android.material.tabs.TabLayout android:layout_height="wrap_content" android:id="@+id/tab_layout" android:layout_width="match_parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toTopOf="@id/pager"/> <androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" /> </androidx.constraintlayout.widget.ConstraintLayout>
PagerFragment
public class PagerFragment extends Fragment { ViewPagerAdapter pagerAdapter; ViewPager pager; TabLayout tl; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); pagerAdapter = new ViewPagerAdapter(this.getContext(), getChildFragmentManager(), 4, getActivity()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = super.onCreateView(inflater, container, savedInstanceState); ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_pager, null); pager = root.findViewById(R.id.pager); pager.setAdapter(pagerAdapter); tl = root.findViewById(R.id.tab_layout); tl.addTab(tl.newTab().setText("1")); tl.addTab(tl.newTab().setText("2")); tl.addTab(tl.newTab().setText("3")); tl.addTab(tl.newTab().setText("4")); tl.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { pager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } }); tl.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(pager)); return root; } }
Adapter
public class ViewPagerAdapter extends FragmentPagerAdapter { private Context myContext; int totalTabs; public ViewPagerAdapter(Context context, FragmentManager fm, int totalTabs, FragmentActivity fa) { super(fm); myContext = context; this.totalTabs = totalTabs; } public Fragment getItem(int position) { graph_test gt = new graph_test(String.valueOf(position), position); return gt; } @Override public int getCount() { return totalTabs; } }
注:已知
FragmentPagerAdapter已被废弃,本次实现按要求采用该方式编写。
问题原因
现有代码只实现了Tab选中时触发ViewPager切换页面的单向逻辑,没有添加ViewPager页面滑动后反向同步TabLayout选中状态的关联。代码中添加的TabLayout.ViewPagerOnTabSelectedListener仅负责处理Tab点击联动ViewPager的逻辑,不包含反向同步能力,因此滑动ViewPager时TabLayout无法收到页面变更通知,不会自动切换选中焦点。
修复方案
直接调用TabLayout官方封装的setupWithViewPager()方法即可完成双向绑定,无需手写多套监听,该方法会自动处理:
- Tab点击时切换ViewPager页面
- ViewPager滑动时同步Tab选中状态
- 滑动过程中Tab的指示器偏移联动效果
具体修改点如下:
- 删除手动添加Tab的代码、自定义的
OnTabSelectedListener、手动添加的ViewPagerOnTabSelectedListener,这些逻辑setupWithViewPager()会自动完成 - 给ViewPagerAdapter重写
getPageTitle()方法,返回对应位置Tab需要显示的文本,setupWithViewPager()会自动读取该方法返回值创建Tab
修改后代码
PagerFragment 核心修改部分
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_pager, null); pager = root.findViewById(R.id.pager); pager.setAdapter(pagerAdapter); tl = root.findViewById(R.id.tab_layout); // 一行完成双向绑定 tl.setupWithViewPager(pager); return root; }
ViewPagerAdapter 补充方法
public class ViewPagerAdapter extends FragmentPagerAdapter { private Context myContext; int totalTabs; public ViewPagerAdapter(Context context, FragmentManager fm, int totalTabs, FragmentActivity fa) { super(fm); myContext = context; this.totalTabs = totalTabs; } public Fragment getItem(int position) { graph_test gt = new graph_test(String.valueOf(position), position); return gt; } @Override public int getCount() { return totalTabs; } // 新增该方法,返回对应位置Tab的标题 @Nullable @Override public CharSequence getPageTitle(int position) { return String.valueOf(position + 1); // 对应原逻辑的1、2、3、4标签 } }
特殊场景说明:如果因业务需求不能使用
setupWithViewPager(),可以手动给ViewPager添加addOnPageChangeListener,在onPageSelected(int position)回调中调用tl.selectTab(tl.getTabAt(position))实现同步,但官方封装方法已经处理了滑动冲突、状态同步等所有边界情况,优先使用官方方案。
内容的提问来源于stack exchange,提问作者shiho
相关产品推荐
相关产品推荐

