6、布局与UI组件(下):RecyclerView与Adapter、ListView与GridView、ViewPager与Fragment、Material Design组件入门
好,咱们接着聊布局和UI组件。上一章我们把基础布局和常用控件过了一遍,这一章要上点硬菜了——列表、滑动页、还有Material Design那套现代化的设计语言。这些东西在实际项目中几乎天天用,我敢说,你学会这章,基本就能应付80%的界面需求了。
本章核心脉络:从最经典的ListView,到更灵活的RecyclerView,再到页面滑动的ViewPager,最后用Material Design组件给UI“化妆”。说白了,就是让你知道怎么展示列表数据、怎么切换页面、怎么让界面好看又规范。
6.1 从ListView到RecyclerView:列表的进化
先说说ListView。这玩意儿是Android早期就有的列表控件,用起来简单,但坑也不少。我记得刚入行那会儿,做一个联系人列表,直接用ListView加ArrayAdapter,数据一多就开始卡顿。后来才知道,ListView默认没有强制复用View,你得自己在getView里做优化。
说白了,ListView就像个老实人,你给它什么它显示什么,但不会主动帮你省内存。而RecyclerView呢?它是Google后来推出的“升级版”,强制你使用ViewHolder模式,还自带布局管理器,想横着滑、竖着滑、网格滑都行。
我的建议:新项目直接上RecyclerView,别犹豫。ListView只适合维护老项目。如果你还在用ListView写新功能,嗯,该升级了。
6.1.1 RecyclerView的基本使用
RecyclerView的使用分三步:定义布局、创建Adapter、绑定数据。我习惯把Adapter单独写成一个内部类,这样逻辑清晰。
// 1. 在布局中添加RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
// 2. 创建Adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mData;
public MyAdapter(List<String> data) {
this.mData = data;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_text, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.textView.setText(mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv_text);
}
}
}
// 3. 在Activity中设置
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MyAdapter(dataList));
你看,代码结构很清晰。onCreateViewHolder负责创建View,onBindViewHolder负责填充数据。这里有个细节:ViewHolder是必须的,它缓存了子View的引用,避免每次调用findViewById。我在项目中见过有人不用ViewHolder,结果列表滑动时一卡一卡的,后来改成ViewHolder,流畅度直接翻倍。
6.1.2 LayoutManager的三种模式
RecyclerView的灵活之处在于LayoutManager。你可以换着花样展示列表:
| LayoutManager | 效果 | 适用场景 |
|---|---|---|
| LinearLayoutManager | 垂直或水平线性列表 | 聊天记录、新闻列表 |
| GridLayoutManager | 网格布局 | 相册、商品展示 |
| StaggeredGridLayoutManager | 瀑布流布局 | 笔记应用、图片墙 |
举个例子,如果你要做个类似微信朋友圈的九宫格图片,用GridLayoutManager设置spanCount为3就行。我曾经做过一个电商App,商品列表用StaggeredGridLayoutManager,每个商品卡片高度不同,看起来特别自然。
注意:使用StaggeredGridLayoutManager时,如果item高度变化太大,可能会出现item错位。我曾经遇到过图片加载完成后高度突变,导致布局乱掉。解决方案是给图片设置固定宽高比,或者用Glide的override方法预占位。
6.2 GridView:网格列表的老前辈
GridView和ListView是同一时代的产物。它把数据展示成网格,每行可以有多列。用法和ListView几乎一样,也是用Adapter。不过说实话,现在有了RecyclerView的GridLayoutManager,GridView基本被取代了。
但如果你在维护老项目,还是得会。GridView有个坑:它默认不支持点击item的动画,而且嵌套在ScrollView里会出问题。我建议,除非项目强制要求,否则别用GridView,直接用RecyclerView加GridLayoutManager,省心。
6.3 ViewPager与Fragment:页面滑动切换
ViewPager是Android里实现左右滑动页面的利器。最常见的场景就是引导页、图片轮播、或者Tab切换。ViewPager需要配合PagerAdapter使用,而FragmentPagerAdapter和FragmentStatePagerAdapter是两种常用的实现。
6.3.1 基本用法
// 创建FragmentPagerAdapter
public class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
public MyPagerAdapter(FragmentManager fm, List<Fragment> list) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.fragmentList = list;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
// 在Activity中设置
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), fragments));
// 配合TabLayout
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
这里有个选择:FragmentPagerAdapter会把所有Fragment保存在内存中,适合页面少的情况;FragmentStatePagerAdapter会销毁不可见的Fragment,适合页面多的情况。我个人的经验是,超过4个页面就用FragmentStatePagerAdapter,否则内存容易爆。
避坑指南:我曾经在ViewPager嵌套Fragment时,遇到Fragment重叠的问题。原因是Fragment被重复添加了。解决方案是在FragmentPagerAdapter的getItem方法中不要每次都new Fragment,而是从集合中取。或者使用FragmentStatePagerAdapter,它会自动管理状态。
6.3.2 ViewPager2:更现代的替代
ViewPager2是ViewPager的升级版,基于RecyclerView实现。它解决了ViewPager的一些老问题,比如不支持RTL布局、不能竖直滑动等。用法也差不多,只是Adapter变成了RecyclerView.Adapter。
如果你是新项目,我强烈建议直接用ViewPager2。它更稳定,而且支持竖直滑动,做那种上下滑动的卡片效果特别方便。
6.4 Material Design组件入门
Material Design是Google推出的一套设计语言,说白了就是让Android App看起来更现代、更统一。Android提供了很多Material Design组件,咱们挑几个最常用的说说。
6.4.1 FloatingActionButton(悬浮按钮)
FAB就是那个圆形的、带图标的按钮,通常放在右下角。它用来触发主要操作,比如写邮件、添加新条目。
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:src="@drawable/ic_add"
app:backgroundTint="@color/primaryColor" />
FAB的点击事件和普通按钮一样。但要注意,FAB不要滥用,一个页面最多一个。我见过有人一个页面放三个FAB,那画面太美不敢看。
6.4.2 Snackbar:轻量级提示
Snackbar是Toast的升级版,它可以从底部弹出,还支持一个操作按钮。比如删除操作后,弹出一个“已删除,点击撤销”的Snackbar。
Snackbar.make(view, "数据已删除", Snackbar.LENGTH_LONG)
.setAction("撤销", v -> {
// 恢复数据
})
.show();
注意,Snackbar需要一个View作为父容器,通常传CoordinatorLayout。这样Snackbar才能正确显示在底部,并且不影响FAB的位置。
6.4.3 CardView:卡片式布局
CardView是一个带圆角和阴影的容器,用来展示独立的信息块。比如一条新闻、一个商品。
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="4dp"
app:strokeColor="@color/gray"
app:strokeWidth="1dp">
<!-- 内容 -->
</com.google.android.material.card.MaterialCardView>
CardView配合RecyclerView使用效果极佳。每个item都是一个卡片,看起来干净利落。我在项目中经常用CardView做列表项,用户反馈说界面看起来“很高级”。
6.4.4 AppBarLayout与CollapsingToolbarLayout
这两个组件用来实现可折叠的顶部栏。比如一个详情页,顶部有图片,向上滑动时图片慢慢缩小,最后变成普通的Toolbar。效果很炫酷,实现也不复杂。
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
这里的关键是layout_scrollFlags和layout_collapseMode。scroll表示跟随滚动,exitUntilCollapsed表示折叠到最小高度后固定。parallax是视差效果,pin是固定住。嗯,这些参数多试几次就明白了。
6.5 实战建议:如何选择组件
学完这么多组件,你可能会问:什么时候用哪个?我总结了一个简单的选择逻辑:
- 展示列表数据:首选RecyclerView,灵活高效。
- 页面滑动切换:用ViewPager2,配合TabLayout。
- 需要卡片效果:CardView + RecyclerView,绝配。
- 需要悬浮操作按钮:FloatingActionButton,一个页面一个。
- 需要折叠顶部栏:AppBarLayout + CollapsingToolbarLayout。
记住,组件是工具,不是目的。别为了用而用,合适才是最好的。我在项目中见过有人硬套Material Design,结果界面反而变得臃肿。保持简洁,用户看着舒服,代码也好维护。
最后一个小技巧:写RecyclerView时,记得在Adapter里实现setHasStableIds(true),并重写getItemId()。这样当数据变化时,RecyclerView能更高效地更新界面,避免闪烁。我踩过这个坑,数据刷新时整个列表闪了一下,用户体验极差。
公众号:蓝海资料掘金营,微信deep3321