[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]

不要脸啦!咱们的官方群放在前面:(/ω\)
我建的这个群,一个很积极向上的群。主要是用来交流Android开发技术的,里面还有一个小分群,每天早晨6点,晨读练口语。也有一些小伙伴,自发组织背单词之类。
重点:里面小姐姐超多哇!(o゚▽゚)o 想加入进来的老铁姐妹们,来吧。

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第1张图片

==================================================================

学安卓的小伙伴们,今天咱们通过一个列子,来学学Android RecyclerView的用法。RecyclerView毋庸置疑可以说是Android系统中,到目前为止最强大的控件。它可以加载任何布局,而且加载数量没有限制。做安卓开发,这块是你必须要会的。

咱们这个教程面向的读者定位是刚接触Android开发不久的新人,所以写的比较详细。Android开发大佬看这个教程,可能会有点啰嗦,尽量憋喷。
基础好的大佬可以直接去GitHub撸我的源码:
https://github.com/SteveSuper/Android-RecyclerView-Demo

下图就是我们今天要实现的列子,左图就是RecyclerView实现的布局,点击其中的每个条目可以启动对应的详情页面(右图)。 (•̀ω•́ 」∠)

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第2张图片

跟着我做完整个列子,你将会增加以下技能:

1> 如何使用RecyclerView,这是最基本的。
2> 如何在RecyclerView中加载多布局,咱们这个例子就加载了两种布局。
3> 如何实现RecyclerView的点击事件,启动新的Acitivity。
4> 如何使用目前最流行的Glide,从网络加载图片。
5> 如何自定义Toolbar(或者叫ActionBar),让App更好看些。
6> 等等吧,还有好多其他小技能,不列举了。

在开始之前,我得先硬塞给你一张图,先给你简单科普下RecyclerView的实现原理,让你心里有点数。我知道你可能还没接触过RecyclerView这东西,读下面这一段可能会犯晕。没事,先尽力尝试理解下就好,也不用刻意记忆。下面这段话,等你长大成人了,慢慢的你就懂了....... ←_←

ღ⊙□⊙╱ 下面这里敲黑板了啊!要划重点了啊!

  1. RecyclerView在屏幕上显示的各个条目,加载的是同一个,或者几个xml布局文件,只不过是往里面塞了不同的数据而已。
  2. RecyclerView每个条目中的各个控件(ImageView,TextView,Button......),是由一个叫ViewHolder的家伙把控的。这个ViewHolder需要咱们自己写,并且它需要继承自RecyclerView.ViewHolder。
  3. RecyclerView每个条目中的数据,是由一个叫Adapter的家伙适配的,并且它需要上面的ViewHolder的配合。这个Adapter也是需要咱们自己实现的类,并且它需要继承自RecyclerView.Adapter。
  4. 有一个叫LayoutManager的家伙负责RecyclerView条目的显示方式,比如垂直显示,水平显示,还是卡片式显示等。
[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第3张图片

看完上面那一段话,我估计很多萌萌小白们,已经开始犯晕了,没事,犯晕是对学习RecyclerView最起码的尊重,挺好的...... (●´ϖ`●)

==【第一部分】===================================================

好,接下来咱们需要着手实现咱们的列子了。但在咱们实现上面那个相对比较复杂的列子之前,咱们先通过用最少,最简单的代码,先把最简单的RecyclerView运行起来。然后咱们一步一步的,增添代码,实现咱们最终的列子。好吧?
首先咱们要实现的Demo是这样子的:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第4张图片

交代一下我的开发环境:我写这篇教程的时候,我用的Android Studio版本是3.2,本地的JDK是1.8。

这部分的任务清单(To-Do List)
1> 新建Project
2> 添加RecyclerView库依赖
3> 修改下App主题颜色
4> 在activity_main.xml中添加RecyclerView组件
5> 创建RecyclerView条目的布局文件
6> 实现RecyclerView需要的ViewHolder和Adapter两个辅助类
7> 在MainActivity.java中启动RecyclerView

那咱们就按照上面的任务清单走,清晰明朗。

(1)首先建个新Project,咱们App的名称就叫:Meizi。剩下的,按照你平常的设置,一带一路默认就好,进入Project。

(2)添加RecyclerView库依赖:咱们今天要学的RecyclerView这个组件,存在于Android的一个支持库中:'com.android.support:recyclerview-v7:27.1.0',需要添加这个库的依赖。不过现在添加这个库很简单,在activity_main.xml页面视图中,直接点击图中的下载按钮就好。或者直接在build.gradle(Mocule:app)中,添加下面的库依赖是一样的。

implementation 'com.android.support:recyclerview-v7:27.1.1'
[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第5张图片

(3)修改下App主题颜色:接下来咱们先修改一下rec/values/colors.xml中的颜色值,并添加几个颜色(后面用),对应的代码如下:

rec/values/colors.xml



    #FF1744
    #CC1236
    #FF1744
    #000000
    #FFFFFF
    #DDDDDD

这里我把App标题栏和Android通知栏的颜色都设置成了红色,遵循的Material Design设计风格。

(4)在activity_main.xml中添加RecyclerView组件:接下来把activity_main.xml中的“Hello World”删掉,并添加一个RecyclerView组件。相关设置如图,对于代码如下。


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第6张图片

activity_main.xml




    


(5)创建RecyclerView条目的布局文件:在创建条目布局文件之前,咱们先需要把下面这张妹子图片meizi.jpg素材放到res/drawbale目录中。


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第7张图片

然后在res/layout目录下新建一个Layout resource file,起名为meizi_item.xml,其他都默认就好。这个meizi_item.xml就是咱们的RecyclerView中的每个条目的布局文件。上面两步操作完后,显示如下图:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第8张图片

咱们的这个条目布局文件很简单:父容器用ConstraintLayout ,里面就放一张照片。设置属性如下图,代码在下面。
嘱咐一句:父容器ConstraintLayout的layout_height,要设置成wrap_content
至于为什么用ConstraintLayout ,小伙伴就自己搜搜它的优点以及高级用法吧,这货也是Android官方主推的组件。

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第9张图片




    


好,基本的布局啥的,就这么多,接下来要进入重头戏了。

(6)实现RecyclerView需要的ViewHolder和Adapter两个辅助类。
下面的要注意啦啊!这里我需要发布下小高能预警!"(ºДº*) 有皇家晕码血统的小可爱们请先撤离下现场......(ノ°ο°)ノ 因为接下来咱们要实现RecyclerView的核心了。这里我有必要再贴出来RecyclerView的实现原理,铺垫一下:

  1. RecyclerView在屏幕上显示的各个条目,加载的是同一个,或者几个xml布局文件,只不过是往里面塞了不同的数据而已。
  2. RecyclerView每个条目中的各个控件(ImagaeView,TextView,Button......),是由一个叫ViewHolder的家伙把控的。这个ViewHolder需要咱们自己写,并且它需要继承自RecyclerView.ViewHolder。
  3. RecyclerView每个条目中的数据,是由一个叫Adapter的家伙适配的,并且它需要上面的ViewHolder的配合。这个Adapter也是需要咱们自己实现的类,并且它需要继承自RecyclerView.Adapter。
  4. 有一个叫LayoutManager的家伙负责RecyclerView条目的显示方式,比如垂直显示,水平显示,还是卡片式显示等。

好,按照上面的步骤,第一项的那个xml布局文件已经完成了,接下来需要实现传说中的ViewHolder和Adapter了。
接下来憋说话,先看我操作,伴随头晕目眩属正常生理现象。///•A•///

在java/com.nidebao.meizi包下,新建一个名为MeiziAdapter.java类,这个就是咱们的Adapter,让这个类继承RecyclerView.Adapter,并且需要在RecyleView.Adpter后面加上泛型。我知道这个时候Android Studio已经出现了姨妈红,先憋管,先别按Alt+Enter修复。
紧接着,在class MeiziAdapter类里面创建内部类ViewHolder,让这个ViewHolder继承RecyclerView.ViewHolder。上面这两波操作完后应该显示如下:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第10张图片

这两行红代码我偷偷解释下←_←:咱们的ViewHolder继承RecyclerView.ViewHolder,在RecyclerView的实现原理中也提到了,这是必须的,ViewHolder把控着RecyclerView里面控件。这里咱们把ViewHolder写成了MeiziAdapter的内部类。咱们的MeiziAdapter也必须继承RecyclerView.Adapter,它的功能就是为RecyclerView适配数据。并且需要在RecyclerView.Adapter的后面加上ViewHolder泛型。为什么这样写?Andriod组件设计人员就是这样的设计的,你点开一下那个RecyclerView.Adapter源码,你看它的类的定义就是下面这样子。

public abstract static class Adapter {
    ......

抛开这两行代码的内容,如果连这两句代码的逻辑都看不懂的小伙贼,你课下要偷偷补补Java基础了啊。ღ⊙□⊙╱
好,接下来咱们处理下姨妈红。先在内部类ViewHolder上按Alt+Enter键,选择"Create constructor matching super",建立匹配父类构造器。
然后在MeiziAdapter类上按Alt+Enter建,选择"Implement methods",实现那三个抽象方法。
操作完后,对比下图:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第11张图片

MeiziAdapter.java

package com.cool.meizi;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

public class MeiziAdapter extends RecyclerView.Adapter {

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }


    class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);
        }
    }
}

估计这一波小操作,晕倒了不少骚年...... 可以的。ಥ_ಥ
咱的Adapter和ViewHolder两个类已经定义好了,下面修改onCreateViewHolder(......)方法,在里面增添3行代码如下:

@NonNull @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View itemView = inflater.inflate(R.layout.meizi_item, parent, false);
    return new ViewHolder(itemView);
}

听我解释:首先这个onCreateViewHolder(...)方法就是用来创建ViewHolder的。咱们的代码,分三步实现这一目的:

  1. 通过LayoutInflater.from(......)方法拿到inflater实例,如果翻译的话,我想把它翻译为”控件吹鼓器“比较合适,inflate的意思就是吹鼓,吹气球的意思。它的作用就是解析指定的xml,并把它像气球一样“吹起来”,用于显示在屏幕上。
  2. 接下来通过inflater.inflat(......)方法,把咱们之前定义的meizi_item.xml条目布局文件”吹鼓“为View实例。
  3. 把上面拿到的meizi_item.xmlView实例交给ViewHolder的构造方法,创建ViewHolder对象并返回。

好,然后把下面的getItemCount()方法中的return 0,改为return 100。这个从方法名字上也能看出,这个货肯定是决定RecyclerView中条目数量的,没错。

 @Override
    public int getItemCount() {
        return 100;
    }

到此咱的ViewHolder和Adapter就暂时写好了。

(7)最后一步,在MainActivity.java中启动RecyclerView:切换到MainActivity.java,在其onCreate方法中,增加下面5行代码。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**增加下面五行代码 ****************************************/
        MeiziAdapter adapter = new MeiziAdapter();
        LayoutManager layoutManager = new LinearLayoutManager(this);
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(layoutManager);

    }
}

听我解释:ღ⊙□⊙╱
当MainActivity启动时,首先咱们创建了一个咱们自己的MeiziAdapter实例,等待传给RecyclerView。然后new了一个LayoutManager实例,上面也提到了,这个LayoutManager的作用就是决定RecyclerView中条目的显示方式(垂直,水平,卡片式......) 这里的LineraLayoutManager,就是垂直显示条目。

如果你想水平显示条目,可以这样写:

LayoutManager layoutManager = new LinearLayoutManager(
    this, LinearLayoutManager.HORIZONTAL, false
);

如果你想卡片式显示条目,可以这样写(第一参数表示列数):

LayoutManager layoutManager = new StaggeredGridLayoutManager(
    2, StaggeredGridLayoutManager.VERTICAL
);

小伙伴可以自己试试并看看效果,这里暂时不做过多扩展。

接下来通过findViewById(......),拿到RecyclerView,之后通过它的set...方法分别为其设置了AdpterLayoutManager
对了,有的小伙贼可能用的比较老的Android Studio,用findViewById(...)时,前面需要加上转型。

好啦,好啦!咱们的一小小部分就完成了!这就是能运行起来RecyclerView最少的代码!赶紧的,打开你的模拟器,运行一下!如果没有什么问题,你是按照我说的一步一步写过来的,你的安卓模拟器上显示的应该是这个样子:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第12张图片

如果你看到了这样的界面,我觉得这个时候咱有必要小兴奋一下,奖励给自己一根烟...... 不抽烟的可以叼根筷子意思一下,仪式感很重要...... 233333(ಡωಡ)

==【第二部分】===================================================

接下来,咱们修改下条目布局文件meizi_item.xml,让咱们的RecyclerView更高端大佬一些。咱们即将实现的样式如下:

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第13张图片

这部分的任务清单(To-Do List)
1> 为Project添加几个Icon图标素材
2> 重新布局meizi_item.xml

这部分很简单,就是添加了几个Icon,修改meizi_item.xml,把上面列子的图片替换为了一个更复杂点的布局而已,没动其他任何Java代码,来一起实现这两步。
首先咱们需要为咱们的项目添加几个icon,如图中的小人icon,爱心icon,评论icon,看我操作。

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第14张图片

在res/drawable目录上,右击,依次选择New > Vector Asset,然后双击666!.................. 额,你若真双击了,麻烦退回去,单击就好,不好意思,节目效果,必须有的...... 说下你可能知道的快捷操作:点击下drawable目录,按Alt+Insert,然后选择Vector Asset,可以达到同样的疗效。

下面就来到下面这个视图:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第15张图片

然后点击上面的小Android图标,进入下面的界面:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第16张图片

这里面所有的Icon都是Google粑粑提供的官方Material Icons,左上角的搜索框可以搜索,选择你喜欢的就好。重复这样的操作,咱们需要3个icon,最后你的icon应该出现在左侧的drawable目录里,如下图:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第17张图片

好啦,准备工作就这么多!下面开始重新编写meizi_item.xml文件。我这里实在不能一步一步的给你演示ConstraintLayout怎么布局各个小控件了啊,要不然这教程就太臃肿了。如果有不会Android ConstraintLayout(约束性布局),自己费点心,先补补这块儿,很简单的。下面我把设计好后的视图,以及代码留下。视图中,各个控件的关系,以及距离都能看清楚,自己比葫芦画瓢按照我的做就好。

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第18张图片

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第19张图片

代码清单:meizi_item.xml




    

    

    

    

    

    

    

    


好啦,这一块儿就这么多,大家应该能搞定吧!搞定后,赶紧再运行一下,模拟器上的显示视图应该是这个样子:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第20张图片

好,如果你实现到了这一步,赶紧给自己点小奖励,再来跟事后烟?(ಡωಡ) ......


接下来,来点小插曲:
你有没有发现,咱们的例子每个条目之间的空隙有点大?感觉有点丑,能不能在条目之间加上一个分割线?
要实现这个,有很多方式:你可以在条目布局中直接插入一个分割线,不过这种方式以后的扩展性不好,这里使用一种更高级的方式。
RecyclerView有一个ItemDecoration类,“decoration”是修饰,装饰的意思。这个ItemDecoration就是专门用来设置RecyclerView条目之间空隙,分割线的类。实现也很简单,看我操作:
首先新建一个MeiziItemDecoration类,让他继承RecyclerView.ItemDecoration。所有的代码如下:

package com.cool.meizi;

import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class MeiziItemDecoration extends RecyclerView.ItemDecoration {

    private final int verticalSpaceHeight;

    public MeiziItemDecoration(int verticalSpaceHeight) {
        this.verticalSpaceHeight = verticalSpaceHeight;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.bottom = verticalSpaceHeight;
    }
}

verticalSpaceHeight是咱们自定义的一个成员变量,咱用它来把控条目之间的空隙大小。上面咱们重写了getItemOffsets(...)方法,outRect.bottom = verticalSpaceHeight;就是设置条目底部间距的大小。
如果你想设置左右空隙,和上部空隙,增加outRect.top, outRect.leftoutRect.right这几个属性就好。
如果你不想给最后一个条目加上间距,在getItemOffsets(...)方法内,加上下面这个判断:

if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
    outRect.bottom = verticalSpaceHeight;
}

好,然后回到activity_main.xml文件,给RecyclerView加上一个灰色背景色:

android:background="@color/grayLite"

最后在MainActivity.java中,添加一行代码,把咱们的修饰类添加给RecyclerView:

recyclerView.addItemDecoration(new MeiziItemDecoration(2));
[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第21张图片

我指定的大小为2,你可以根据你的喜好修改。编译运行,看看条目之间是不是有分割线效果了?


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第22张图片

==【第三部分】===================================================

好,接下来咱们进入本教程的核心中的核心了!实现用Java代码的方式,让RecyclerView动态加载数据,这块还涉及到用Glide从网络加载图片。下面是重点中的重点,代码量也有点大,晕码的哥们,你想晕就晕吧,窝不管了..... ( •̀⊿•́)ง120,我希望你能顶得住!好,开始啦!

接下来要实现的样式是介样的...... 注意哦,好消息是,咱不需要导入图片素材,咱们来从网络加载图片! (•̀ᴗ•́)و ̑̑


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第23张图片

这部分的任务清单(To-Do List)
1> 创建提供数据的Model,为RecyclerView提供数据用。
2> 修改ViewHolder内部类,拿到条目中的控件。
3> 如何使用Glide加载图片。
4> 修改MeiziAdapter类,让其给RecyclerView条目中的控件适配数据。

好,既然动态加载数据,肯定要有一个能提供数据的Model(数据模型),跟着我操作,在目录java/com.nidebao.meizi包下,先建一个Meizi.java类,代码如下,:

package com.cool.meizi;

public class Meizi {

   private String imageUrl;
   private String title;
   private String name;
   private int favorites;
   private int comments;

   public Meizi(String imageUrl, String title, String name, int favorites, int comments) {
       this.imageUrl = imageUrl;
       this.title = title;
       this.name = name;
       this.favorites = favorites;
       this.comments = comments;
   }

   public String getImageUrl() { return imageUrl; }
   public String getTitle() { return title; }
   public String getName() { return name; }
   public int getFavorites() { return favorites; }
   public int getComments() { return comments; }
}

咱的Meizi就5个变量:imageUrltitlenamefavoritescomments,不至晕吧?然后创建了构造方法,和Getters。
好,妹子模型有了,下面咱再建一个“妹子工厂”,MeiziFactory.java,专门来生产大量的Meizi。(´இ皿இ`) 代码如下:

package com.cool.meizi;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MeiziFactory {

    private static String[] imageUrls = {
        "https://upload-images.jianshu.io/upload_images/11181600-db48df38b76ed467.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-f9cc67a96b58d49e.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-4860ecb6acb8fce5.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-a5db9d1018924867.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-d2131192b602b425.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-e3002c837b5335f9.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-2ddfaaa766a399e5.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-d58b814b598ea3d3.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-8e2e151505cf6899.jpg",
        "https://upload-images.jianshu.io/upload_images/11181600-a535283f3402accb.jpg"
    };

    private static String[] titles = {
        "I'm the most beautiful girl in the world! Do you agree?",
        "I'm Isabella. Long time no see, do you miss me?",
        "Sophia here! I need more boyfriends! More money!",
        "Did you watch my new movie? You can't miss it.",
        "Hi there! I miss you so much, call me PLEASE!",
        "Here's Hannah from Japan. Do you wanna be my guy?",
        "Hi! My name is Emma, I have a lot of boyfriends!",
        "Hey man! I just wanna tell you, I love you! :)",
        "Please be my boyfriend! I need you sooooo much!",
        "I'm Sofia, I'm just a very shy girl. Do you like me?"
    };

    private static String[] names = {
        "Olivia", "Isabella", "Sophia", "Elizabeth",
        "Charlotte", "Hannah", "Emma", "Emily", "Harper", "Sofia"
    };

    public static List createMeizis(int num) {
        Random rand = new Random();
        List meizis = new ArrayList<>();
        int arySize = imageUrls.length;
        for (int i = 0; i < num; i++) {
            int a = i % arySize;
            String url = imageUrls[a];
            String title = titles[a];
            String name = names[a];
            int favorites = rand.nextInt(10000);
            int comments = rand.nextInt(10000);
            meizis.add(new Meizi(url, title, name, favorites, comments));
        }
        return meizis;
    }
}

代码有一点小小多,但不难理解。里面我放了3个Array数组:
imageUrls里存放的妹子图片网络地址,等会咱们加载用。这些地址都是我自己把图片上传后得到的,你打开每个地址就是一张图片。所有的图片就在这里
https://www.jianshu.com/p/9ae7d796213a
titles是我随便写的,与妹子的数量对应。
names是10个妹子的名字。

public static List createMeizis(int num) { 
    ... 
}

这个静态方法就是用来生产Meizi的,基础差的小伙伴好好撸撸,不难的,我不多解释了。

OK,Meizi数据模型建好了,那么怎么动态的向RecyclerView中的条目中添加Meizi数据呢?想向条目中添加数据,你首先得能获取到条目中的各个控件吧(TextView, ImageView, Button等)?怎么获取?在哪里获取?难道还是像以前一样,在Activity中,通过findViewById(......)的方式获取?...... 小伙贼,你的想法很好,但不是这样的...... (<_<)

如果你记性好,咱们上面提到了,RecyclerView中的条目,是通过ViewHolder控制的,由他来提供访问权限。
好,小乖乖们下面看我在哪里写,以及怎么写。

修改ViewHolder内部类,让其持有RecyclerView条目中的各个控件: 回到咱们的MeiziAdapter.java文件,在咱们的内部类ViewHolder中,添加如下代码。

ViewHolder.class

class ViewHolder extends RecyclerView.ViewHolder {
    ConstraintLayout meiziItem; //这里也拿到了条目的Layout,后面添加onClick()方法用;
    ImageView image;
    TextView title;
    TextView name;
    TextView favorites;
    TextView comments;
    public ViewHolder(View itemView) {
        super(itemView);
        meiziItem = itemView.findViewById(R.id.meizi_item);
        image = itemView.findViewById(R.id.meizi_item_image);
        title = itemView.findViewById(R.id.meizi_item_title);
        name = itemView.findViewById(R.id.meizi_item_name);
        favorites = itemView.findViewById(R.id.meizi_item_favorites);
        comments = itemView.findViewById(R.id.meizi_item_comments);
    }
}

听我解释:这些代码应该不难理解,就是在ViewHolder类中,添加了咱们需要访问的ImageView,TextView控件的成员变量。然后紧接着在ViewHolder的构造器中,通过findViewById(......)的方式,初始化,或者说获取到那些控件的对象。只不过这里的findViewById(......)和Activity中的不一样,前面需要有view.findViewById(......),其实Activity中的那个findViewById(......)只是这个的语法糖而已,性质上是一样的。
既然拿到了一个个控件的对象,咱们接来下就可以通过viewHolder.xxx的语法操控这些控件了。


接下来就要用到大名鼎鼎的Glide了,需要让其给Adaper加载网络图片,用这货其实很简单,几步操作搞定:
首先给咱们的App添加网络访问权限,在AndroidManifest.xml中,添加下面的权限:


别把权限放错地方了亲爱的,所以给你截了个图。


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第24张图片

接着在build.gradle(Mocule:app)中,添加Glide库依赖。

implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第25张图片

咱们这个教程没有涉及Glide的高级用法,可以不添加annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'这行依赖,只添加implementation 'com.github.bumptech.glide:glide:4.7.1'这一行依赖也行,编译更快。具体的请参考Glide官方文档,有中文。
https://github.com/bumptech/glide

好啦,下面回到MeiziAdapter.java,接下来修改MeiziAdapter类,让其给RecyclerView条目中的控件适配数据: 编写MeiziAdapter中的onBindViewHolder(...)方法,这个方法从名字上就能看出,它是绑定数据用的。你看这个方法需要的参数:(ViewHolder holder, int positon),里面的holder肯定是上面的public ViewHolder onCreateViewHolder(...)方法返回的holderposition肯定是RecyclerView条目的位置。

给其添加两个成员变量:Context context;List meizis;和对应的构造方法,代码如下:

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第26张图片
    Context context;
    List meizis;

    public MeiziAdapter(Context context, List meizis) {
        this.context = context;
        this.meizis = meizis;
    }

然后在onBindViewHolder( ... ) { ... }中添加如下代码,并把getItemCount(...)中的return 100;改为return meizi.size();

[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第27张图片

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        Meizi meizi = meizis.get(position);

        Glide.with(context)
            .load(meizi.getImageUrl())
            .into(holder.image);

        holder.title.setText(meizi.getTitle());
        holder.name.setText(meizi.getName());
        holder.favorites.setText(String.valueOf(meizi.getFavorites()));
        holder.comments.setText(String .valueOf(meizi.getComments()));

    }

    @Override
    public int getItemCount() {
        return meizis.size();
    }

其中

    Glide.with(context)
        .load(meizi.getImageUrl())
        .into(holder.image);

就是Glide的用法,如果你的项目不需要特殊的图片加载优化,基本上这一行代码都能搞定图片加载。

其他的简要解释下:这一块代码,先根据条目的位置拿到对应的Meizi,然后通过Meizi这个实例获取到各个数据(image, title, name, favorites, comments),然后把这些数据通过ViewHolder(holder)适配给RecyclerView的条目中。
下面的getItemCount(),返回List meizis的大小,有多少妹子,就创建多少个条目。

不知道小白白们晕了没有?......坚持住,马上就可以事后烟了!→_→

最后一步!回到MainAcitvity.java中,修改如下代码:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第28张图片

咱们创建了一个List,并把它传给MeiziAdapter,就这些!!!
好,不罗嗦,现在赶紧准备好下一根事后烟,从新编译运行下,看看是不是像我下面的样子......
到这里,如果你硬着头皮挺了过来,那么你已经学会了如何动态的向RecyclerView中添加数据了!而且是从网络加载的图片,厉害啊!(๑´ㅂ`๑́)و✧


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第29张图片

===============================================================

好,在这个时间点,咱们再来回顾下RecyclerView的实现原理,效果最佳。你再反复阅读下你刚才写的代码,再看看下面这4条,参照下图片,看看是不是有点道理了?

  1. RecyclerView在屏幕上显示的各个条目,加载的是同一个,或者几个xml布局文件,只不过是往里面塞了不同的数据而已。
  2. RecyclerView每个条目中的各个控件(ImagaeView,TextView,Button......),是由一个叫ViewHolder的家伙把控的。这个ViewHolder需要咱们自己写,并且它需要继承自RecyclerView.ViewHolder。
  3. RecyclerView每个条目中的数据,是由一个叫Adapter的家伙适配的,并且它需要上面的ViewHolder的配合。这个Adapter也是需要咱们自己实现的类,并且它需要继承自RecyclerView.Adapter。
  4. LayoutManager负责RecyclerView条目的显示方式,比如垂直显示,水平显示,还是卡片式显示等。
[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第30张图片

.
.

==【第四部分】===================================================

好,接下来咱们中场小憩一下,来学点轻松点的:如何自定义Toolbar(也叫ActionBar),这部分涉及到的Java代码不多,先对轻松些,来一块完成。
首先在res/values/styles.xml中增加自定义主题。如下图:


[ ღ⊙□⊙╱ ] 一个列子,搞懂Android RecyclerView![2018.08.06更新]_第31张图片