Android入门教程|Android入门教程 | RecyclerView实际使用

RecyclerView实际使用
用 RecyclerView 做出一个列表。显示测试数据。
这个示例会分为以下几个步骤:

  • 确定数据。这里用的是模拟数据。
  • 设计UI和表现形式。
  • 编写 layout 与适配器。
模拟数据 巧妇难为无米之炊,模拟数据就是模拟需求。
新建一个类 DataTest,设计 4 个属性。
public class DataTest { private String timezone; private int number; private int personCount; private int count; public DataTest(String timezone, int number, int personCount, int count) { this.timezone = timezone; this.number = number; this.personCount = personCount; this.count = count; } // getter setter... }

设计 item 的布局 设计UI,在一行里显示 4 个属性值。item 用的是 item_recy2.xml

准备颜色,尺寸等资源 相关的style和颜色,尺寸配置文件,在res/values目录下。
style文件style.xml
0dp 40dp 2 #ffffff center @color/rePage2Item

我们给 layout 里的每个TextView都设置了 layout_width 为 0dp。是为了使用 layout_weight 属性。 让它们 4 个 TextView 按比例分割父 View 的宽度。
这里的分割用到了LinearLayout的分割占比特性。
颜色配置文件color.xml,添加如下颜色设置。
#082941

尺寸配置dimens.xml。
4dp 2dp

设计 ViewHolder 资源文件和 layout 准备妥当,开始写对应的 viewHolder。这里也是把 VH 类和 Adapter 类放在 activity 类里面。
private class VH extends RecyclerView.ViewHolder { TextView tv1; TextView tv2; TextView tv3; TextView tv4; public VH(@NonNull View itemView) { super(itemView); tv1 = itemView.findViewById(R.id.tv1); tv2 = itemView.findViewById(R.id.tv2); tv3 = itemView.findViewById(R.id.tv3); tv4 = itemView.findViewById(R.id.tv4); } }

设计 Adapter 类 适配器 Adapter 类。
private class Adapter extends RecyclerView.Adapter {private List dataList = new ArrayList<>(); public Adapter() { }public void setDataList(List dataList) { this.dataList = dataList; // 最好进行判空操作 notifyDataSetChanged(); }@NonNull @Override public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recy2, parent, false)); }@Override public void onBindViewHolder(@NonNull VH holder, int position) { DataTest dataTest = dataList.get(position); holder.tv1.setText(dataTest.getTimezone()); holder.tv2.setText(String.valueOf(dataTest.getNumber())); holder.tv3.setText(String.valueOf(dataTest.getPersonCount())); holder.tv4.setText(String.valueOf(dataTest.getCount())); }@Override public int getItemCount() { return dataList.size(); } }

配置 RecyclerView 设置 recyclerview。
// 在activity或者fragment里 private Adapter mAdapter = new Adapter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_recy_2); RecyclerView recyclerView = findViewById(R.id.re_view); recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false)); recyclerView.setAdapter(mAdapter); mAdapter.setDataList(genDataTestList()); recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.top = getResources().getDimensionPixelOffset(R.dimen.re_2_gap); } }); }// 生成模拟数据 private List genDataTestList() { List list = new ArrayList<>(); for (int i = 1; i <= 60; i++) { DataTest d = new DataTest("时区" + i, i, i, i); list.add(d); } return list; }

在 onCreate 方法中配置 recyclerView。 recyclerView.addItemDecoration 方法是给 item 设置间隔样式。 getItemOffsets 可以设置子项的间距。这里给子项底部一个间距值。具体数值设置在 dimen 中。 genDataTestList() 是生成模拟的数据。
使用 include 修改 layout 有的朋友问:表头和item的结构是一样的,可以复用吗?
其实是可以的。我们可以在layout中使用include标签,把另一个layout文件“包含”进来。 复制act_recy_2.xml粘贴得到act_recy_2_include.xml,把原来的表头的 LinearLayout 改成 include。

给 include 设定 layout,即我们定义的 item 的布局item_recy2。 当我们想添加个margin-top的时候,比如这样

as会弹出警告:
Layout parameter layout_marginTop ignored unless both layout_width and layout_height are also specified on tag
也就是在 include 标签中,如果要设置其他属性,需要先设置 layout_width 和 layout_height。
修改一下,再加个 id,变成这样。

让Activity使用这个layout。修改一下RecyclerViewDemo2Act。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_recy_2_include); initHeader(); // 配置RecyclerView的部分 }private void initHeader() { View header = findViewById(R.id.header); TextView tv1 = header.findViewById(R.id.tv1); tv1.setText("时区"); TextView tv2 = header.findViewById(R.id.tv2); tv2.setText("序号"); TextView tv3 = header.findViewById(R.id.tv3); tv3.setText("人员"); TextView tv4 = header.findViewById(R.id.tv4); tv4.setText("数量"); }

我们添加了一个方法 initHeader()。里面先把 header 找到,通过 header 找到它的子 view,也就是那4个TextView。 分别设置文字即可。
RecyclerView 相关面试题
1. RecyclerView第一次layout时,会发生预布局pre-layout吗?
第一次布局时,并不会触发pre-layout。pre-layout只会在每次notify change时才会被触发,目的是通过saveOldPosition方法将屏幕中各位置上的ViewHolder的坐标记录下来,并在重新布局之后,通过对比实现Item的动画效果。
2. ViewHolder何时被缓存到RecycledViewPool中?
主要有以下2种情况:
  • 当ItemView被滑动出屏幕时,并且CachedView已满,则ViewHolder会被缓存到RecycledViewPool中
  • 当数据发生变动时,执行完disappearrance的ViewHolder会被缓存到RecycledViewPool中
3. CachedView 和 RecycledViewPool 的关系
当一个ItemView被滑动滚出屏幕之后,默认会先被保存在CachedView中。CachedView的默认大小为2,可以通过 setItemViewCacheSize 方法修改它的值。
当CachedView已满后,后续有新的ItemView从屏幕内滑出时,会迫使CachedView根据FIFO规则,将之前的缓存的ViewHolder转移到RecycledViewPool中.
RecycledViewPool默认大小为5,可以通过以下方式修改RecycledViewPool的缓存大小:
RecyclerView.getRecycledViewPool().setMaxRecycledViews(int viewType, int max);

4. CachedView 和 RecycledViewPool 两者区别
缓存到CachedView中的ViewHolder并不会清理相关信息(比如position、state等),因此刚移出屏幕的ViewHolder,再次被移回屏幕时,只要从CachedView中查找并显示即可,不需要重新绑定(bindViewHolder)。
而缓存到RecycledViewPool中的ViewHolder会被清理状态和位置信息,因此从RecycledViewPool查找到ViewHolder,需要重新调用bindViewHolder绑定数据。
5. 你是从哪些方面优化RecyclerView的?
主要可以从以下几个方面对 RecyclerView 进行优化:
  • 尽量将复杂的数据处理操作放到异步中完成。RecyclerView 需要展示的数据经常是从远端服务器上请求获取,但是在网络请求拿到数据之后,需要将数据做扁平化操作,尽量将最优质的数据格式返回给UI线程。
  • 优化 RecyclerView 的布局,避免将其与 ConstraintLayout 使用
  • 针对快速滑动事件,可以使用 addOnScrollListener 添加对快速滑动的监听,当用户快速滑动时,停止加载数据操作。
  • 如果 ItemView 的高度固定,可以使用 setHasFixSize(true)。这样 RecyclerView 在 onMeasure 阶段可以直接计算出高度,不需要多次计算子 ItemView 的高度,这种情况对于垂直 RecyclerView 中嵌套横向 RecyclerView 效果非常显著。
  • 当 UI 是 Tab feed 流时,可以考虑使用 RecycledViewPool 来实现多个 RecyclerView 的缓存共享。
【Android入门教程|Android入门教程 | RecyclerView实际使用】Android零基础入门教程视频参考

    推荐阅读