值得深入学习的控件-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遮挡住。如下图:

7911186-fd62aa3a8e909acd.png
2.png

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

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

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

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

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

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

7911186-9ed85b66247c8434.png
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()。

7911186-1cf4ed0223a849c3.jpg
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效果如下


7911186-049005c781c08461.jpg
3.jpg

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值