02_ItemDecoration介绍和源码分析

159 阅读2分钟

介绍

RecyclerView的ItemDecoration是用来在RecyclerView的子项之间添加装饰(如分隔线、边距等)的类。它允许你在不修改RecyclerView子项布局的情况下,通过绘制在子项周围来改变它们的外观和间距。

主要用途

  • 添加分隔线: 可以是水平或垂直的线条,用于将列表项分隔开。
  • 设置边距: 可以增加列表项的边缘空间,改变它们之间的间距。
  • 自定义装饰: 可以根据需求自定义复杂的装饰,如根据条件显示不同类型的分隔线或边距。

示例代码

val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val itemDecoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
recyclerView.addItemDecoration(itemDecoration)

RecyclerView.ItemDecoration介绍

RecyclerView.ItemDecoration是一个抽象类,用于定义在RecyclerView中绘制装饰的规范。我们需要继承并实现它的几个方法来实现自定义的装饰效果:

方法解析

  • onDraw: 在RecyclerView的子项下方绘制装饰,例如分隔线。这些内容在子项视图之前绘制,因此会显示在视图的底部。
  • onDrawOver: 在RecyclerView的子项上方绘制装饰,例如悬浮效果。这些内容在子项视图之后绘制,因此会显示在视图的顶部。
  • getItemOffsets: 为每个子视图设置装饰的空间偏移,通常用于为分隔线预留空间。

DividerItemDecoration

DividerItemDecoration是官方提供的一个默认的ItemDecoration实现,用于在RecyclerView的子项之间添加分隔线。它可以根据需要设置分隔线的样式、颜色等属性。

onDraw方法示例

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

onDraw方法中,根据分隔线的方向(垂直或水平),调用对应的绘制方法,如drawVertical用于绘制垂直分隔线。

drawVertical方法实现

private void drawVertical(Canvas canvas, RecyclerView parent) {
    // 保存画布当前状态
    canvas.save();
    final int left;
    final int right;
    if (parent.getClipToPadding()) {
        // 如果RecyclerView设置了clipToPadding,考虑Padding的影响
        left = parent.getPaddingLeft();
        right = parent.getWidth() - parent.getPaddingRight();
        // 剪切画布,使其只在padding区域内绘制
        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);
        parent.getDecoratedBoundsWithMargins(child, mBounds);
        // 计算分隔线的顶端和底端位置
        final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
        final int top = bottom - mDivider.getIntrinsicHeight();
        // 设置分隔线的绘制区域
        mDivider.setBounds(left, top, right, bottom);
        // 绘制分隔线
        mDivider.draw(canvas);
    }
    // 恢复画布状态
    canvas.restore();
}

drawVertical方法中,我们首先保存了画布的当前状态,然后根据RecyclerView的设置(是否clipToPadding)确定绘制区域,并遍历子视图来绘制垂直方向的分隔线。

getItemOffsets方法示例

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    if (mDivider == null) {
        outRect.set(0, 0, 0, 0);
        return;
    }
    if (mOrientation == VERTICAL) {
        outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
    } else {
        outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
    }
}

getItemOffsets方法用于为每个子视图设置装饰的空间偏移,以便为分隔线预留必要的空间。