RecyclerView基本用法笔记

主要包括RecyclerView基本用法、插入删除Item、Item点击事件监听

一、RecyclerView的基本用法

1.RecyclerView的介绍
RecyclerView是support-V7报下的一个控件,用来替代ListView、GridView等。下面是官方的解释:

RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the RecyclerView widget when you have lists with elements that change dynamically. 其实也就是说当你需要动态展示一些item的时候建议使用RecyclerView。

2.RecyclerView的使用

  1. 首先需要导入这个包的引用,需要在app对应的build.gradle文件中导入引用:
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:recyclerview-v7:23.4.0'//这个是RecyclerView的依赖项
}

我用的AndroidStudio一般都是下面这个步骤来添加引用:File→Project Structure左侧选择对应的Moudle,选择右侧Dependences→add→Library Dependece然后找到对应的包选择添加。

  1. 布局文件activity_normal中使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.zwf.recyclerviewdemo.Activity.NormalActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/insertBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="插入"
            android:layout_weight="1"/>
        <Button
            android:id="@+id/removeBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="移除"
            android:layout_weight="1"/>
    </LinearLayout>
    
    <!--这里是使用RecyclerView-->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/id_normal_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</LinearLayout>

布局文件很简单,上面两个按钮,下面一个RecyclerView。

3.然后在代码中对RecyclerView进行使用,一般需要做以下设置:

public class NormalActivity extends AppCompatActivity implements View.OnClickListener {
    private RecyclerView mRecyclerView;
    private List<String> mDatas;
    private HomeAdapter adapter;
    private Button insertBtn,removeBtn;

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

        insertBtn = (Button) findViewById(R.id.insertBtn);
        removeBtn = (Button) findViewById(R.id.removeBtn);
        insertBtn.setOnClickListener(this);
        removeBtn.setOnClickListener(this);

        mDatas = this.getIntent().getStringArrayListExtra("data");

        //获取RecyclerView
        mRecyclerView = (RecyclerView) findViewById(R.id.id_normal_recyclerview);

        //为RecyclerView设置布局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        //为RecyclerView设置Adapter
        adapter = new HomeAdapter(this, mDatas);
        mRecyclerView.setAdapter(adapter);

        //为RecyclerView设置分割线(这个可以对DividerItemDecoration进行修改,自定义)
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL_LIST));

        //动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.insertBtn:
                adapter.addData(1);
                break;
            case R.id.removeBtn:
                adapter.removeData(1);
                break;
        }
    }
}

要想使用一个RecyclerView一般需要设置布局管理器、适配器、分割线、动画。

主要说一下布局管理器,一共有三种:LinearLayoutManager(线性布局)、GridLayoutManager(网格布局)、StaggeredGridLayoutManager(瀑布流布局)

然后看一下HomeAdapter的内容:

public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{
    private List<String> mDatas;
    private LayoutInflater mInflater;
    /**
     * Adapter适配器的构造函数,主要用来初始化数据,例如:LayoutInflater、data
     * @param mInflaters    这个参数也可以是Context,无非就是在哪获得LayoutInflater
     * @param mDatas        这是对应的RecyclerView要显示的数据
     */
    public HomeAdapter(Context context, List<String> mDatas){
        this.mDatas = mDatas;
        mInflater = LayoutInflater.from(context);
    }
    /**
     * 创建一个ViewHolder并返回
     * @param parent
     * @param viewType  可以根据这个参数创建不同item的ViewHolder
     * @return
     */
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        /**
         * LayoutInflater.from(MainActivity.this) 获取到父容器的inflater,跟fragment类似
         */
        MyViewHolder holder = new MyViewHolder(mInflater.inflate(R.layout.item_normal, parent, false));
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.tv.setText(mDatas.get(position));
    }

    /**
     * 返回item的个数
     */
    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView tv;
        public MyViewHolder(View view){
            super(view);
            // 这里传进来的view是一个MyViewHolder
            tv = (TextView) view.findViewById(R.id.id_num);
        }
    }

    public void addData(int position){
        mDatas.add(position, "insert");
        notifyItemInserted(position);
    }

    public void removeData(int position){
        mDatas.remove(position);
        notifyItemRemoved(position);
    }
}

这个Adapter需要继承自RecyclerView.Adapter,并且实现其中的抽象方法

上面还用到了这么一个类DividerItemDecoration,用来画分割线:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private Drawable mDivider;
    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent) {

        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }

    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}
二、插入删除Item

上面的HomeAdapter中已经实现了插入删除的功能,如下:

public void addData(int position){
    mDatas.add(position, "insert");
    notifyItemInserted(position);
}

public void removeData(int position){
    mDatas.remove(position);
    notifyItemRemoved(position);
}

其实主要是对数据进行插入删除操作,然后更新界面。插入用notifyItemInserted()删除用notifyItemRemoved(),这两个方法都需要一个下标参数。

三、Item点击监听

这里其实我是用接口回调实现的,具体会在瀑布流相关的代码中体现。

  • 初始化一个接口
  • 在Adapter中声明一个接口属性
  • onBindViewHolder()方法中为每一个Item添加点击事件或者长按事件,同时调用接口变量中对应的方法(此时可以把点击的下标也传进去)
  • 在为RecyclerView设置Adapter的时候,同时给Adapter设置上面声明接口属性,此时就可以实现在Activity或者Fragment中实现对Item点击事件的监听
四、关于瀑布流的使用

这里需要把RecyclerView的布局管理器设置成StaggeredGridLayoutManager,如下:

mRecycler.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));
//第一个参数是表示显示列(行),第二个参数是滚动方向
public class StaggeredAdapter extends RecyclerView.Adapter<MyViewHolder> {
    private List<String> mDatas;
    private LayoutInflater mInflaters;

    private List<Integer> mHieghts;
    private List<Integer> mWidths;

    /**
     * Adapter适配器的构造函数,主要用来初始化数据,例如:LayoutInflater、data
     * @param mInflaters    这个参数也可以是Context,无非就是在哪获得LayoutInflater
     * @param mDatas        这是对应的RecyclerView要显示的数据
     */
    public StaggeredAdapter(LayoutInflater mInflaters, List<String> mDatas){
        this.mInflaters = mInflaters;
        this.mDatas = mDatas;

        //init heights and width
        mHieghts = new ArrayList<>();
        for (int i=0; i<mDatas.size(); i++){
            mHieghts.add((int) (150+Math.random()*300));
        }
        mWidths = new ArrayList<>();
        for (int i=0; i<mDatas.size();i++){
            mWidths.add((int) (320+Math.random()*300));
        }
    }

    /**
     * 创建一个ViewHolder并返回
     * @param parent
     * @param viewType  可以根据这个参数创建不同item的ViewHolder
     * @return
     */
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        MyViewHolder holder = new MyViewHolder(mInflaters.inflate(R.layout.item_home, parent, false));

        return holder;
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        //获取Layout内容
        LayoutParams lp = holder.tv.getLayoutParams();

        //设置Layout的高度(根据具体位置的具体要求设置不同的高度)
        lp.height = mHieghts.get(position);
//        lp.width = mWidths.get(position);

        holder.tv.setLayoutParams(lp);
        holder.tv.setText(mDatas.get(position));

        if (mOnItemClickListener != null){
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickListener.onItemClick(view, pos);
                }
            });

            holder.itemView.setOnLongClickListener(new View.OnLongClickListener(){
                @Override
                public boolean onLongClick(View view) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickListener.onItemLongClick(holder.itemView, pos);
                    removeData(pos);

                    return false;
                }
            });
        }
    }

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

    /**
     * add item
     * @param position
     */

    /**********************add and remove*************************/
    public void addData(int position){
        mDatas.add(position, "insert");
        mHieghts.add(position, (int) (150 + Math.random() * 300));
        mWidths.add(position, (int) (320 + Math.random() * 300));
        notifyItemInserted(position);
    }

    public void removeData(int position){
        if (mDatas.size() > position)
        {
            mDatas.remove(position);
        }else {
            return;
        }

        mHieghts.remove(position);
        mWidths.remove(position);
        notifyItemRemoved(position);
    }
    /**********************add and remove*************************/

    /**********************click listener*************************/
    public interface OnItemClickListener{
        void onItemClick(View view, int position);
        void onItemLongClick(View view, int position);
    }

    private OnItemClickListener mOnItemClickListener;

    public void setmOnItemClickListener(OnItemClickListener onItemClickListener){
        mOnItemClickListener = onItemClickListener;
    }
    /**********************click listener*************************/
}

上面的实现是其实是使用的同一个ViewHolder,然后对这个ViewHolder的布局文件高度进行改变,来实现类似瀑布流的效果。
重点看一下onCreateViewHolder(ViewGroup parent, int viewType)这个方法,这个方法有两个参数。其中第二个参数就是View的类型,其实是getItemViewType(int position)的返回值。我们可以重写这个方法, 根据不同的数据,返回不同的值(这个值可以定义枚举类型),然后在穿件ViewHolder的时候就可以直接根据类型来创建了。

然后就是ViewHolder中常用的一个API是:getLayoutPosition(),可以获得Item的下标。

总的来说,关于RecyclerView的基本用法是弄明白了,但是表述的有点乱。这里做个总结,整理一下思路。还有像mRecyclerView.addItemDecoration(); mRecyclerView.setItemAnimator();这两个方法的用法可能在实际用的时候还需要深究。还有点击的时候长按在手指离开的时候也会触发点击事件。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,280评论 25 707
  • 这篇文章分三个部分,简单跟大家讲一下 RecyclerView 的常用方法与奇葩用法;工作原理与ListView比...
    LucasAdam阅读 4,368评论 0 27
  • 这周真是极冷 感觉从初冬重回冬天 回家却发现 你却在这时奋力生长 小小的盆已容不下你的身躯 冒出的小芽企图占有大片...
    渝绾阅读 173评论 0 0
  • 我想真的是之前的人生太顺利,或者说是忘的快无所谓没心没肺差不多的态度那些不顺利总觉得没什么,或者说曾经有一人看到他...
    凡人angel不烦人阅读 203评论 0 0
  • 不愉快的夜晚,不愉快的早上 对于自己讨厌的人提出精神病一样的要求,我没有涵养包容,为了老公的面子,我还得委屈自己,...
    candymoon阅读 143评论 0 0