美文网首页高级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