美文网首页高级UIrecycleview优秀博客
RecyclerView<第五篇>:分割线(含粘贴头

RecyclerView<第五篇>:分割线(含粘贴头

作者: NoBugException | 来源:发表于2019-06-26 18:35 被阅读30次

在以前,项目中普遍使用ListView,而ListView有个divider属性可以添加分割线,然而这种分割线却显得比较简单,单调。后来,项目中普遍改用Recyclerview,下面我为大家展现Recyclerview分割线的风采。

Recycleview默认是没有分割线的,需要程序猿手动添加,添加分割线的方式有两种:

  • 在每个Item布局中添加一个横线作为分割线
  • 使用ItemDecoration抽象接口

【方式一】 每个Item布局中添加一个横线作为分割线,布局如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="#fff000">

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:textSize="20sp"
        android:textColor="@color/colorAccent"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:background="@color/gray"/>
</LinearLayout>

如图:

图片.png

怎么样,是不是很简单?但是,这样做有个弊端,我们需要在代码中隐藏最后一个Item下面的分割线,这样才能有更好的效果,一般添加分割线我们会采用方式二。

【方式二】 ItemDecoration抽象类,源码如下:

public abstract static class ItemDecoration {
    public ItemDecoration() {
    }

    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        this.onDraw(c, parent);
    }

    /** @deprecated */
    @Deprecated
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
    }

    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        this.onDrawOver(c, parent);
    }

    /** @deprecated */
    @Deprecated
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
    }

    /** @deprecated */
    @Deprecated
    public void getItemOffsets(@NonNull Rect outRect, int itemPosition, @NonNull RecyclerView parent) {
        outRect.set(0, 0, 0, 0);
    }

    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        this.getItemOffsets(outRect, ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition(), parent);
    }
}

我们发现,去除一些过时的方法之后只剩下三个方法,分别是onDrawgetItemOffsetsonDrawOver

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) 
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) 
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)

随便举几个例子:

(1)getItemOffsets的使用

outRect.set(0, 0, 0, mDividerHeight)

确定绘制Item的偏移量,四个参数分别代表左、上、右、下偏移量,如果设置分割线只需要配置下便宜即可,类似于将Item设置了mDividerHeight个margin值。

图片.png

具体代码如下:

/**
 * 绘制RecyclerView的分割线
 */
public class MyRecyclerViewDivider extends RecyclerView.ItemDecoration {

    private int mDividerHeight = 0;//分割线的高度


    public MyRecyclerViewDivider(int mDividerHeight){
        this.mDividerHeight = mDividerHeight;
    }

    /**
     * 实现类似margin的效果
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(0, 0, 0, mDividerHeight);
    }

}
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider(10));

效果如下:

图片.png

(2)onDraw的使用

onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)

onDraw是绘制背景,该回调方法有一个Canvas形参,Canvas可以绘制任意背景(矩形,圆形,路径,文字等等)。

您可能会考虑一个问题, RecyclerView可以设置背景,RecyclerView的Item也可以设置背景,那么这个方法到底绘制的是什么背景呢?

答: onDraw绘制的背景介于以上两者之间。(以上两者:RecyclerView背景和Item背景)

代码如下:

/**
 * 绘制RecyclerView的分割线
 */
public class MyRecyclerViewDivider extends RecyclerView.ItemDecoration {

    private Paint dividerPaint;
    private int mDividerHeight;

    public MyRecyclerViewDivider(int dividerHeight) {
        this.mDividerHeight = dividerHeight;
        dividerPaint = new Paint();
        dividerPaint.setColor(Color.BLUE);
        dividerPaint.setStyle(Paint.Style.FILL);
    }

    /**
     * 实现类似margin的效果
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(0, 0, 0, mDividerHeight);
    }

    /**
     * 添加Item的背景
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            View view = parent.getChildAt(i);
            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            float left = view.getLeft();
            float right = view.getRight();
            float top = view.getBottom() + lp.bottomMargin;
            float bottom = view.getBottom() + mDividerHeight + lp.bottomMargin;
            c.drawRect(left, top, right, bottom, dividerPaint == null ? new Paint() : dividerPaint);
        }
    }
    
}
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider(10));

效果如下:

图片.png

(3)onDrawOver的使用

onDrawOver可以在Item上方绘制(矩形,圆形,路径,文字等等),和onDraw有一定的相似之处,区别是onDraw在Item下方绘制,onDrawOver则在Item上绘制。

下面演示以下一个模拟场景:如果该项已读,那么在右边显示一个已读图片。(当然,也可以作为分割线显示在Item的上方,这里就不演示了)

代码如下:

public class MyRecyclerViewDivider extends RecyclerView.ItemDecoration {

    private Paint dividerPaint;
    private int mDividerHeight;
    private List<PhotoBean> list;
    private Context mContext;

    public MyRecyclerViewDivider(Context mContext, int dividerHeight, List<PhotoBean> list) {
        this.mDividerHeight = dividerHeight;
        dividerPaint = new Paint();
        dividerPaint.setColor(Color.BLUE);
        dividerPaint.setStyle(Paint.Style.FILL);

        this.list = list;
        this.mContext = mContext;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(0, 0, 0, mDividerHeight);
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            View child = parent.getChildAt(i);

            int position = parent.getChildAdapterPosition(child);

            PhotoBean photoBean = list.get(position);

            if(photoBean.isRead()){
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.read, options);
                float scale = child.getHeight() / 2.0f / options.outHeight;
                bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.read), (int) (scale * options.outWidth), (int) (scale * options.outWidth), true);
                Matrix matrix = new Matrix();
                matrix.setTranslate(child.getRight() * 1.0f - bitmap.getWidth() - 10, child.getTop() - (bitmap.getHeight() - child.getHeight()) / 2);
                c.drawBitmap(bitmap, matrix, dividerPaint == null ? new Paint() : dividerPaint);
            }
        }
    }
}
    mRecyclerView.addItemDecoration(new MyRecyclerViewDivider(MainActivity.this, 10, list));

效果如下:

图片.png

以上介绍了添加分割线的两种方式,下面开始介绍其它知识点。

(1)ItemDecoration的组合

您可能已经知道,RecyclerView添加分割线的代码是这样的

mRecyclerView.addItemDecoration(new MyRecyclerViewDivider1());

然而,我们可以添加多个

mRecyclerView.addItemDecoration(new MyRecyclerViewDivider1());
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider2());
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider3());

如果添加多个,不是覆盖,而是MyRecyclerViewDivider1、MyRecyclerViewDivider2、MyRecyclerViewDivider3同时存在。

(2)getItemOffsets和onDrawOver结合实现粘贴头部效果

首先看一下效果

70.gif

这种效果已经在越来越多的项目中运用。

代码如下:

/**
 * 绘制RecyclerView的分割线
 */
public class MyRecyclerViewDivider extends RecyclerView.ItemDecoration {

    private Paint dividerPaint;
    private int mDividerHeight;
    private List<PhotoBean> list;
    private Context mContext;
    private Paint.FontMetrics fontMetrics;

    public MyRecyclerViewDivider(Context mContext, int dividerHeight, List<PhotoBean> list) {
        this.mDividerHeight = dividerHeight;
        dividerPaint = new Paint();
        dividerPaint.setColor(Color.BLUE);
        dividerPaint.setStyle(Paint.Style.FILL);
        fontMetrics = new Paint.FontMetrics();
        dividerPaint.setTextSize(50);
        fontMetrics = dividerPaint.getFontMetrics();
        this.list = list;
        this.mContext = mContext;
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(0, mDividerHeight, 0, 0);
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        int itemCount = state.getItemCount();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            int position = parent.getChildAdapterPosition(child);

            //核心代码--start---
            int left = child.getLeft();
            int right = child.getRight();
            int bottom = child.getBottom();
            int top = Math.max(mDividerHeight, child.getTop());

            if (position + 1 < itemCount) {
                if (bottom < top ) {
                    top = bottom;
                }
            }
            //核心代码--end---

            dividerPaint.setColor(Color.GRAY);
            c.drawRect(left, top - mDividerHeight, right, top, dividerPaint);
            dividerPaint.setColor(Color.parseColor("#ffffff"));
            c.drawText("类型AAAAAg", left + 10, top - fontMetrics.descent - (mDividerHeight - fontMetrics.bottom + fontMetrics.top) / 2, dividerPaint);
        }
    }
}

以上已经标注了核心代码,非核心代码按需求自由发挥。

[本章完...]

相关文章

网友评论

    本文标题:RecyclerView<第五篇>:分割线(含粘贴头

    本文链接:https://www.haomeiwen.com/subject/wkrcfqtx.html