博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RecyclerView 知识梳理(4) ItemDecoration
阅读量:6445 次
发布时间:2019-06-23

本文共 5976 字,大约阅读时间需要 19 分钟。

一、概述

通过ItemDecoration,可以给RecyclerView或者RecyclerView中的每个Item添加额外的装饰效果,最常用的就是用来为Item之间添加分割线,今天,我们就来一起学习有关的知识:

  • API
  • DividerItemDecoration解析
  • 自定义ItemDecoration

二、API介绍

当我们实现自己的ItemDecoration时,需要继承于ItemDecoration,并根据需要实现以下三个方法:

2.1 public void onDraw(Canvas c, RecyclerView parent, State state)

  • canvasRecyclerViewcanvas
  • parentRecyclerView实例
  • StateRecyclerView当前的状态,值包括START/LAYOUT/ANIMATION

所有在这个方法中的绘制操作,将会在itemViews被绘制之前执行,因此,它会显示在itemView之下。

2.2 public void onDrawOver(Canvas c, RecyclerView parent, State state)

2.1方法类似,区别在于它绘制在itemViews之上。

2.3 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)

通过outRect,可以设置item之间的间隔,间隔区域的大小就是outRect所指定的范围,view就是对应位置的itemView,其它的参数解释和上面相同。

三、DividerItemDecoration解析

3.1 使用方法

上面我们解释了需要重写的方法以及其中参数的含义,下面,我们通过官方自带的DividerItemDecoration,来进一步加深对这些方法的认识。 DividerItemDecoration是为LinearLayoutManager提供的分割线,在创建它的时候,需要指定ORIENTATION,这个方向应当和LinearLayoutManager的方向相同。

private void init() {        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_content);        mTitles = new ArrayList<>();        for (int i = 0; i < 20; i++) {            mTitles.add(String.valueOf(i));        }        BaseAdapter baseAdapter = new BaseAdapter(mTitles);        recyclerView.setLayoutManager(new LinearLayoutManager(this));        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));        recyclerView.setAdapter(baseAdapter);    }复制代码

最终展示的效果为:

3.2 源码解析

3.2.1 绘制

DividerItemDecoration重写了基类当中的onDraw方法,也就是说这个分割线是在itemView之前绘制的:

@Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        if (parent.getLayoutManager() == null) {            return;        }        if (mOrientation == VERTICAL) {            drawVertical(c, parent);        } else {            drawHorizontal(c, parent);        }    }复制代码

我们先看纵向排列的RecyclerView分割线:

@SuppressLint("NewApi")    private void drawVertical(Canvas canvas, RecyclerView parent) {        //首先保存画布        canvas.save();        final int left;        final int right;        //确定左右边界的范围,如果RecyclerView不允许子View绘制在Padding内,那么这个范围为去掉Padding后的范围        if (parent.getClipToPadding()) {            left = parent.getPaddingLeft();            right = parent.getWidth() - parent.getPaddingRight();            canvas.clipRect(left, parent.getPaddingTop(), right,                    parent.getHeight() - parent.getPaddingBottom());        } else {            left = 0;            right = parent.getWidth();        }        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            //获得itemView的范围,这个范围包括了margin和offset,它们被保存在mBounds当中            parent.getDecoratedBoundsWithMargins(child, mBounds);            //需要考虑translationY和translationY            final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));            //由于是垂直排列的,因此上边界等于下边界减去分割线的高度.            final int top = bottom - mDivider.getIntrinsicHeight();            //设置divider和范围            mDivider.setBounds(left, top, right, bottom);            //绘制.            mDivider.draw(canvas);        }        //回复画布.        canvas.restore();    }复制代码

整个过程分为三步:

  • 确定子ViewRecyclerView中的绘制范围
  • 确定每个子View的范围
  • 确定mDivider的绘制范围

下图就是最终计算的结果:

横向排列的
RecyclerView列表和上面的原理是相同的,区别就在于计算
mDivider.setBounds的计算:

//....parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));final int left = right - mDivider.getIntrinsicWidth();mDivider.setBounds(left, top, right, bottom);//..复制代码

3.2.2 边界处理

从上面的分析可以知道,如果将divider直接绘制在itemView的范围内,那么由于我们是先绘制divider,再绘制itemView的内容的,那么它就会被覆盖,因此,通过重写getItemOffsets,通过其中的outRect来指定留出的空隙:

@Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,            RecyclerView.State state) {        if (mOrientation == VERTICAL) {            //如果是纵向排列,那么要在itemView的下方留出一个下边界            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());        } else {            //如果是横向排列,那么要在itemView的右方留出一个右边界            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);        }    }复制代码

四、自定义ItemDecoration

下面,我们参考上面的写法,写一个简单的GridLayoutManager的分割线:

public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {    private static final int[] ATTRS = new int[] { android.R.attr.listDivider };    private Drawable mDivider;    private final Rect mBounds = new Rect();    public GridDividerItemDecoration(Context context) {        final TypedArray a = context.obtainStyledAttributes(ATTRS);        mDivider = a.getDrawable(0);        a.recycle();    }    public void setDrawable(@NonNull Drawable drawable) {        mDivider = drawable;    }    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        drawDivider(c, parent);    }    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());    }    private void drawDivider(Canvas canvas, RecyclerView parent) {        canvas.save();        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View view = parent.getChildAt(i);            parent.getDecoratedBoundsWithMargins(view, mBounds);            mDivider.setBounds(mBounds.right - mDivider.getIntrinsicWidth(), mBounds.top, mBounds.right, mBounds.bottom);            mDivider.draw(canvas);            mDivider.setBounds(mBounds.left, mBounds.bottom - mDivider.getIntrinsicHeight(), mBounds.right , mBounds.bottom);            mDivider.draw(canvas);        }        canvas.restore();   }}复制代码

最终的效果为:

这里我们为了演示方便,没有考虑最后一列或者最后一行没有分割线的情况,这篇文章写的比较好: 。

五、总结

ItemDecoration的使用并不难,大多数情况下就只需要重写onDrawonDrawOver中的一个;如果需要在Item之间添加间隔,那么要重写getItemOffsets并理解outRect的含义,假如不需要添加间隔,那么不需要重写该方法。


更多文章,欢迎访问我的 Android 知识梳理系列:

  • Android 知识梳理目录:
  • 个人主页:
  • 个人知识总结目录:

转载地址:http://arpwo.baihongyu.com/

你可能感兴趣的文章
[摘录]高效人士七习惯—重新探索自我
查看>>
CheckBoxList控件选中的选项不能改变
查看>>
编程回调面向对象设计开卷考题A
查看>>
posix多线程有感--线程高级编程(线程和fork,exec)
查看>>
如何通过超链接打开Activity并传入参数
查看>>
在Sql2005中,向表中插入数据时遇到uniqueidentifier列,如何插入数据?
查看>>
nullnullIOS里多态的一些方法
查看>>
PostgreSQL在何处处理 sql查询之三十六
查看>>
Windows Phone Isolated Storage 系列 - 使用XmlSerializer读取和存储XML文件
查看>>
创建FileShare的content source的SharePoint 2013的powershell脚本
查看>>
【iOS开发者必备】APP 图标规格参考表
查看>>
泛型中去掉指定字段重复的数据
查看>>
通过修改CR0寄存器绕过SSDT驱动保护
查看>>
Zen Coding css,html缩写替换大观 快速写出html,css
查看>>
根据复选框后面的描述文字进行选择的技巧
查看>>
asp.net 中将汉字转换成拼音
查看>>
[转]Easy Stored Procedure Output Oracle Select
查看>>
字符串的最长公共子序列问题
查看>>
Debug和Release区别
查看>>
php应该在何时调用mysql_close() ,可能和中断请求有关
查看>>