Android-Recyclerview常用总结

概述

在android开发中我们不可避免的会用到Recyclerview,用以替代之前的ListView,GridView,Gallery等.它是support:recyclerview-v7中提供的控件,最低兼容到android 3.0版本.之前listview难以实现或者不能实现的效果,它都可以实现或者轻松实现.
1.那么它有哪些优势呢?
总结也就一句话:高类聚低耦合.RecyclerView已经标准化ViewHolder,我们自定义的ViewHoler需要继承 RecyclerView.ViewHolder,然后在构造方法中初始化控件.
2.如何添加依赖?

- implementation 'com.android.support:recyclerview-v7:26.1.0'
  如果只是用到recyclerview控件优先选用此依赖方式
- implementation 'com.android.support:design:26.1.0'
  通过添加MD的disign包方式使用该控件,内部包含多种MD控件如:SnakeBar,

3.有哪些常用操作?

  • LayoutManager,布局管理器,控制其显示的方式。
  • ItemDecoration,控制Item间的间隔(允许绘制)。
  • ItemAnimator,控制Item增删的动画。

使用

recyclerview的布局管理器有三种分别是线性布局管理器(LinearLayoutManager);表格布局管理器(GridLayoutManager);瀑布流布局管理器(StaggeredGridLayoutManager)。这个不做详细介绍。简单上一下代码。

       //设置为线性管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //设置为表格管理器
        mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
        //瀑布流管理器
        StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(manager);
        //设置item增加删除动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        //给item增加自带分割线
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));

关于RecyclerView设置分割线和增加删除动画我们这里使用的是默认你都可以自定义,这个无所谓。

1.item的监听

不知道为啥recycleerview没有提供监听方法,这就要我们手动写相关代码了,这个实现方式一般有两种。前者是通过对Recyclerview触摸的监听,后者是自定义接口实现接口回调。后者相对简单,在我们的adapter中我们来搞一下:

item的监听 方式一
 private OnItemClickLitener mOnItemClickLitener;

    public interface OnItemClickLitener
    {
        void onItemClick(View view, int position);
        void onItemLongClick(View view , int position);
    }

    public void setOnItemClickLitener(OnItemClickLitener OnItemClickLitener)
    {
        this.mOnItemClickLitener = OnItemClickLitener;
    }

然后设置监听回调:

  @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        holder.tvName.setText(mArrayList.get(position).name);
        holder.tvSex.setText(mArrayList.get(position).sex);
        holder.tvAge.setText(mArrayList.get(position).age + "");

        // 如果设置了回调,则设置点击事件
        if (mOnItemClickLitener != null) {
            holder.LinearItem.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemClick(holder.itemView, pos);
                }
            });

            holder.LinearItem.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                    return false;
                }
            });

        }
    }

在写一个增加和减少item的方法:


    public void addItem(int position) {
        RecyclerViewBean mRecyclerViewBean =new RecyclerViewBean("增加用户"+position,100,"nan");
        mArrayList.add(position, mRecyclerViewBean);
        notifyItemInserted(position);
    }

    public void removeItem(int position) {
        mArrayList.remove(position);
        notifyItemRemoved(position);
    }

设置adaper监听:

mRecyclerViewAdapter.setOnItemClickLitener(new RecyclerViewAdapter.OnItemClickLitener() {
            @Override
            public void onItemClick(View view, int position) {
                mRecyclerViewAdapter.addItem(position);
            }

            @Override
            public void onItemLongClick(View view, int position) {
                mRecyclerViewAdapter.removeItem(position);
            }
        });
item的监听 方式二(对recyclerview触摸实现监听)

这个相对有一点难度,在这个之前我们要先来了解一个手势识别类----》GestureDetectorCompat
如果你不了解GestureDetectorCompat那么你一定知道onTouchEvnet,我们刚开始都是通过View.OnTouchListener内部接口,通过重写他的[onTouch]方法,获取Action来判断move,up,down动作,如下:

 mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
 int action = MotionEventCompat.getActionMasked(event);
    switch(action) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
       default : 
            return super.onTouchEvent(event);

            }
        });

不陌生吧,但是这个监听只能应对一些简单的操作,如果是一些比较复杂的,比如:根据用户触摸的轨迹去判断是什么手势就显得吃力了,所以Android sdk给我们提供了GestureDetectorCompat(Gesture:手势Detector:识别)类,更高效。

GestureDetectorCompat类是GestureDetector的替代,它的创建方式有两种,我们选第一种方式

image.png

它是V4包下的。这不是重点,重点是我们怎么用它,感兴趣的小伙伴自行百度。

先来说一下我们的思路:
给Recyclerview设置添加OnItemTouchListener监听重写其方法,然后创建GestureDetectorCompat对象,通过一个手势探测器 GestureDetectorCompat 来探测屏幕事件,然后通过手势监听器 SimpleOnGestureListener 来识别具体事件种类,对应回调,算了上代码吧
PjuDEV.md.png

Pjus4U.md.png

RecyclerView的addOnItemTouchListener监听

PjuQBt.png

ItemTouchListener这个类提出来单独使用,可以减少每个adapter中都定义一个接口回调,优化性能还高大上

2.RecyclerView之ItemDecoration

2.1.itemDecoration 简单使用

2.1.1 itemDecoration 简单使用针对LinearLayoutManager
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));

大家都注意到我们给recyclerview设置的这个属性了吧,这个属性就是使用其默认的ItemDecoration, DividerItemDecoration 继承自ItemDecoration。ok,我们试试如果自定义一个分割线。看一下DividerItemDecoration的setDrawable()方法。

/**
     * Sets the {@link Drawable} for this divider.
     *
     * @param drawable Drawable that should be used as a divider.
     */
    public void setDrawable(@NonNull Drawable drawable) {
        if (drawable == null) {
            throw new IllegalArgumentException("Drawable cannot be null.");
        }
        mDivider = drawable;
    }

明白了吧,先自定义一个drawable文件,如下:


image.png

给RecyclerView添加代码,如下:


image.png
2.1.2 itemDecoration 简单使用针对GridLayoutManager和StaggeredGridLayout

因为GridLayoutManager和StaggeredGridLayout的原理和方法差异不大, 我们就统一写一个了。
1.如果只是想简单实现我们可以在adapter的onCreateViewHolder()方法中设置View,如图:


image.png
2.2 如果想来一个稍微有难度的,可以尝试自定义一个ItemDecoration,那么就要 extends RecyclerView.ItemDecoration重写两个方法(其实一般有三个,我们这里用不到其中的一个onDrawOver):第一个getItemOffsets()此方法是针对每一个 ItemView,实际上RecyclerView 中的 ItemView 外面会包裹着一个矩形(outRect)。当outRect的左,右,上,下的内容都为0时,itemview和ontRect重叠你感觉只有一个item.类似:
image.png

其目的是控制矩形(outRect)与 ItemView的间隔,部分代码如下:


image.png

第二个方法:onDraw(),该方法配合前面的 getItemOffsets() 一起使用,在outRect矩形 与 ItemView的间隔区域 绘制内容,也就是在itemview的下面绘制了一个矩形的分割线,部分代码如下:

image.png

最后一步:给recyclerview设置自定义的分割线:


image.png

3.RecyclerView之实现滑动删除拖拽排序(首个可以固定)

3.1实现RecyclerView的滑动删除拖拽排序

滑动删除和拖拽必须用到ItemTouchHelper ,我们看它如何使用:

ItemTouchHelper  itemTouchHelper  = new ItemTouchHelper(new ItemTouchHelper.Callback());
itemTouchHelper.attachToRecyclerView(mRecyclerView); 
      

其创建的对象过程中要传一个ItemTouchHelper.CallBack(),它是ItemTouchHelper在拖拽中需要回调的方法,我们只需要在其对应返回的回调方法中处理自己的逻辑就好了,先看extends它需要重写的三个方法及作用。

image.png

针对getMovementFlags()方法如果是线性布局管理器有两个方向:上,下。但如果是网格布局管理器则有四个方向上,下,左,右,所以其方法要做判断是那种管理器,代码如下:
image.png

onMove()方法中的操作,注释都很明了,看不懂转行吧

这两个方法重写完,已经可以实现拖拽效果了,但为了美观我们要添加一个拖拽的背景,这就要用到另外两个方法:onSelectedChanged()和clearView(),代码如下:


image.png

然后就实现拖拽功能了

    ItemTouchHelper itemTouchHelper
                = new ItemTouchHelper(new RecyclerItemTouchHelperCallBack(mRecyclerViewAdapter));
        itemTouchHelper.attachToRecyclerView(mRecyclerView);

再然后我们使用第三个方法来实现滑动删除。

别忘了变动之前的getMovementFlags()。

image.png

这样我们就实现了滑动删除和拖拽移动功能,但别高兴太早,这代码仍然存在问题,你正常来讲网格布局应该不具备滑动删除才对,这怎么解决呢?通过有参构造来设置一个变量,在重写一个isItemViewSwipeEnabled()返回该boolean类型的值控制是否具备滑动删除即可。如图:

image.png

image.png

这样就是实现线性布局管理器和网格布局管理器区分对待,即:线性布局管理器拥有滑动删除和拖拽而网格布局管理器只拥有拖拽功能。

3.2实现首item固定功能

既然实现了item的拖拽和删除那么,首个固定就很简单了,我们继续在有参构造里添加一个boolean类型的变变量控制是否首个item固定,然后重写一个方法,如图:


image.png

image.png

所有item都被固定,然后


image.png

至此,首个item固定也就完成了。

4.RecyclerView之实现自定义动画

大家都看到了我们之前使用的是系统给我们提供的自带动画mRecyclerView.setItemAnimator(new DefaultItemAnimator());DafaultItemAnimator继承的是抽象类SimpleItemAnimator,SimpleItemAnimator主要对动画内部实现进行封装,通过抽象让我们更只关注于更具体的操作,我们定义一个类继承SimpleItemAnimator,然后里面主要有几个需要重写的方法。

  • void runPendingAnimations():当有动画需要执行时调用。
  • boolean isRunning():返回当前是否有动画正在运行。
  • boolean animateAdd():添加元素时调用,通常返回true。
  • boolean animateRemove():移除数据时调用。
  • boolean animateMove():列表项位置移动时调用。
  • boolean animateChange():列表项数据发生改变时调用。
  • void endAnimation():当某个动画需要被立即停止时调用,这里一般做视图的状态恢复。
  • void endAnimations() 作用和endAnimation()一样,区别是停止多个动画时调用。

具体就不说了,demo已上传github,代码戳我.

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

推荐阅读更多精彩内容