TabLayout 是 Android 支持库中的一个组件,它是 Design 支持库的一部分。TabLayout 提供了一个水平的标签页界面,允许用户在不同的视图或数据集之间进行切换。以下是 TabLayout 的一些主要特性和使用方法
ViewPager2 是 Android Jetpack 库中的一个组件,它是 ViewPager 的一个改进版本,提供了更好的性能和更多的功能。ViewPager2 允许用户左右滑动来浏览不同的视图,类似于一个滑动页面的控件。
ViewPager2 优化了滑动性能,特别是在处理大量页面或复杂视图时。ViewPager 仅支持水平滑动。TabLayout 集成:可以与 TabLayout 集成,实现标签页和页面视图的联动。官方文档:
ViewPager2 | Jetpack | Android Developers (google.cn)
使用 ViewPager2 创建包含标签的滑动视图 | Android Developers (google.cn)
本篇博客,笔者将带大家实现这样一个标题栏+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 中当前选中标签的滚动位置,确保用户可以看到当前页面对应的标签。中间有一段实在不知道干什么用的,就留给读者去解决啦~
本文参考:
因篇幅问题不能全部显示,请点此查看更多更全内容