值得深入学习的控件-RecyclerView(进阶篇)

在入门篇我们学会了RecyclerView的简单使用。传送门:(https://www.jianshu.com/p/4260f7c6eb94)

但RecyclerView还是有很多其他的知识没有介绍。比如在入门篇我们看最终的实现效果里相邻的Item就没有分割线,这样看起来就不是很美观。一开始我以为RecyclerV没有默认的分割线,就写了一个,写完之后发现我的命名和自带默认的名字一样,这才发现原来是有默认分割线的[笑哭]。虽然RecyclerView提供默认的分割线,但是吧,那只是一条黑黑的线不好看,所以还是决定自己写一个可以变身的分割线了。

另外一个是RecyclerView没有ListVies方便的地方,就是它没有默认的点击事件实现。
也就是我们的Item没有实现触摸反馈功能。比如单击事件和长按事件的实现。

所以我们的进阶篇主要就学习上面的两个部分:

(1)自定义分割线

(2)Item的点击事件的实现

一、自定义分割线

1.实现分割线最关键的点就是计算好在哪个位置去画一条线。更恰当的说是画一个矩形。

(1)首先定义一个类先继承抽象类ItemDecoration,

(2)然后重写里面几个重要的方法:onDraw()、getItemOffsets()

2.其中onDraw()我们很熟悉了,通过 Canvas 对象绘制内容

要注意的是:Itemdecoration的onDraw()绘制会先于ItemView的onDraw()绘制,所以如果在Itemdecoration的onDraw()中绘制的内容在ItemView边界内,就会被ItemView遮挡住。如下图:

2.png

黄色部分区域就会被item遮挡住,这个现象被称为onDraw的overDraw现象。

3.重写它的另外一个方法:getItemOffsets()

(1)设置ItemView的内嵌偏移长度(inset)

内嵌偏移长度是指:该矩形(outRect)与 ItemView的间隔

内嵌偏移长度分为4个方向:上、下、左、右,并由outRect 中的 top、left、right、bottom参数控制。默认四个值都是0

对照下图的左图,其中灰色区域就是内嵌偏移长度inset。当inset的四个方向都是默认值0的时候,就是右图的效果:矩形和item重叠了

1.png

(2)那么对于之前说的onerDraw现象就可以借助这个方法来解决了

解决方案:通过getItemOffsets()设置与Item的间隔区域,从而获得与ItemView不重叠的绘制区域

4.结合代码来看

/**
 * 任意颜色的分割线
 */
public class ColorDividerItemDecoration extends RecyclerView.ItemDecoration {
    private float mDividerHeight;
    private Paint mPaint;

    //item布局方式
    private int mOrientation;
    //水平布局
    public static final int HORIZONTAL_LIST= LinearLayoutManager.HORIZONTAL;
    //竖直布局
    public static final int VERTICAL_LIST=LinearLayoutManager.VERTICAL;


    /**
     *默认是黑色、宽度为1的水平线
     */
    public ColorDividerItemDecoration(){
        mPaint=new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.BLACK);
        mDividerHeight=1;
        mOrientation=HORIZONTAL_LIST;
    }

    /**
     * @param color             分割线颜色
     * @param mDividerHeight   分割线宽度
     * @param  mOrientation     布局方向
     */
    public ColorDividerItemDecoration(int color,float mDividerHeight,int mOrientation){
        mPaint=new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(color);
        this.mDividerHeight=mDividerHeight;
        setOrientation(mOrientation);
    }


    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (mOrientation==VERTICAL_LIST){
            outRect.set(0,0,100,0);
        }else {
            outRect.set(0,0,0,100);
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if(mOrientation==HORIZONTAL_LIST)
            drawHorizontal(c,parent);
        else if (mOrientation==VERTICAL_LIST)
            drawVertical(c, parent);
    }


    /**
     * 画水平的分割线
     * @param c  画板
     * @param parent   recyclerview
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int childCount=parent.getChildCount();
        //要给每一个item都画上线条,所以要遍历每个item的view
        for (int i=0;i<childCount;i++){
            View view=parent.getChildAt(i);
            int index=parent.getChildAdapterPosition(view);

            if (index==0) continue;
            float dividerTop=view.getTop()-mDividerHeight;
            float dividerLeft=parent.getPaddingLeft();
            float dividerRight=parent.getWidth()-parent.getPaddingRight();
            float dividerBottom=view.getTop();

            c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);
        }
    }


    /**
     * 画竖直的分割线
     * @param c  画板
     * @param parent   recyclerview
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount=parent.getChildCount();
        //要给每一个item都画上线条,所以要遍历每个item的view
        for (int i=0;i<childCount;i++){
            View view=parent.getChildAt(i);
            int index=parent.getChildAdapterPosition(view);

            if (index==1) continue;
             //RecyclerView的距离顶部padiing的距离
                float dividerTop=parent.getPaddingTop();
            //子view距离左边界距离减去分割线的宽度
            float dividerLeft=view.getLeft()-mDividerHeight;
            //子view距离左边界距离
            float dividerRight=view.getLeft();
            //RecyclerView的高度减去距离底部的padding距离
            float dividerBottom=parent.getHeight()-parent.getPaddingBottom();

            c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);
        }
    }


    /**
     * 设定布局方向
     * @param orientation
     */
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException(
                    "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
        }
        mOrientation = orientation;
    }
}

(1)在构造方法中准备数据:画笔、item的布局方式

(2)然后就可以在onDraw方法中判断你的item布局方式从而选择画相应的分割线。

(3)比如这里我的布局是垂直布局,那么就需要画垂直的分割线drawVertical()。

2.jpg

(4)简单分析一下drawVertical()过程:

因为onDraw()针对RecyclerView本身,所以在使用onDraw()绘制时,需要先遍历RecyclerView的所有ItemView,分别获取它们的位置信息,然后再绘制内容。

位置信息:

 //RecyclerView的距离顶部padiing的距离
float dividerTop=parent.getPaddingTop();
//子view距离左边界距离减去分割线的宽度
float dividerLeft=view.getLeft()-mDividerHeight;
//子view距离左边界距离
float dividerRight=view.getLeft();
//RecyclerView的高度减去距离底部的padding距离
float dividerBottom=parent.getHeight()-parent.getPaddingBottom();

位置确定完成后就可以画矩形框了,最终形成的就是我们的分割线

 c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);

注意getItemOffsets() 针对是每一个ItemView的

if (mOrientation==VERTICAL_LIST){
            outRect.set(0,0,100,0);
        }

这里我设置距离左边是100单位,那么可以回到上面的效果图中看出黑色和红色分割线有一段较长的距离。

5.自定义分割线分析完了,那么最后只需要在加入一句话就可以应用这个分割线了:

mRecyclerView.addItemDecoration(new ColorDividerItemDecoration(Color.RED,5,LinearLayoutManager.VERTICAL));

二、Item的点击事件

1.定义一个接口
写两个方法:单击事件和长按事件

 public  interface OnItemClickListener<T>{
        void onClick(T item);
        void onItemLongClick(int item);
    }

2.写回调方法

public void setOnClickListener(OnItemClickListener<News> mOnClickListener){
        this.mOnItemClickListener=mOnClickListener;
    }

3.在onBindViewHolder里面实现接口方法

        //点击事件
        newsViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mOnItemClickListener!=null){
                    mOnItemClickListener.onClick(news);
                }
            }
        });

        //长按点击事件
        newsViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                int pos=holder.getLayoutPosition();
                mOnItemClickListener.onItemLongClick(pos);
                return false;
            }
        });

4.在Activity里监听回调

     mNewsAdapter.setOnClickListener(new NewsAdapter.OnItemClickListener<News>() {
            @Override
            public void onClick(News item) {
                Toast.makeText(MainActivity.this,"点击了其中一条"+item.getNewsTitle(),Toast.LENGTH_SHORT).show();
            }

            //监听长按事件
            @Override
            public void onItemLongClick(final int item) {
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("确认删除吗?")
                        .setNegativeButton("取消",null)
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                mNewsAdapter.removeData(item);
                                Toast.makeText(MainActivity.this,"已经删除了此条信息"+item,Toast.LENGTH_SHORT).show();
                                mNewsAdapter.notifyDataSetChanged();
                            }
                        })
                        .show();
            }
        });

5.点击其中一个item效果如下


3.jpg

到这里我们就结束了RecyclerView的小进阶,其实关于RecyclerView的其他内容还有很多,比如比较常用的时间轴效果,可能会在下一篇或下下一篇进行讲解。因为下一篇很有可能是源码解析篇哈。

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

推荐阅读更多精彩内容