图文搞懂RecyclerView 的各部分组成 | RecyclerView进阶

这是一个系列,该文为系列第一篇。

1 概述

RecyclerView作为Android开发中最常用的View之一。很多App的feed流都是使用RecyclerView来实现的。加深对于RecyclerView的掌握对于开发效率和开发质量都有很重要的意义。

接下来我打算从源码角度剖析RecyclerView的实现,加深对于RecycledView的了解。RecyclerView的源码实现还是很庞大的。本文就先来看一下RecyclerView的整体设计,了解其核心实现类的作用以及大致实现原理。

下面这张图是我截取的RecyclerView的Structure:
类的组成

本文着重看:

ViewHolder、Adapter、AdapterDataObservable、RecyclerViewDataObserver、LayoutManager、、Recycler、RecyclerPool。

从而理解RecycledView的大致实现原理。

先用一张图大致描述他们之间的关系,这张图是adapter.notifyXX()时RecyclerView的执行逻辑涉及到的一些类:
RecyclerView组成类之间的关系

2 ViewHolder

对于Adapter来说,一个ViewHolder就对应一个data。它也是Recycler缓存池的基本单元。

    class ViewHolder {
        public final View itemView;
        int mPosition = NO_POSITION;
        int mItemViewType = INVALID_TYPE;
        int mFlags;
        ...
    }

上面我列出了ViewHolder最重要的4个属性:

  1. itemView : 会被当做child view来add到RecyclerView中。
  2. mPosition : 标记当前的ViewHolder在Adapter中所处的位置。
  3. mItemViewType :
    这个ViewHolder的Type,在ViewHolder保存到RecyclerPool时,主要靠这个类型来对ViewHolder做复用。
  4. mFlags : 标记ViewHolder的状态,比如
    FLAG_BOUND(显示在屏幕上)、FLAG_INVALID(无效,想要使用必须rebound)、FLAG_REMOVED(已被移除)等。

3 Adapter

它的工作是把data和View绑定,即上面说的一个data对应一个ViewHolder。主要负责ViewHolder的创建以及数据变化时通知RecycledView。

比如下面这个Adapter:

    class SimpleStringAdapter(val dataSource: List<String>, val context: Context) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            if (holder.itemView is ViewHolderRenderProtocol) {
                (holder.itemView as ViewHolderRenderProtocol).render(dataSource[position], position)
            }
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = SimpleVH(SimpleStringView(context))
    
        override fun getItemCount() = dataSource.size
    
        override fun getItemViewType(position: Int) = 1
    
        override fun notifyDataSetChanged() {   //super的实现
            mObservable.notifyChanged();
        }  
    }

即:

  1. 它引用着一个数据源集合dataSource
  2. getItemCount()用来告诉RecyclerView展示的总条目
  3. 它并不是直接映射data -> ViewHolder, 而是 data position -> data type ->
    viewholder。 所以对于ViewHolder来说,它知道的只是它的view type

4 AdapterDataObservable

Adapter是数据源的直接接触者,当数据源发生变化时,它需要通知给RecyclerView。这里使用的模式是观察者模式。AdapterDataObservable是数据源变化时的被观察者。RecyclerViewDataObserver是观察者。

在开发中我们通常使用adapter.notifyXX()来刷新UI,实际上Adapter会调用AdapterDataObservable的notifyChanged():

    public void notifyChanged() {
        for (int i = mObservers.size() - 1; i >= 0; i--) {
            mObservers.get(i).onChanged();
        }
    }

逻辑很简单,即通知Observer数据发生变化。

5 RecyclerViewDataObserver

它是RecycledView用来监听Adapter数据变化的观察者:

    public void onChanged() {
        mState.mStructureChanged = true; // RecycledView每一次UI的更新都会有一个State
        processDataSetCompletelyChanged(true);
        if (!mAdapterHelper.hasPendingUpdates()) {
            requestLayout();
        }
    }

6 LayoutManager

它是RecyclerView的布局管理者,RecyclerView在onLayout时,会利用它来layoutChildren,它决定了RecyclerView中的子View的摆放规则。但不止如此, 它做的工作还有:

  1. 测量子View
  2. 对子View进行布局
  3. 对子View进行回收
  4. 子View动画的调度
  5. 负责RecyclerView滚动的实现

7 Recycler

对于LayoutManager来说,它是ViewHolder的提供者。对于RecyclerView来说,它是ViewHolder的管理者,是RecyclerView最核心的实现。下面这张图大致描述了它的组成:
Recycler的组成

scrap list

    final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
    ArrayList<ViewHolder> mChangedScrap = null;

View Scrap状态

相信你在许多RecyclerView的crash log中都看到过这个单词。它是指View在RecyclerView布局期间进入分离状态的子视图。即它已经被deatach(标记为FLAG_TMP_DETACHED状态)了。这种View是可以被立即复用的。

它在复用时,如果数据没有更新,是不需要调用onBindViewHolder方法的。如果数据更新了,那么需要重新调用onBindViewHolder。

mAttachedScrap和mChangedScrap中的View复用主要作用在adapter.notifyXXX时。这时候就会产生很多scrap状态的view。 也可以把它理解为一个ViewHolder的缓存。不过在从这里获取ViewHolder时完全是根据ViewHolder的position而不是item type。如果在notifyXX时data已经被移除掉你,那么其中对应的ViewHolder也会被移除掉。

mCacheViews

可以把它理解为RecyclerView的一级缓存。它的默认大小是3, 从中可以根据item type或者position来获取ViewHolder。可以通过RecyclerView.setItemViewCacheSize()来改变它的大小。

RecycledViewPool

它是一个可以被复用的ViewHolder缓存池。即可以给多个RecycledView来设置统一个RecycledViewPool。这个对于多tab feed流应用可能会有很显著的效果。它内部利用一个ScrapData来保存ViewHolder集合:

    
    class ScrapData {
        final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
        int mMaxScrap = DEFAULT_MAX_SCRAP;   //最多缓存5个
        long mCreateRunningAverageNs = 0;
        long mBindRunningAverageNs = 0;
    }
  
  SparseArray<ScrapData> mScrap = new SparseArray<>();  //RecycledViewPool 用来保存ViewHolder的容器

一个ScrapData对应一种type的ViewHolder集合。看一下它的获取ViewHolder和保存ViewHolder的方法:

    //存
    public void putRecycledView(ViewHolder scrap) {
        final int viewType = scrap.getItemViewType();
        final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
        if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size())  return; //到最大极限就不能放了
        scrap.resetInternal();  //放到里面,这个view就相当于和原来的信息完全隔离了,只记得他的type,清除其相关状态
        scrapHeap.add(scrap);
    }
    
    //取
    private ScrapData getScrapDataForType(int viewType) {
        ScrapData scrapData = mScrap.get(viewType);
        if (scrapData == null) {
            scrapData = new ScrapData();
            mScrap.put(viewType, scrapData);
        }
        return scrapData;
    }

以上所述,是RecycledView最核心的组成部分(本文并没有描述动画的部分)。
下一篇文章会分析RecyclerView的刷新机制.

## 免费获取安卓开发架构的资料(包括Fultter、高级UI、性能优化、架构师课程、 NDK、Kotlin、混合式开发(ReactNative+Weex)和一线互联网公司关于android面试的题目汇总可以加:936332305 / 链接:点击链接加入【安卓开发架构】:https://jq.qq.com/?_wv=1027&k=515xp64 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190123143200826.png)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要使用RecyclerView实现图文列表,可以按照以下步骤进行操作: 1. 在布局文件添加RecyclerView控件 ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:clipToPadding="false" android:padding="5dp" android:scrollbars="vertical" /> ``` 2. 创建RecyclerView的Adapter类 ``` public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private List<MyData> dataList; public MyAdapter(List<MyData> dataList) { this.dataList = dataList; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_layout, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { MyData data = dataList.get(position); holder.titleTextView.setText(data.getTitle()); holder.contentTextView.setText(data.getContent()); holder.imageView.setImageResource(data.getImageResource()); } @Override public int getItemCount() { return dataList.size(); } static class MyViewHolder extends RecyclerView.ViewHolder { TextView titleTextView; TextView contentTextView; ImageView imageView; MyViewHolder(@NonNull View itemView) { super(itemView); titleTextView = itemView.findViewById(R.id.title_text_view); contentTextView = itemView.findViewById(R.id.content_text_view); imageView = itemView.findViewById(R.id.image_view); } } } ``` 3. 创建数据类 ``` public class MyData { private String title; private String content; private int imageResource; public MyData(String title, String content, int imageResource) { this.title = title; this.content = content; this.imageResource = imageResource; } public String getTitle() { return title; } public String getContent() { return content; } public int getImageResource() { return imageResource; } } ``` 4. 创建item布局文件 ``` <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/image_view" android:layout_width="72dp" android:layout_height="72dp" android:layout_marginEnd="10dp" android:src="@drawable/ic_launcher_background" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/title_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/content_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Content" android:textSize="14sp" /> </LinearLayout> </LinearLayout> ``` 5. 在Activity设置RecyclerView的Adapter和LayoutManager ``` RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); List<MyData> dataList = new ArrayList<>(); dataList.add(new MyData("Title1", "Content1", R.drawable.image1)); dataList.add(new MyData("Title2", "Content2", R.drawable.image2)); dataList.add(new MyData("Title3", "Content3", R.drawable.image3)); MyAdapter adapter = new MyAdapter(dataList); recyclerView.setAdapter(adapter); ``` 这样就可以实现一个简单的图文列表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值