RecyclerView使用总结-------上

前言

学习使用RecyclerView也有一段时间,一直都没有时间做个总结,就抽空想把RecyclerView的一些使用方法总结出来,方便大家也方便自己查阅。

简介

RecyclerView是support.v7包中的控件,可以说是ListView和GridView的增强升级版。既然官方已经提供了ListView和GridView,为什么还要提供RecyclerView呢?相对于来说RecyclerView肯定是比他们具有一些没有的优势。总的来说就是:低耦合高内聚,RecyclerView已经标准化ViewHolder,我们自定义的ViewHoler需要继承 RecyclerView.ViewHolder,然后在构造方法中初始化控件。通过设置不同的LayoutManager,可以实现Item不同的布局方式。插拔式的使用体验,结合ItemDecoration , ItemAnimator,ItemTouchHelper,则可以实现Item之间的分割线、动画,滑动拖拽等炫酷效果,这是ListView、GridView等控件难以企及的。


RecyclerView的简单使用

首先添加依赖库:

implementation 'com.android.support:recyclerview-v7:27.1.1'

activity的布局:

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

Item的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        app:cardElevation="5dp"
        app:cardCornerRadius="10dp"
        app:cardBackgroundColor="#47b7aa"
        android:layout_marginRight="30dp"
        android:layout_marginLeft="30dp"
        android:layout_height="50dp">
        <TextView
            android:textColor="#000000"
            android:text="text"
            android:textSize="16dp"
            android:gravity="center"
            android:id="@+id/text"
            android:clickable="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v7.widget.CardView>
</RelativeLayoAut>

在Activity中获取RecyclerView,并设置LayoutManager以及Adapter:

//初始化数据
List<String> datas = new ArrayList<>();
for(int i=0;i<10;i++){
     datas.add("item "+i);
}

recyclerView = findViewById(R.id.recycler_view);
//设置LayoutManager为LinearLayoutManager
recyclerView.setLayoutManager(new LinearLayoutManager(this));
//设置Adapter
recyclerView.setAdapter(new GeneralAdapter(this,datas));

这里的LayoutManager就是RecyclerView的布局管理器,用以实现Item不同布局排列方式,RecyclerView必须调用setLayoutManager设置布局管理器。RecyclerView中默认带有三个布局管理器:LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager,这三种布局管理器都支持横向和纵向排列以及反向滑动,这个在后面会讲解到。

Adapter的代码:

public class GeneralAdapter extends RecyclerView.Adapter<GeneralAdapter.MyViewHolder> {
    //当前上下文对象
    Context context;
    //RecyclerView填充Item数据的List对象
    List<String> datas;

    public GeneralAdapter(Context context,List<String> datas){
        this.context = context;
        this.datas = datas;
    }

    //创建ViewHolder
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //实例化得到Item布局文件的View对象
        View v = View.inflate(context, R.layout.item_recycler,null);
        //返回MyViewHolder的对象
        return new MyViewHolder(v);
    }

    //绑定数据
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.textView.setText(datas.get(position));
    }

    //返回Item的数量
    @Override
    public int getItemCount() {
        return datas.size();
    }

    //继承RecyclerView.ViewHolder抽象类的自定义ViewHolder
    class MyViewHolder extends RecyclerView.ViewHolder{
        TextView textView;

        public MyViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.text);
        }
    }
}

这里的Adapter与ListView的Adapter相差不大,只不过这里是继承自RecyclerView.Adapter这个抽象类。后面是一个必须继承自RecyclerView.ViewHolder抽象类的ViewHolder的泛型约束,也就是GeneralAdapter.MyViewHolder这个内部类,这个在前言中讲到过。RecyclerView.Adapter抽象类有三个必须实现的抽象方法:

public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);

public abstract void onBindViewHolder(@NonNull VH holder, int position);

public abstract int getItemCount();

具体的用途上方Adapter的代码中都有注释。

以上就是RecyclerView的简单使用了,献上效果图:


Screenshot_2018-05-14-16-12-23.png

item的点击事件

在RecyclerView当中,并没有如同ListView那样并直接提供类似setOnItemClickListener( )和setOnItemLongClickListener ( )的方法。如果要实现Item的点击事件,我们可以自己去实现。

这里给出两种方法:

① 在Adapter里面直接对控件做点击事件
② 写接口,在Activity或Fragment上实现接口中定义的方法

方法一:

@Override
    public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
        holder.textView.setText(datas.get(position));
        //直接对控件做点击事件
        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context,holder.textView.getText()+"被点击了",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

方法二:

定义回调接口:

/**
 * 设置回调接口
 */
public interface OnItemClickListener {
    void onItemClick(View view, int position);
}
/**
 * 设置长按回调接口
 */
public interface OnItemLongClickListener {
    boolean onItemLongClick(View view, int position);
}

Adapter中的部分代码:

public class GeneralAdapter extends RecyclerView.Adapter<GeneralAdapter.MyViewHolder> {
    //当前上下文对象
    Context context;
    //RecyclerView填充Item数据的List对象
    List<String> datas;

    public GeneralAdapter(Context context,List<String> datas){
        this.context = context;
        this.datas = datas;
    }


    /**
     * 事件回调监听
     */
    private OnItemClickListener onItemClickListener;
    private OnItemLongClickListener onItemLongClickListener;
    /**
     * 设置回调监听
     *
     * @param listener
     */
    public void setOnItemClickListener(OnItemClickListener listener) {
        this.onItemClickListener = listener;
    }

    public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
        this.onItemLongClickListener = onItemLongClickListener;
    }
   
    。。。。。。。。。

    //绑定数据
    @Override
    public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
        holder.textView.setText(datas.get(position));
        //通过接口回调响应点击事件
        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(onItemClickListener != null){
                    onItemClickListener.onItemClick(view,position);
                }

            }
        });
        //通过接口回调响应长按事件
        holder.textView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                if(onItemLongClickListener != null){
                    return onItemLongClickListener.onItemLongClick(view,position);
                }
                return false;
            }
        });
    }

    。。。。。。。

}

Activity中使用:

        //点击事件
        adapter.setOnItemClickListener(new GeneralAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(GeneralActivity.this,datas.get(position)+"被点击了",
                        Toast.LENGTH_SHORT).show();
            }
        });
        //长按事件
        adapter.setOnItemLongClickListener(new GeneralAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(View view, int position) {
                Toast.makeText(GeneralActivity.this,datas.get(position)+"被长按了",
                        Toast.LENGTH_SHORT).show();
                return true;
            }
        });

效果图:


Screenshot_2018-05-15-14-55-15.png
Screenshot_2018-05-15-15-15-16.png

布局管理器

前文说到RecyclerView的使用必须调用setLayoutManager来设置布局管理器,是为什么呢?因为RecyclerView不像ListView Item只有垂直排列一种排列方式,而RecyclerView通过setLayoutManager设置不同的布局管理器,Item就会按照设置的布局管理器的排列方式来排列。如果不调用setLayoutManager来设置布局管理器就会导致RecyclerView不知道它的Item如何布局,界面上就会一片空白。RecyclerView自身带有三种默认的布局管理器:LinearLayoutManagerGridLayoutManagerStaggeredGridLayoutManager

LinearLayoutManager线性布局管理器

LinearLayoutManager就如同它的翻译一样,线性布局管理器。设置这个布局管理器的话,可以设置它的排列方向,通过setOrientation()函数设置,参数可以是水平或者垂直。如果是垂直的话,那就跟ListView一模一样了。也就是前面简单使用中的例子。

LinearLayoutManager布局管理器默认使用垂直排列布局,而需要水平排列布局的话则可以通过LinearLayoutManager对象的setOrientation方法设置,此方法接收int类型的值,分别为RecyclerView中的两个Int常量:

public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;

HORIZONTAL :代表水平排列
VERTICAL :代表垂直排列

也可以在new出LinearLayoutManager对象的时候调用它的三个参数的构造方法设置它的布局排列方向。

public LinearLayoutManager(Context context, @RecyclerView.Orientation int orientation,
boolean reverseLayout)
第二个参数就是这个布局管理器的排列方式,依旧是传入上方两个常量值中的其中一个。第三个参数一般传false,它的用途是当为true时数据和滑动都是反向的。具体意思可以自行实验。

在前面的代码基础上,放上水平排列的代码

Item布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.CardView
        android:layout_width="200dp"
        app:cardElevation="5dp"
        app:cardCornerRadius="10dp"
        app:cardBackgroundColor="#60b3a9"
        android:layout_height="200dp">
        <TextView
            android:textColor="#000000"
            android:text="text"
            android:textSize="16dp"
            android:gravity="center"
            android:id="@+id/text"
            android:clickable="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v7.widget.CardView>
</RelativeLayout>

Activity中的代码:

public class GeneralActivity extends AppCompatActivity {
    
    RecyclerView recyclerView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_general);

        //初始化数据
        List<String> datas = new ArrayList<>();
        for(int i=0;i<10;i++){
            datas.add("item "+i);
        }

        recyclerView = findViewById(R.id.recycler_view);
        //通过setOrientation设置或者直接在构造方法中设置
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(RecyclerView.HORIZONTAL);

        //设置LayoutManager为LinearLayoutManager
        recyclerView.setLayoutManager(new LinearLayoutManager(this,RecyclerView.HORIZONTAL,false));
        //recyclerView.setLayoutManager(linearLayoutManager);
        //设置Adapter
        recyclerView.setAdapter(new GeneralAdapter(this,datas));
    }
}

Adapter中无变化,就不放上来筹字数了。

呈上效果图:

Screenshot_2018-05-14-17-08-20.png
GridLayoutManager网格布局管理器

网格布局管理器,顾名思义Item已网格方式排列。跟线性布局管理器一样,也支持通过setOrientation()或者构造方法来设置Item的排列方向。

垂直排列:

//设置LayoutManager为GridLayoutManager
recyclerView.setLayoutManager(new GridLayoutManager(this,3));

这里使用了GridLayoutManager两个参数的构造方法,第二个参数Item有几行或者几列。之所以这么说,是因为当水平排列时,就是有几行。垂直排列时就是有几列。当不设置排列方式时,默认使用垂直排列。

垂直排列效果图:

Screenshot_2018-05-15-13-54-29.png

水平排列:

GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
gridLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
//设置LayoutManager为GridLayoutManager
recyclerView.setLayoutManager(gridLayoutManager);

效果图:

Screenshot_2018-05-15-13-55-42.png
StaggeredGridLayoutManager 瀑布流布局管理器

通过设置StaggeredGridLayoutManager则可以实现炫酷的瀑布流效果。它最常用的构造函数就一个,StaggeredGridLayoutManager(int spanCount, int orientation),spanCount代表每行或每列的Item个数,orientation代表列表的方向,竖直或者水平。
要实现瀑布流效果(仅讨论竖直方向的瀑布流样式),每一个Item的高度要有所差别,如果所有的item的高度相同,就和网格样式是一样的展示效果。示例中就实现两中不同高度的Item,一个高度为100dp,一个高度为200dp。

item1布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.CardView
        android:layout_width="200dp"
        app:cardElevation="5dp"
        app:cardCornerRadius="10dp"
        app:cardBackgroundColor="#60b3a9"
        android:layout_height="200dp">
        <TextView
            android:textColor="#000000"
            android:text="text"
            android:textSize="16dp"
            android:gravity="center"
            android:id="@+id/text"
            android:clickable="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v7.widget.CardView>
</RelativeLayout>

Item2布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.CardView
        android:layout_width="200dp"
        app:cardElevation="5dp"
        app:cardCornerRadius="10dp"
        app:cardBackgroundColor="#1e5851"
        android:layout_height="100dp">
        <TextView
            android:textColor="#000000"
            android:text="text"
            android:textSize="16dp"
            android:gravity="center"
            android:id="@+id/text"
            android:clickable="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v7.widget.CardView>

Adapter的代码:

public class StaggeredApapter extends RecyclerView.Adapter<StaggeredApapter.MyViewHolder> {

    //当前上下文对象
    Context context;
    //RecyclerView填充Item数据的List对象
    List<String> datas;

    public StaggeredApapter(Context context,List<String> datas){
        this.context = context;
        this.datas = datas;
    }

    @Override
    public int getItemViewType(int position) {
        if(position %2 == 0){
            return 0;
        }else{
            return 2;
        }
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v;
        if(viewType == 0){
            v = View.inflate(context,R.layout.item_recycler,null);
        }else{
            v = View.inflate(context,R.layout.item2_recycler,null);
        }

        return new MyViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.textView.setText(datas.get(position));
    }

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

    class MyViewHolder extends RecyclerView.ViewHolder{

        TextView textView;

        public MyViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.text);
        }
    }
}

这里Adapter的代码相对于之前,多重写了一个public int getItemViewType(int position)一个方法,这个方法是根据当前Item的position返回当前Item的类型。这里就是实现瀑布流的关键。可以看到在onCreateViewHolder方法中,我们根据不同的ViewType,初始化了不同 Item布局。这样Item的位置不一样, 那么它的高度就可能不一样。

Activity中的代码:

public class StaggeredLayoutActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_general);

        //初始化数据
        List<String> datas = new ArrayList<>();
        for(int i=0;i<20;i++){
            datas.add("item "+i);
        }
        recyclerView = findViewById(R.id.recycler_view);
        //设置LayoutManager为StaggeredGridLayoutManager
        recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,RecyclerView.VERTICAL));
        //设置Adapter
        recyclerView.setAdapter(new StaggeredApapter(this,datas));

    }
}

效果图:

Screenshot_2018-05-15-14-42-34.png

未完待续。。。。。。

敬请期待下篇的高级操作,Item间隔线、Item滑动删除、Item拖拽、Item添加移除动画、滑动动画、添加Footer,Header、分组、上拉加载、下拉刷新等

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容