赞
踩
TabLayout
是 Android 支持库中的一个组件,它是 Design
支持库的一部分。TabLayout
提供了一个水平的标签页界面,允许用户在不同的视图或数据集之间进行切换。以下是 TabLayout
的一些主要特性和使用方法
TabLayout
可以显示多个标签页,每个标签页可以包含文本、图标或两者兼有。ViewPager
结合使用:TabLayout
可以与 ViewPager
无缝集成,使得用户在滑动 ViewPager
的页面时,TabLayout
的选中标签页也会相应地变化。官方文档:
ViewPager2
是 Android Jetpack 库中的一个组件,它是 ViewPager
的一个改进版本,提供了更好的性能和更多的功能。ViewPager2
允许用户左右滑动来浏览不同的视图,类似于一个滑动页面的控件。
ViewPager2
优化了滑动性能,特别是在处理大量页面或复杂视图时。ViewPager
仅支持水平滑动。TabLayout
集成:可以与 TabLayout
集成,实现标签页和页面视图的联动。官方文档:
本篇博客,笔者将带大家实现这样一个标题栏+Fragment水平切换页面的案例。
创建Activity的步骤不再赘述,这里放上写好的xml文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical" style="@style/LayoutStyle"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/TabStyle"/> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" /> </LinearLayout>
顺便展示一下Tab的样式配置:
<style name="TabStyle">
<item name="tabGravity">fill</item>
<item name="tabIndicatorFullWidth">false</item>
<item name="tabIndicatorAnimationMode">elastic</item>
<item name="tabMode">fixed</item>
<item name="tabUnboundedRipple">false</item>
<item name="tabBackground">@color/white</item>
<item name="tabTextAppearance">@style/TabTextStyle</item>
<item name="tabIndicator">@drawable/shape_tab_indicator</item>
<item name="tabIndicatorColor">@color/text_change_color</item>
<item name="tabTextColor">@color/black</item>
<item name="tabSelectedTextColor">@color/text_change_color</item>
</style>
tabGravity
: 这个属性设置Tab的对齐方式,fill
表示Tab将填充整个TabLayout的宽度。tabIndicatorFullWidth
: 设置指示器是否占据整个Tab的宽度,false
表示指示器不会占据整个Tab的宽度。tabIndicatorAnimationMode
: 指示器动画模式,elastic
表示弹性动画效果。tabMode
: 设置Tab的模式,fixed
表示Tab的数量是固定的。tabUnboundedRipple
: 是否允许未绑定的涟漪效果,false
表示不允许。tabBackground
: 设置Tab的背景颜色,这里使用了颜色资源@color/white
。tabTextAppearance
: 设置Tab文本的样式,这里引用了一个样式资源@style/TabTextStyle
。tabIndicator
: 设置Tab指示器的形状,这里引用了一个可绘制资源@drawable/shape_tab_indicator
。tabIndicatorColor
: 设置Tab指示器的颜色,使用了颜色资源@color/text_change_color
。tabTextColor
: 设置Tab未选中时的文本颜色,使用了颜色资源@color/black
。tabSelectedTextColor
: 设置Tab选中时的文本颜色,同样使用了颜色资源@color/text_change_color
。
仅作样式展示,大家喜欢的话可以直接cv走,如果想要自定义可以自行去网络上搜索配置属性,或者是查看源码。
下一步,我们创建需要的Fragments,这里仅演示一下标题栏的写法,故而Fragment就简单一些。
简简单单,一个Fragment。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TextFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="人尔 女子"
android:textSize="40dp"/>
</LinearLayout>
大概长这样:
接下来我们来写FragmentAdapter。
首先创建一个类,使他继承FragmentStateAdapter。
FragmentStateAdapter
是 Android 开发中的一部分,它是一个适配器类,用于管理与 Fragment 相关的数据集合。FragmentStateAdapter
继承自FragmentActivity.Callbacks
和FragmentManager.FragmentLifecycleCallbacks
,因此它可以接收到 Fragment 生命周期的回调,并根据数据的变化来管理 Fragment 的状态。
FragmentStateAdapter
通常与ViewPager2
一起使用,为ViewPager2
提供 Fragment。
刚继承完就爆红了,不要慌,alt+enter重写几个方法。
FragmentStateAdapter要求我们重写createFragment方法和getItemCount方法,
createFragment(int position)
:
Fragment
对象,对应于 ViewPager2
中的每个页面。position
参数表示当前页面的位置(索引),从0开始。Fragment
实例。getItemCount()
:
ViewPager2
中页面的数量。ViewPager2
应该有多少个页面。先创建一个管理fragment的list成员变量,用于管理fragment页面,然后填充几个函数即可,别忘了写一个新的构造方法。
代码如下:
public class FragmentAdapter extends FragmentStateAdapter { private List<Fragment> fragmentList; public FragmentAdapter(@NonNull FragmentActivity fragmentActivity , List<Fragment> fragmentList) { super(fragmentActivity); this.fragmentList = fragmentList; } @NonNull @Override public Fragment createFragment(int position) { return fragmentList == null ? null : fragmentList.get(position); } @Override public int getItemCount() { return fragmentList == null ? 0:fragmentList.size(); } }
为了防止空指针异常,我们在每次返回时,判断一次fragment是否为空。
配置ViewPager2的步骤也不复杂,首先回到MainActivity中,把刚刚写好的适配器和用于管理的List列表写出来:
private FragmentAdapter fragmentAdapter;
private List<Fragment> fragmentList;
然后将适配器实例化,再设置给viewPager组件即可,完整代码如下:
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private FragmentAdapter fragmentAdapter; private List<Fragment> fragmentList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); initData(); fragmentAdapter = new FragmentAdapter(this,fragmentList); binding.viewPager.setAdapter(fragmentAdapter); } private void initData() { fragmentList = new ArrayList<>(); fragmentList.add(new TextFragment()); fragmentList.add(new TextFragment()); fragmentList.add(new TextFragment()); } }
省去实例化Fragment的过程,仅仅两行代码就完成配置了,真是便便又捷捷呀。
我们不妨点进去这个ViewPager2的setAdapter方法,看看他都干了些什么。在网上搜索ViewPager2和ViewPager的区别的时候,总能看到一句话:两者最大的区别就是可以直接把ViewPager2看成RecyclerView,先放下这些疑惑,我们点进去看看:
点进去第一眼就是这个非常显眼的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);
}
笔者大概查询了一下,这段代码首先用
final Adapter<?> currentAdapter = mRecyclerView.getAdapter();
这段代码获取当前RecyclerView的适配器,并将其存储在
currentAdapter
变量中。
mAccessibilityProvider.onDetachAdapter(currentAdapter);
unregisterCurrentItemDataSetTracker(currentAdapter);
这段代码通知辅助功能提供者(Accessibility Provider)当前适配器已经被分离(detached)。这是为了在适配器更换时更新辅助功能的状态,而后取消注册当前适配器的数据集变化跟踪器。这通常是在适配器更换时进行的,以确保旧的适配器不再被跟踪。
mRecyclerView.setAdapter(adapter);
这行代码将一个新的适配器
adapter
设置给RecyclerView。这是实际更换适配器的操作。
restorePendingState();
这行代码尝试恢复挂起的状态。这可能是一个自定义方法,用于在适配器更换后恢复之前的状态,例如恢复滚动位置等。
mAccessibilityProvider.onAttachAdapter(adapter);
这行代码通知辅助功能提供者新的适配器已经被附加(attached)。这是为了确保辅助功能能够正确地与新的适配器交互。
registerCurrentItemDataSetTracker(adapter);
这行代码注册新的适配器的数据集变化跟踪器。这允许RecyclerView监听数据集的变化,并在变化发生时进行适当的更新。
大概总结一下,这段代码的作用就是更加安全地为内置的RecyclerView更换了适配器,并且确保一系列的辅助功能能正确的运行与更新。
看都看了,不妨再看一点:
翻着翻着发现ViewPager2的构造方法都要用到一个initalize方法,我们看看这个initalize方法都干了什么:
非常长一大串,不过没关系,我们忽略其中的大部分内容。看到这句话:
这两段想必并不陌生,这是创建RecyclerView视图时必不可缺的两句话,第一句用于创建Recycler实例,第二句用于创建一个线性布局管理器,而后将管理器设置到RecyclerView。
最后的这句代码也能望文生义,也就是将刚刚创建的RecyclerView附加到父视图上。
以上就是对ViewPager2的简单介绍了,希望对大家理解这个组件能有一些帮助。
在Activity的onCreate方法中增加一句话即可:
new TabLayoutMediator(binding.tabLayout, binding.viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int i) {
tab.setText("nihao");
}
}).attach();
这段代码是Android开发中用于连接TabLayout
和ViewPager2
(或ViewPager
)的TabLayoutMediator
类的使用示例。TabLayoutMediator
是一个实用工具类,它帮助开发者将TabLayout
的标签与ViewPager
或ViewPager2
的页面同步。以下是代码的详细分析:
创建TabLayoutMediator
实例:
new TabLayoutMediator(binding.tabLayout, binding.viewPager, …)
这行代码创建了一个新的TabLayoutMediator对象。它接收三个参数:
binding.tabLayout
: 一个TabLayout
实例,表示包含标签的组件。binding.viewPager
: 一个ViewPager
或ViewPager2
实例,表示用户可以左右滑动浏览的组件。TabLayoutMediator.TabConfigurationStrategy
接口的匿名类实例,这个接口定义了如何配置每个标签。配置标签:
new TabLayoutMediator.TabConfigurationStrategy() {…}
这是一个匿名内部类,实现了TabLayoutMediator.TabConfigurationStrategy
接口。这个接口包含一个方法onConfigureTab
,用于配置每个标签。
@Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int i) { … }
这是onConfigureTab方法的重写,它接收两个参数:
@NonNull TabLayout.Tab tab
: 当前需要配置的Tab
对象。int i
: 表示当前标签在TabLayout
中的位置(索引)。连接TabLayout
和ViewPager
:
.attach();
TabLayoutMediator
实例的attach
方法,它的作用是将TabLayout
和ViewPager
连接起来。一旦连接,当ViewPager
的页面发生变化时,TabLayout
会相应地更新当前选中的标签。同样,当用户点击某个标签时,ViewPager
会滚动到对应的页面。笔者不小心手滑,刚写完代码就点进去了一个陌生的页面。
原来是attach()方法的实现原理,正巧笔者也对TabLayout如何连接两个组件非常好奇,不妨来看一看。
首先这个方法会检查Mediator是否连接到了viewPager,而后会检查viewPager的适配器是否存在,保障其安全性。
一切准备就绪后,将attached的状态设置为true,接下来就是激动人心的逻辑环节了:
创建页面变化回调:
this.onPageChangeCallback = new TabLayoutOnPageChangeCallback(this.tabLayout);
TabLayoutOnPageChangeCallback
实例,用于处理 ViewPager2
页面变化事件,并更新 TabLayout
。注册页面变化回调:
this.viewPager.registerOnPageChangeCallback(this.onPageChangeCallback);
ViewPager2
。创建标签选择监听器:
this.onTabSelectedListener = new ViewPagerOnTabSelectedListener(this.viewPager, this.smoothScroll);
创建一个 ViewPagerOnTabSelectedListener
实例,用于处理 TabLayout
标签选择事件,并更新 ViewPager2
。
添加标签选择监听器:
this.tabLayout.addOnTabSelectedListener(this.onTabSelectedListener);
将创建的标签选择监听器添加到 TabLayout
。
设置标签滚动位置:
this.tabLayout.setScrollPosition(this.viewPager.getCurrentItem(), 0.0F, true);
TabLayout
中当前选中标签的滚动位置,确保用户可以看到当前页面对应的标签。中间有一段实在不知道干什么用的,就留给读者去解决啦~
本文参考:
【Android】ViewPager2和TabLayout协同使用,实现多Fragment页面切换类似于QQ音乐,bilibili效果_tablayout和viewpager2-CSDN博客
【Android】ViewPager2监听页面切换事件_viewpager2 监听-CSDN博客
安卓:TabLayout+ViewPager2+Fragment使用(java)_tablayout viewpager2 fragment-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。