深入RecyclerView(一)

原文链接:https://developer.android.com/guide/topics/ui/layout/recyclerview

用RecyclerView来创建列表视图

如果你的应用需要显示一个滚动的列表视图,并且这个列表包含了大量数据(或一些频繁更改的数据,则可以按照本篇文章的指引来使用RecyclerView。

深入RecyclerView(一)_第1张图片

如果你想创建一个卡片列表,如图2 所示,你也可以使用CardView组件,具体用法参见

创建基于卡片的布局.

如果想查看RecylerView的使用示例代码,参见 RecyclerView Sample App.

RecyclerView 概述

RecyclerView 控件 是ListView 的升级版,更加高级和灵活。
在RecyclerView模型中,会有几个不同的组件配合工作来显示数据。添加到布局中的RecyclerView对象充当构成用户界面的容器。 RecyclerView会使用你设置的布局管理器提供的视图来填充自己的视图。你可以使用系统提供的标准布局管理器(例如LinearLayoutManagerGridLayoutManager),或者实现你自己的布局管理器。

列表中的View组件由ViewHolder(view组件持有者)来表示。你可以继承RecyclerView.ViewHolder 类来创建一个ViewHolder。每个ViewHolder负责显示单个Item的视图。举个例子来说,如果你的列表显示用户的音乐收藏,那每个view holder可能代表的是一个专辑。 RecyclerView只会创建那些需要在屏幕上显示内容的view holder及额外少量的view holder。当用户滚动浏览列表时,RecyclerView会取出未在屏幕上显示的view,并将它们和滚动到屏幕上需要进行显示的数据进行绑定。

view holder(视图持有者)对象由一个adapter(适配器)管理,该适配器通过继承RecyclerView.Adapter创建。adapter根据需要创建view holder。adapter还负责将view holder 和数据进行绑定。它通过将view holder分配给某个位置并调用适配器的onBindViewHolder()方法来完成此操作。这个方法利用view holder 的位置并根据它在列表中的位置来显示内容。

这个RecyclerView模型做了很多优化工作,所以你不必:

  • 当列表首次填充时,它会在列表任一侧创建并绑定一些view holder。例如,如果视图正在显示列表位置0到9,RecyclerView会创建并绑定这些view holder,并且还可能创建并绑定位置10的view holder。这样,如果用户滚动列表,则下一个元素已经提前准备好了。

  • 当用户滚动列表时,RecyclerView根据需要创建新的view holder。它还可以保存已经滚动到屏幕外的view holder,让他们能被重复使用。如果用户变换了滚动方向,那么之前已经滚动出屏幕的view holder就可以拿回来重用。另一方面,如果用户保持相同方向来滚动,那么已经离开最久的view holder可以被重新绑定到新数据。view holder不需要再次执行创建或构造view 的过程,只需要更新view的内容即可。

  • 当显示的item数据发生变化时,你可以通过调用合适的RecyclerView.Adapter.notify ...()方法来通知适配器。之后适配器的内部代码逻辑会自己重新绑定受影响的item。

添加support library

要使用 RecyclerView 控件,你要在项目添加 v7 Support Libraries 依赖。

  1. 打开 module 的build.gradle 文件
  2. dependencies 节点下 添加:
dependencies {
    implementation 'com.android.support:recyclerview-v7:27.1.1'
}

在布局文件添加 RecyclerView

之后,你就可以在布局文件中添加RecyclerView 了。下面的这个布局把RecyclerView 作为了整个布局的唯一view。



<android.support.v7.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

然后你就在代码中来操作RecyclerView 了,获取它的引用,设置一个布局管理器,并给需要显示的数据来添加一个adapter 。过程如下:

public class MyActivity extends Activity {
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);
    }
    // ...
}

添加列表adapter

要把数据填充到list中去,你需要继承 RecyclerView.Adapter 类。此对象负责为列表项创建视图,并在原始列表项不再可见时用新数据替换某些视图的内容。下面的代码示例实现了一个包含字符串数组的列表,并用TextView 组件显示出来。


public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private String[] mDataset;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        TextView v = (TextView) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.my_text_view, parent, false);
        ...
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset[position]);

    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

布局管理器调用适配器的onCreateViewHolder()方法。该方法需要构建一个RecyclerView.ViewHolder并设置其用于显示内容的视图。 ViewHolder的类型必须与Adapter类签名中声明的类型相匹配。通常,它会通过加载XML布局文件来设置视图。由于view holder尚未分配给任何特定的数据,因此该方法实际上并未设置视图的内容。

布局管理器然后将view holder绑定到对应的数据。它通过调用适配器的onBindViewHolder()方法并在RecyclerView中传递view holder的位置来完成此操作。 onBindViewHolder()方法需要获取适当的数据,并用它来填充view holder的布局。例如,如果RecyclerView显示一个名称列表,该方法可能会在列表中找到适当的名称,并填入view holder的TextView控件中。

如果列表需要更新,则调用RecyclerView.Adapter对象的通知方法,例如notifyItemChanged()。布局管理器然后会重新绑定任何受影响的view holder,让他们更新数据。

自定义你的RecyclerView

你可以自定义RecyclerView对象来满足特定需求。标准类已经提供了大多数开发人员需要的所有功能;在许多情况下,你需要做的唯一定制就是为每个view holder设计视图,并编写代码用合适的数据来更新这些视图。但是,如果你的应用程序需求特殊,则可以通过多种方式修改标准行为。以下各节介绍其他一些常用自定义设置。

修改布局

RecyclerView使用布局管理器将各个列表项放置在屏幕上,并确定何时重用对用户不再可见的列表项view视图。要重用(或回收)view,布局管理器可能会请求适配器使用数据集当中的不同元素来替换视图的内容。通过避免创建不必要的视图或执行代价高昂的findViewById()查找方法重用视图提升了性能。 Android支持库包含三个标准布局管理器,每个管理器都提供了许多自定义选项:

  • LinearLayoutManager将项目排列在一维列表中。在LinearLayoutManager中使用RecyclerView提供了像早期ListView布局一样的功能。

  • GridLayoutManager将项目排列在二维网格中,例如棋盘上的方格。在GridLayoutManager中使用RecyclerView提供了像旧版GridView布局一样的功能。

  • StaggeredGridLayoutManager将项目排列在一个二维网格中,每列与之前的一列略有偏移,就像美国国旗中的星星一样。
    如果这些布局管理器都不符合你的需求,则可以通过扩展RecyclerView.LayoutManager抽象类来创建自己的布局管理器 。

添加item动画

每当item发生变化时,RecyclerView会使用 一个animator来改变其外观。这个animator 对象继承自RecyclerView.ItemAnimator类。默认情况下,RecyclerView使用DefaultItemAnimator来提供动画。如果你想提供自定义动画,你可以通过继承RecyclerView.ItemAnimator来定义你自己的animator 对象。

启用列表项选择

recyclerview-selection 库让用户能够使用触摸操作或鼠标操作来选择RecyclerView列表项。你可以自定义选定列表项时的视觉呈现效果。你也可以自定义选取策略,例如哪些列表项可以被选择以及可以选择的列表项数量。

要将RecyclerView,请按照下列步骤操作:

1. 确定要使用哪种选择键类型,然后构建一个ItemKeyProvider

有三种关键类型可用于标识选定项目:Parcelable(以及Uri等所有子类),StringLong。有关选择键类型的详细信息,请参阅SelectionTracker.Builder

2. 实现ItemDetailsLookup 接口。

ItemDetailsLookup使选择库可以访问给定MotionEvent的RecyclerView 列表项的信息。它实际上是一个ItemDetails实例的工厂,它是RecyclerView.ViewHolder的一个实例拷贝(或从中一个实例中提取而来)。

3. 更新RecyclerView中的列表视图以反映用户已选择或取消选择

selection library不提供所选项目的默认视觉效果。当你实现onBindViewHolder()时你必须提供这个。推荐的方法如下:
* 在onBindViewHolder()中,使用truefalse(取决于是否选择该列表项),在View对象上调用setActivated()(而不是setSelected())。
* 更新视图的样式来表示激活的状态。我们建议您使用 color state list resource(颜色状态列表)资源来配置样式。

4. 使用ActionMode给用户提供工具来执行选取操作。

注册一个SelectionTracker.SelectionObserver以在选择更改时通知。首次创建选择时,启动ActionMode将其表示给用户,并提供特定于选择的操作。例如,您可以向ActionMode栏添加一个删除按钮,并连接栏上的后退箭头以清除选择。当选择变为空时(如果用户上次清除选择),不要忘记终止操作模式。

5. 监听选取操作

在结束处理事件序列时,此库可以确定用户是试图通过点击它来激活列表项,还是试图拖放一个或一组选定的列表项。通过注册适当的监听器来监听操作。更多信息请参见SelectionTracker.Builder

6. 使用SelectionTracker.Builder 构造器

以下示例展示了如何利用构造器 采取Long选择键将多个操作组合在一起:

SelectionTracker tracker = new SelectionTracker.Builder<>(
        "my-selection-id",
        recyclerView,
        new StableIdKeyProvider(recyclerView),
        new MyDetailsLookup(recyclerView),
        StorageStrategy.createLongStorage())
        .withOnItemActivatedListener(myItemActivatedListener)
        .build();

为了构建SelectionTracker实例,你的应用程序必须给SelectionTracker.Builder提供用于初始化RecyclerView 的那个 RecyclerView.Adapter 。因此,你可以在RecyclerView.Adapter 中 注入一个 SelectionTracker 实例,在adapter 初始化时 初始化这个实例。否则,你无法通过onBindViewHolder()方法检查列表项的选定状态。

7. 在Activity 生命周期事件保持选择状态。

为了在整个Activity生命周期事件中保持选择状态,你的应用程序必须在Activity的onSaveInstanceState()onRestoreInstanceState()方法调用 selection tracker的onSaveInstanceState()onRestoreInstanceState()方法。你的应用程序还必须为SelectionTracker.Builder构造函数提供唯一的选择标识。此ID是必需的,因为Activity或Fragment可能有多个不同的可选列表,所有这些列表都需要保存在其saved state中。

你可能感兴趣的:(Android)