ItemDecoration的一些使用小技巧

RecyclerView是安卓开发中经常使用的列表控件,当初google设计它的目的就是用来取代listview和gridview。这篇文章要讲的主角是recyclerview的一个附属品-ItemDecoration。讲解的也都是基础内容,主要有三个部分:java

  1. 为每一个item实现索引
  2. 为特定的item实现不一样的分隔线
  3. 覆盖在item上的一个可移动的icon

最终的效果以下(忽略毫无设计的ui):git

github地址:github.com/rangaofei/C…github

简介

ItemDecoration是一个很是简单的抽象类,它没有抽象方法,除去废弃的api,共有三个方法能够重写,分别是canvas

//获取当前view的位置信息
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) //在item背后draw public void onDraw(Canvas c, RecyclerView parent, State state) //在item上边draw public void onDrawOver(Canvas c, RecyclerView parent, State state) 复制代码

这三个方法能够说是三个很是重要的方法,他们之间有着强烈的因果关系。api

首先须要注意的是三个方法的调用顺序:ide

  1. 首先调用的是getItemOffsets会被屡次调用,在layoutmanager每次测量可摆放的view的时候回调用一次,在当前状态下须要摆放多少个view这个方法就会回调多少次。
  2. 其次会调用ondraw方法,ItemDecoration的ondraw方法是在recyclerview的ondraw方法中调用的,注意这时候传入的canvas是recyclerview的canvas,要时刻注意这点,它是和recyclerview的边界是一致的。这个时候绘制的内容至关于背景,会被item覆盖。
  3. 最后调用的是ondrawover方法,ItemDecoration的ondrawover方法是在recyclerview的draw方法中调用的,一样传入的是recyclerview的canvas,这时候onlayout已经调用,因此此时绘制的内容会覆盖item。

理解了以上三个方法,咱们就能够随意定制itemdecoration了。ui

为每一个item实现索引

这个要引出的知识点是关于getItemOffsets的详细用法,在这里会传过来四个参数(全部的方法中的state暂不讨论,它和layoutmanager关系比较密切),主要关注前三个参数:this

  1. outRect,核心参数,这个rect至关于item摆放的时候设置的margin,rect的left至关于item的marginleft,rect的right至关于item的marginright。
  2. view,当前绘制的view,能够用来获取它在adapter中的位置
  3. parent,recyclerview,没什么好说的。

上一段简单的代码,来实现为全部的左侧添加一个空白google

if (parent.getLayoutManager() instanceof LinearLayoutManager) {
    this.layoutOrientation = ((LinearLayoutManager) parent.getLayoutManager()).getOrientation();
}
switch (this.layoutOrientation) {
    case LinearLayoutManager.VERTICAL:
        outRect.set(40, 0, 0, 2);//bottom正常
        break;
    case LinearLayoutManager.HORIZONTAL://比较懒,没写
        break;
    default:
        break;
}
复制代码

这样,就在全部的item左侧流出来一段空白,接下来就能够在这段空白上来绘制文字:spa

首先在ondraw方法中分发事件:

@Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        Log.d("---", "onDraw");
        switch (this.layoutOrientation) {
            case LinearLayoutManager.VERTICAL:
                drawVertical(c, parent);
                break;
            case LinearLayoutManager.HORIZONTAL:
                drawHorizontal(c, parent);
                break;
            default:
                break;
        }
    }
复制代码

而后看一下具体的绘制文字流程:

c.save();
final int left;
final int right;
if (parent.getClipToPadding()) {
    left = parent.getPaddingLeft();
    right = parent.getWidth() - parent.getPaddingRight();
    c.clipRect(left, parent.getPaddingTop(), right,
            parent.getHeight() - parent.getPaddingBottom());
} else {
    left = 0;
    right = parent.getWidth();
}

for (int i = 0; i < parent.getChildCount(); i++) {
    View view = parent.getChildAt(i);
    int position = parent.getChildAdapterPosition(view);
    String text = String.valueOf(position+1);
    float w = textPaint.measureText(text);
    c.drawText(text, 20 - w / 2, view.getBottom() - view.getHeight() / 2 + textPaint.getFontMetri().descent, textPaint);
}
c.restore();
复制代码

这里要注意两点,paren.getChildCount是获取的当前显示的view的数量,并不会获取不显示的view的数量。假如recyclerview里共有30条数据,而当前屏幕内显示的只有5条,这paren.getChildCount的值是5,不是30。 int position = parent.getChildAdapterPosition(view) 这段代码是获取的当前这个view在30条数据中的位置。

c.drawText(text, 20 - w / 2, view.getBottom() - view.getHeight() / 2 + textPaint.getFontMetri().descent, textPaint);

这段代码绘制了文字,文字的位置就是20 - w / 2, view.getBottom() - view.getHeight() / 2 + textPaint.getFontMetri().descent这个坐标,假如对绘制文字时坐标的获取不太熟悉的话,须要搜索一下资料。

为特定的item实现不一样的分隔线

这个很是简单,只须要在getitemoffset中判断便可:

switch (this.layoutOrientation) {
            case LinearLayoutManager.VERTICAL:
                if (parent.getChildAdapterPosition(view) % 3 == 0) {
                    outRect.set(40, 0, 0, 100);

                } else {
                    outRect.set(40, 0, 0, 2);
                }
                break;
            case LinearLayoutManager.HORIZONTAL:
                break;
            default:
                break;
        }
复制代码

经过上面代码,就为全部的索引能被3整除的位置添加了一个高度为100的分隔线。看一下效果。

覆盖在item上的一个可移动的icon

这个方法须要在ondrawover中实现,icon会覆盖在item上边,并随屏幕比例来回移动,废话很少,直接上代码:

private void drawOverVertical(Canvas c, RecyclerView parent) {
        c.save();
        final int left;
        final int right;
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            c.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        for (int i = 0; i < parent.getChildCount(); i++) {

            View view = parent.getChildAt(i);
            int top = view.getTop();
            int totalHeight = parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom();
            float percentT = (float) top / totalHeight;
            drawable.setBounds((int) (view.getLeft() + 10 + view.getWidth() * percentT),
                    view.getTop(),
                    (int) (view.getLeft() + 58 + view.getWidth() * percentT),
                    view.getTop() + 48);
            drawable.draw(c);
        }
    }
复制代码

上面代码中测量了当前view的top位置相对于recyclerview的高度的比例,在item上实现随位置滚动而左右滑动的一个icon。

一篇很是简单的文章,但愿能帮助到一些朋友。

另外分享一个本身正在编写的基于注解的库: github.com/rangaofei/T…

欢迎提意见

相关文章
相关标签/搜索