当前位置 博文首页 > 启舰:自定义控件三部曲视图篇(五)——RecyclerView系列之二It

    启舰:自定义控件三部曲视图篇(五)——RecyclerView系列之二It

    作者:[db:作者] 时间:2021-06-30 12:42

    从来不跌倒不算光彩,每次跌倒后能再站起来,才是最大的荣耀。


    系列文章: Android自定义控件三部曲文章索引: http://blog.csdn.net/harvic880925/article/details/50995268


    一、添加分割线

    1.1 引入ItemDecoration

    在上一篇中,我们讲解了RecyclerView的基本使用方法,但有个问题:为什么Item之间没有分割线呢?其实,给RecyclerView添加分割线也非常简单,只需要添加上一句话:

    DividerItemDecoration  mDivider = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
    mRecyclerView.addItemDecoration(mDivider);
    

    完整的代码如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_linear);
        
        generateDatas();
        RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.linear_recycler_view);
    
        //线性布局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(linearLayoutManager);
    
        //初始化分隔线、添加分隔线
        DividerItemDecoration mDivider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mDivider);
    
        RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
        mRecyclerView.setAdapter(adapter);
    }
    

    这里实现的效果如下图所示:
    在这里插入图片描述
    可以看到,这里只需要添加一句: mRecyclerView.addItemDecoration(mDivider);就可以给底部添加了一条横线,那什么是ItemDecoration呢?
    首先,我们肯定理解什么是Item,在这个布局中,每个Item都单独占一行,在没加ItemDecoration时,下面红线框中就是一个Item:
    在这里插入图片描述

    1.2 什么是ItemDecoration

    那ItemDecoration与Item是什么关系呢?对于英语来讲,Decoration是装饰的意思,ItemDecoration就是Item的装饰。在Item的四周,我们可以给它添加上自定义的装饰,比如刚才的横线,就是在底部给它添加一个横线的装饰。同样的,我们也可以在Item的上下左右各添加装饰,而且这些装饰是允许我们自定义的。系统只给我们提供了一个现成的Decoration类就是刚才使用的DividerItemDecoration,如果我们想实现其它的装饰效果,就需要自定义了。下面这些漂亮的效果都可以使用自定义ItemDecoration来实现:
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

    2、 自定义ItemDecoration

    2.1 getItemOffsets

    2.1.1 getItemOffsets的意义

    当我们要重写ItemDecoration时,主要涉及到三个函数:

    public class LinearItemDecoration extends RecyclerView.ItemDecoration {
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
        }
    
        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
        }
    }
    

    这三个函数我们会逐个讲解,首先,我们来看看getItemOffsets:
    getItemOffsets的主要作用就是给item的四周加上边距,实现的效果类似于margin,将item的四周撑开一些距离,在撑开这些距离后,我们就可以利用上面的onDraw函数,在这个距离上进行绘图了。在了解了getItemOffsets的作用之后,我们来看看这个函数本身:
    getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)

    • Rect outRect:这个是最难理解的部分,outRect就是表示在item的上下左右所撑开的距离,后面详细讲解。
    • View view:是指当前Item的View对象
    • RecyclerView parent: 是指RecyclerView 本身
    • RecyclerView.State state:通过State可以获取当前RecyclerView的状态,也可以通过State在RecyclerView各组件间传递参数,具体的文档,大家可以参考:https://developer.android.com/reference/android/support/v7/widget/RecyclerView.State

    下面我们专门来看看Rect outRect这个参数,outRect 中的 top、left、right、bottom四个点,并不是普通意义的坐标点,而是指的在Item上、左、右、下各撑开的距离,这个值默认是0,示意图如下所示:
    在这里插入图片描述

    2.1.2 getItemOffsets示例

    我们知道,想要实现分隔线,有一种方法是在Item的上方空出一像素的间隔,这样就会漏出底线,看起来就是分割线了。所以我们回到刚才的示例,去掉DividerItemDecoration,改为自定义的LinearItemDecoration。
    首先,给整个Activity添加上一个红色的背景色:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ff0000"
        tools:context=".LinearActivity">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/linear_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </android.support.v7.widget.RecyclerView>
    
    </LinearLayout>
    

    之后,给每个Item添加上默认的背景色白色,这样有白色的地方就不会透出背景色的红色了,而没有白色的地方就会露出红色:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:background="@android:color/white"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/item_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp" />
    
    </LinearLayout>
    

    然后就是自定义LinearItemDecoration :

    public class LinearItemDecoration extends RecyclerView.ItemDecoration {
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
        }
    
        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            outRect.top=1;
        }
    }
    

    在这里,我们将item上方面所撑开的距离硬编码为1px;

    最后,将LinearItemDecoration添加进RecyclerView:

    public class LinearActivity extends AppCompatActivity {
        private ArrayList<String> mDatas = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_linear);
    
            generateDatas();
            RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.linear_recycler_view);
    		…………
            //添加分隔线
            mRecyclerView.addItemDecoration(new LinearItemDecoration());
    
            RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
            mRecyclerView.setAdapter(adapter);
        }
        //其它代码,参考源码
    	…………
    }
    

    效果如下图所示:
    在这里插入图片描述
    可以看到每个Item的上方都出现了一条红线。尤其从第一个Item可以看出来。
    同样的,如果我们改为底部1px,左侧50px,右侧100px:

    public class LinearItemDecoration extends RecyclerView.ItemDecoration {
    	…………
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            outRect.left=50;
            outRect.right=100;
            outRect.bottom=1;
        }
    }
    

    看下效果:
    在这里插入图片描述
    从第一个Item可以看出,顶部是没有红线的,因为我们没有设置outRect.top,所以它默认是0,因为outRect.right=100,而outRect.left=50,明显可以看出右侧的红色宽度是左侧的两倍。

    2.2 onDraw

    2.2.1 onDraw的用法

    在理解了getItemOffsets的用法以后,我们再来看看onDraw函数:

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

    onDraw函数有三个参数,RecyclerView parent, RecyclerView.State state的意义与getItemOffsets的相同。而

    • Canvas c: 是指通过getItemOffsets撑开的空白区域所对应的画布,通过这个canvas对象,可以在getItemOffsets所撑出来的区域任意绘图。

    那这个就厉害了,我们知道Canvas是有非常丰富的绘图函数的,那我们先来个简单的,通过getItemOffsets将Item的左侧撑出来150的距离,然后在中间画一个圆形:

    public class LinearItemDecoration extends RecyclerView.ItemDecoration {
        private Paint mPaint;
        public LinearItemDecoration(){
            mPaint = new Paint();
            mPaint.setColor