Recycler View

原文地址  : https://github.com/writtmeyer/recyclerviewdemo

为什么叫RecyclerView?

谷歌在Android L预览版API文档中是这样描述的: 
一个非常灵活的用于在有限的窗口范围内显示大量数据的控件。 
所以RecyclerView适用于那些有大量同类的View但是不能同时在屏幕中显示的情况,比如联系人、用户列表、音乐文件列表等等。想看到更多信息需要滚动试图,同时对离开屏幕的视图进行回收和重用。

以下图片解释了这个过程:左边是应用程序的初始状态,当向上滚动视图的时候,一些子View准备被回收,比如右边的红框部分中的两个不可见的View。现在回收器可以把他们放入准备重用的View的列表以便在适当的时候重用。

这里写图片描述

对View的回收重用是非常有用的,由于不用每次都重新填充布局,这样可以节约CPU资源;同时由于不用保存大量的不可见的视图,也有效的节省了内存资源。

看到这里可能有人要说,这个概念早就有了,ListView就是使用这样的机制。是的没错,但是在ListView中,控件外观、回收过程和其他操作是紧耦合的,缺乏灵活性,这就是为什么谷歌要开发RecyclerView来取代它的意义所在。

RecyclerView本身不关心视图相关的问题

由于ListView的紧耦合问题,谷歌的改进就是RecyclerView本身不参与任何视图相关的问题。它不关心如何将子View放在合适的位置,也不关心如何分割这些子View,更不关心每个子View各自的外观。进一步来说,RecyclerView只负责回收和重用的工作,这就是它名字的由来。

所有关于布局、绘制和其他相关的问题,也就是跟数据展示相关的所有问题,都被委派给了一些”插件化”的类来处理。这使得RecyclerView的API变得非常灵活。你需要一个新的布局么?接入另一个LayoutManager就可以了!你想要不同的动画么?接入一个新的ItemAnimator就可以了,诸如此类。

以下是RecyclerView中用于数据展示的一些重要的类,他们都是RecyclerView的内部类:

  • Adapter:包装数据集合并且为每个条目创建视图。
  • ViewHolder:保存用于显示每个数据条目的子View。
  • LayoutManager:将每个条目的视图放置于适当的位置。
  • ItemDecoration:在每个条目的视图的周围或上面绘制一些装饰视图。
  • ItemAnimator:在条目被添加、移除或者重排序时添加动画效果。

下面将对这些类进行分别介绍:

RecyclerView.ViewHolder

ViewHolder的基本作用是缓存视图对象,Android官方很早钱就推荐使用ViewHolder的编程模式,但是现在使用这种模式将是强制性的。

RecyclerView.ViewHolder的子类都可以通过ViewHolder的public的成员变量itemView来访问每个条目的根视图,所以ViewHolder子类中不需要在保存这个视图。

以下是ViewHolder的示例代码:

<code class="hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ListItemViewHolder</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RecyclerView</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewHolder</span> {</span> TextView label; TextView dateTime; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">ListItemViewHolder</span>(View itemView) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(itemView); label = (TextView)itemView.findViewById(R.id.txt_label_item); dateTime = (TextView)itemView.findViewById(R.id.txt_date_time); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

RecyclerView.Adapter

Adapter充当两个角色:访问数据集合以及负责为每个条目创建正确的视图。Adapter在Android中经常出现,比如ListView、AutoCompleteTextView、Spinner等,他们都继承自AdapterView,但是RecyclerView并没有这样做。

对于RecyclerView,谷歌决定使用新的RecyclerView.Adapter基类来取代旧的Adapter接口。所以,SimpleCursorAdapter、ArrayAdapter都将成为历史,或者至少不会是他们现在的这种使用方式。

目前RecyclerView.Adapter还没有默认实现,以后可能会添加。由于RecyclerView.Adapter是一个抽象类,所以必须要实现以下三个方法:

  • public VH onCreateViewHolder(ViewGroup parent, int viewType)
  • public void onBindViewHolder(VH holder, int position)
  • public int getItemCount()

其中VH是泛型类,当继承RecyclerView.Adapter时需要使用具体的类来替换。Adapter的示例代码如下:

<code class="hljs axapta has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RecyclerViewDemoAdapter</span> <span class="hljs-inheritance" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span></span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RecyclerView</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Adapter</span> <<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RecyclerViewDemoAdapter</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ListItemViewHolder</span>> {</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> List<DemoModel> items; RecyclerViewDemoAdapter(List<DemoModel> modelData) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (modelData == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IllegalArgumentException( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"modelData must not be null"</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.items = modelData; } @Override <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> ListItemViewHolder onCreateViewHolder(ViewGroup viewGroup, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> viewType) { View itemView = LayoutInflater. from(viewGroup.getContext()). inflate(R.layout.item_demo_01, viewGroup, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ListItemViewHolder(itemView); } @Override <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> onBindViewHolder(ListItemViewHolder viewHolder, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position) { DemoModel model = items.get(position); viewHolder.label.setText(model.label); String dateStr = DateUtils.formatDateTime( viewHolder.label.getContext(), model.dateTime.getTime(), DateUtils.FORMAT_ABBREV_ALL); viewHolder.dateTime.setText(dateStr); } @Override <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> getItemCount() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> items.size(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ListItemViewHolder</span> <span class="hljs-inheritance" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span></span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">RecyclerView</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ViewHolder</span> {</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// ... shown above in the ViewHolder section</span> } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul>

RecyclerView.LayoutManager

LayoutManager可能是RecyclerView中最有趣的部分了,该类负责放置所有的子View到适当位置。该类有一个默认的实现:LinearLayoutManager,当要实现水平或垂直列表时可以使用该类。

必须要给RecyclerView设置一个LayoutManager,否则会抛出异常:

<code class="hljs oxygene has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">08</span>-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">01</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">05</span>:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">00</span>:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">00.000</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2453</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2453</span> E AndroidRuntime: java.lang.NullPointerException: Attempt <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> invoke <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">virtual</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">method</span> '<span class="hljs-title" style="box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">android</span>.<span class="hljs-title" style="box-sizing: border-box;">support</span>.<span class="hljs-title" style="box-sizing: border-box;">v7</span>.<span class="hljs-title" style="box-sizing: border-box;">widget</span>.<span class="hljs-title" style="box-sizing: border-box;">RecyclerView</span>$<span class="hljs-title" style="box-sizing: border-box;">LayoutManager</span>.<span class="hljs-title" style="box-sizing: border-box;">onMeasure</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(android.support.v7.widget.RecyclerView$Recycler, android.support.v7.widget.RecyclerView$State, int, int)</span>' <span class="hljs-title" style="box-sizing: border-box;">on</span> <span class="hljs-title" style="box-sizing: border-box;">a</span> <span class="hljs-title" style="box-sizing: border-box;">null</span> <span class="hljs-title" style="box-sizing: border-box;">object</span> <span class="hljs-title" style="box-sizing: border-box;">reference</span> 08-01 05:</span><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">00</span>:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">00.000</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2453</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2453</span> E AndroidRuntime: at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1310</span>)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

目前LayoutManager只有一个抽象方法:

  • public LayoutParams generateDefaultLayoutParams()

    但是还有一个方法需要重写,因为这个方法很快就要变成抽象的了:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">scrollToPosition</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (DEBUG) { Log.e(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"You MUST implement scrollToPosition. It will soon become abstract"</span>); } } </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

但是只重写以上两个方法还远远不够,毕竟LayoutManager的责任是放置所有子View到适当的位置上,所以还需要重写onLayoutChildren()方法:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onLayoutChildren</span>(Recycler recycler, State state) { Log.e(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"You must override onLayoutChildren(Recycler recycler, State state) "</span>); } </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

LinearlayoutManager

LinearLayoutManager是目前LayoutManager唯一的默认实现,可以用来创建水平或者垂直列表。

LinearLayout非常复杂,这里只简单谈谈其中一些关键部分。LinearLayoutManager的用法是非常简单的:初始化它,然后设置一个方向(水平/垂直)就可以了:

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">LinearLayoutManager layoutManager = new LinearLayoutManager(context)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> layoutManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setOrientation</span>(LinearLayoutManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.VERTICAL</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> layoutManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.scrollToPosition</span>(currPos)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span> recyclerView<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.setLayoutManager</span>(layoutManager)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

LinearLayoutManager还提供了一些方法用来查找当前屏幕上的第一个和最后一个条目:

  • findFirstVisibleItemPosition()
  • findFirstCompletelyVisibleItemPosition()
  • findLastVisibleItemPosition()
  • findLastCompletelyVisibleItemPosition()

RecyclerView.ItemDecoration

使用ItemDecoration可以给每个条目添加偏移量、可以给条目间添加间距或者高亮条目,添加其他修饰效果等等。
ItemDecoration并不是必须使用的,例如可以使用CardView作为每个条目的视图。

另外,可以添加任意数量的ItemDecoration,RecyclerView会遍历所有的ItemDecoration然后按适当顺序调用每一个的绘制方法

ItemDecoration虚基类包含以下三个方法:

  • public void onDraw(Canvas c, RecyclerView parent)
  • public void onDrawOver(Canvas c, RecyclerView parent)
  • public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)

onDraw中绘制的内容可能会被条目内容所覆盖,如果要在条目上绘制内容需要使用onDrawOver()方法。如果需要条目间距来绘制分割线,那么使用以上两个方法的差别不大,但是如果真的要添加装饰效果,还是需要使用onDrawOver()。

LayoutManager在测量阶段会调用getItemOffset()方法来正确计算每个条目视图的大小。outRect参数可能第一眼看来有些奇怪,为什么不使用一个返回值呢?但是这样的参数设置确实是有意义的,它使得对于所有的子视图RecyclerView可以重用同一个Rect对象,节省了资源开销。这并不是必须的,但确实是很有效率的。

RecyclerView.ItemAnimator

ItemAnimator可以为每个条目添加动画效果,它主要处理以下三种事件:

  • An item gets added to the data set
  • An item gets removed from the data set
  • An item moves as a result of one or more of the previous two operations

ItemAnimator有一个默认实现:DefaultItemAnimator,如果没有设置自定义的ItemAnimator,RecyclerView就会使用默认的DefaultItemAnimator。

显然如果要让动画生效,需要知道数据集合的变化,这需要Adapter的帮助。在之前的版本中,当变化发生改变时,需要调用notifyDataSetChanged()方法。但这是不合适的,因为这个方法会触发对于所有可视子View的重绘,而且没有动画效果。要看到动画效果需要使用更加特殊的方法:

RecyclerView.Adapter类包含很多notifyXXX()类型的方法,两个最常用的是:

  • public final void notifyItemInserted(int position)
  • public final void notifyItemRemoved(int position)

监听器

RecyclerVIew提供了一些非常通用的监听器。之前常见的那些使用方式现在都不适用了,RecyclerView没有提供OnItemClickListener或者OnItemLongClickListener。但是可以使用RecyclerView.OnItemTouchListener结合手势监听来处理这些事件。需要的编码工作必之前达到同样的效果要多。

把以上所有类结合起来

布局文件:

<code class="language-xml hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">RelativeLayout</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">xmlns:android</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">xmlns:tools</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"http://schemas.android.com/tools"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:paddingLeft</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@dimen/activity_horizontal_margin"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:paddingRight</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@dimen/activity_horizontal_margin"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:paddingTop</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@dimen/activity_vertical_margin"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:paddingBottom</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@dimen/activity_vertical_margin"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">tools:context</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">".RecyclerViewDemoActivity"</span>></span> <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">android.support.v7.widget.RecyclerView </span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/recyclerView"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"match_parent"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">tools:context</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">".MainActivity"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">tools:listitem</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@layout/item_demo_01"</span>/></span> <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">ImageButton </span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@+id/fab_add"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_alignParentRight</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"true"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_alignParentBottom</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"true"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@dimen/fab_size"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@dimen/fab_size"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_gravity</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"bottom|right"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_marginRight</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"16dp"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:layout_marginBottom</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"16dp"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:background</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@drawable/ripple"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:stateListAnimator</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@anim/anim"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:src</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"@drawable/ic_action_add"</span> <span class="hljs-attribute" style="box-sizing: border-box; color: rgb(102, 0, 102);">android:elevation</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(0, 136, 0);">"1dp"</span>/></span> <span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 0, 136);">RelativeLayout</span>></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul>

RecyclerView的AttributeSet会在generateLayoutParams()方法中使用:

<code class="hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> ViewGroup.LayoutParams <span class="hljs-title" style="box-sizing: border-box;">generateLayoutParams</span>(AttributeSet attrs) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mLayout == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IllegalStateException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"RecyclerView has no LayoutManager"</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mLayout.generateLayoutParams(getContext(), attrs); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

程序代码部分很简单:

<code class="language-Android hljs vhdl has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">setContentView(R.layout.activity_recyclerview_demo); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); LinearLayoutManager layoutManager = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.scrollToPosition(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); recyclerView.setLayoutManager(layoutManager); // allows <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> optimizations <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">all</span> item views are <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> the same size: recyclerView.setHasFixedSize(true); // <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">For</span> the sake <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> simplicity I misused the Application subclass as a DAO List<DemoModel> items = RecyclerViewDemoApp.getDemoData(); adapter = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RecyclerViewDemoAdapter(items); recyclerView.setAdapter(adapter); RecyclerView.ItemDecoration itemDecoration = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST); recyclerView.addItemDecoration(itemDecoration); // this <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> the <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">default</span>; // this call <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> actually only necessary <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">with</span> custom ItemAnimators recyclerView.setItemAnimator(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DefaultItemAnimator()); // onClickDetection <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">is</span> done <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> this Activity<span class="hljs-attribute" style="box-sizing: border-box;">'s</span> OnItemTouchListener // <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">with</span> the help <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">of</span> a GestureDetector; // Tip by Ian Lake <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">on</span> G+ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> a comment <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">to</span> this post: // https://plus.google.com/+LucasRocha/posts/<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">37</span>U8GWtYxDE recyclerView.addOnItemTouchListener(this); gesturedetector = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> GestureDetectorCompat(this, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RecyclerViewDemoOnGestureListener());</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>

主要步骤如下:

  1. 获取RecyclerView的引用
  2. 创建一个LayoutManager并添加
  3. 创建一个Adapter并添加
  4. 如果需要的话,创建1个或多个ItemDecoration并添加
  5. 如果需要的话,创建ItemAnimator并添加
  6. 如果需要的话,创建1个或多个监听器并添加

当然这只是初步的代码,真正有趣的部分在于RecyclerView有很多内部类,可以用来继承并且实现各种自定义的效果,这才是真正需要投入大量工作的地方。

以下是一个使用RecyclerView的简单示例: 1. 首先,在你的项目中添加RecyclerView依赖项。在build.gradle文件中添加以下依赖项: ``` implementation 'androidx.recyclerview:recyclerview:1.0.0' ``` 2. 在你的布局文件中添加RecyclerView: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/my_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 3. 创建一个自定义的ViewHolder类,用于显示RecyclerView中的每个项: ``` public class MyViewHolder extends RecyclerView.ViewHolder { public TextView textView; public MyViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.text_view); } } ``` 4. 创建一个RecyclerView.Adapter类,用于管理RecyclerView中的数据项: ``` public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> { private List<String> mData; public MyAdapter(List<String> data) { mData = data; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_item_layout, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { String item = mData.get(position); holder.textView.setText(item); } @Override public int getItemCount() { return mData.size(); } } ``` 5. 在你的Activity中获取RecyclerView实例,并设置LayoutManager和Adapter: ``` RecyclerView recyclerView = findViewById(R.id.my_recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); MyAdapter adapter = new MyAdapter(myData); recyclerView.setAdapter(adapter); ``` 其中,myData是一个包含数据项的List。 6. 运行你的应用程序,你应该能够看到一个包含数据项的RecyclerView。 这就是一个简单的RecyclerView示例。你可以根据自己的需要对其进行自定义和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值