MeterialDesign_RecyclerView_ItemDecoration的使用

MeterialDesign_RecyclerView_ItemDecoration的使用

有关于ItemDecration的用法笔记

RecyclerView没有默认的分割线,需要自己绘制

由于RecyclerView的LayoutParams的线性布局分为
- LinearLayoutManager
- GridLayoutManager
- StaggeredGridLayoutManager

所以针对这几种布局写对应的分割线
- 线性布局对应分割线
- 网格布局对应分割线

线性布局对应分割线

1. 继承RecyclerView.ItemDecoration

在这里会提示需要重写==getItemOffsets==方法和==onDraw==方法。
- ==getItemOffsets==方法的作用是设置列表中每个Item的偏移量。
- ==onDraw==方法的作用是绘画出每个Item对应分割线。

/**
 * Author:Yang Jianru
 * Time: 2017/9/7  17:18
 * Description: LinearLayoutManager 对应的RecyclerView的分割线
 */
public class DeviderItemDecoration extends RecyclerView.ItemDecoration{

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

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

}

2.编写构造函数

将LinearLayoutManager对应的方向传递进来,并判断是否属于LinearLayoutManager中的枚举。

而且要在构造函数中初始化分割线。


    /**
     * 默认RecyclerView的布局是垂直分布
     */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private Drawable mDevider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DevideItemDecoration(Context mCtx, int orientation){
    //  获取attr获取
    //  TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
    //  mDevider = typedArray.getDrawable(0);
    //  typedArray.recycle();
        setmOrientation(orientation);
        //获取分割线图片
        mDevider = mCtx.getResources().getDrawable(R.drawable.shape_devider);
    }

    public void setmOrientation(int mOrientation) {
        if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("DevideItemDecoration中Orientation非水平和线性的枚举类型");
        }
        this.mOrientation = mOrientation;
    }

3.重写getItemOffsets方法

该方法中只用设置列表对应Item的矩形的偏移量,即给方法中第一个参数“Rect outRect”坐标设置指定值

每个Item在绘制过程中都会调用一遍这个方法。

outRect.set(left, top, right, bottom)

left、top、 right、 bottom 代表各个方向的偏移量,当然现在在编写LinearLayoutManager 对应的RecyclerView的分割线,那就只用在横向或者纵向添加分割线

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

4.重写onDraw方法

onDraw方法其实就是在画布中把分割线描绘出来,需要判断布局方向和计算出分割线的位置。

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.VERTICAL){
            //TODO:未判断时候是否为最后一个item,最后一个item的分割线不用画
            drawVertical(c,parent);
        }else{
            drawHorizontal(c,parent);
        }
    }

    /**
     * 画横向的线
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bootom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + mDevider.getIntrinsicWidth();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * 画纵向的线
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

4.DeviderItemDecoration完整代码

使用方式

        RecyclerView.addItemDecoration(new DevideItemDecoration(this,LinearLayoutManager.HORIZONTAL));
/**
 * Author:Yang Jianru
 * Time: 2017/9/7  17:18
 * Description: LinearLayoutManager 对应的RecyclerView的分割线
 */
public class DeviderItemDecoration extends RecyclerView.ItemDecoration{

    /**
     * 默认RecyclerView的布局是垂直分布
     */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private Drawable mDevider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DeviderItemDecoration(Context mCtx, int orientation){
//        TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
//        mDevider = typedArray.getDrawable(0);
//        typedArray.recycle();
//        mDevider = BitmapFactory.decodeResource(mCtx.getResources(),R.drawable.shapeLine);
        setmOrientation(orientation);
        mDevider = mCtx.getResources().getDrawable(R.drawable.shape_line);
    }

    public void setmOrientation(int mOrientation) {
        if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("DevideItemDecoration中Orientation非水平和线性的枚举类型");
        }
        this.mOrientation = mOrientation;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        //1.调用此方法(首先会先获取条目之间的间隙宽度---Rect矩形区域)
        // 获得条目的偏移量(所有的条目都回调用一次该方法)
        if (mOrientation == LinearLayoutManager.VERTICAL){
            outRect.set(0,0,0,mDevider.getIntrinsicHeight());
        }else{
            outRect.set(0,0,mDevider.getIntrinsicWidth(),0);
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.VERTICAL){
            drawVertical(c,parent);
        }else{
            drawHorizontal(c,parent);
        }
    }

    /**
     * 画横向的线
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bootom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + mDevider.getIntrinsicWidth();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * 画纵向的线
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }
}

表格布局对应分割线

表格布局对应分割线其实和线性布局类似,区别在于表格布局中再重写getItemOffsets方法。

1.表格布局的getItemOffsets方法

在该方法中设置偏移距离。之前在线性布局中只用偏移横向和纵向,在表格布局中大部分item横向纵向都需要需要偏移,最后一列item只用偏移横向,最有一行item只用偏移纵向。

这里需要注意的是由于我们要计算是否为最后一行 或者 最后一列 所以需要参数有itemPosition的那个方法。(getItemOffsets总共有两个这样的方法,有itemPosition参数的是过时的方法。)

@Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        super.getItemOffsets(outRect, itemPosition, parent);
        int bottom = mDevider.getIntrinsicWidth();
        int right = mDevider.getIntrinsicHeight();
        if (isLastColum(parent,itemPosition)){
            right = 0;
        }
        if (isLastRow(parent,itemPosition)){
            bottom = 0;
        }

        outRect.set(0,0,right,bottom);
    }

    /**
     * 是否是最后一行
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastRow(RecyclerView parent, int itemPosition) {
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int childCount = parent.getAdapter().getItemCount();
            int rowCount = childCount%spanCount == 0 ? childCount/spanCount : childCount/spanCount + 1 ;
            itemPosition++;
            int currentRow= itemPosition%spanCount == 0 ? itemPosition/spanCount : itemPosition/spanCount + 1 ;
            if(currentRow == rowCount){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否为最后一行
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastColum(RecyclerView parent, int itemPosition) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int spanCount = getSpanCount(parent);
            Log.d("isLastColum", itemPosition + "----------" +spanCount);
            if ((itemPosition + 1)%spanCount == 0){
                Log.d("isLastColum", "---true-------");
                return true;
            }
        }
        Log.d("isLastColum", "---false-------");
        return false;
    }

    //得到列数
    private int getSpanCount(RecyclerView parent){
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
            GridLayoutManager lm = (GridLayoutManager)layoutManager;
            int spanCount = lm.getSpanCount();
            return spanCount;
        }
        return 0;
    }

2.表格布局的onDraw方法

描绘分割线,描绘横向和纵向的分割线。

//横向还没做
new DevideGridItemDecoration(this,GridLayoutManager.VERTICAL);
@Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        drawVertical(c,parent);
        drawHorizontal(c,parent);
    }

    /**
     * 画横向的线
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getLeft() - layoutParams.leftMargin;
            int top = childView.getBottom() + layoutParams.bottomMargin;
            int right = childView.getRight() + layoutParams.rightMargin;
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * 画纵向的线
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() +  layoutParams.rightMargin;
            int top = childView.getTop() - layoutParams.topMargin;
            int right = left + mDevider.getIntrinsicWidth();
            int bootom = childView.getBottom() + layoutParams.bottomMargin;
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

3.DeviderGridItemDecoration代码

表格布局的横向没有处理类似效果,不做了。。。有时间在说吧

/**
 * Author:Yang Jianru
 * Time: 2017/9/7  17:18
 * Description: GridLayoutManager 对应的RecyclerView的分割线
 */
public class DevideGridItemDecoration extends RecyclerView.ItemDecoration{

    /**
     * 默认RecyclerView的布局是垂直分布
     */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private Drawable mDevider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DevideGridItemDecoration(Context mCtx, int orientation){
//        TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
//        mDevider = typedArray.getDrawable(0);
//        typedArray.recycle();
        setmOrientation(orientation);
        mDevider = mCtx.getResources().getDrawable(R.drawable.shape_line);
    }

    public void setmOrientation(int mOrientation) {
        if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("DevideItemDecoration中Orientation非水平和线性的枚举类型");
        }
        this.mOrientation = mOrientation;
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        super.getItemOffsets(outRect, itemPosition, parent);
        int bottom = mDevider.getIntrinsicWidth();
        int right = mDevider.getIntrinsicHeight();
        if (mOrientation == GridLayoutManager.VERTICAL){
            if (isLastColum(parent,itemPosition)){
                right = 0;
            }
            if (isLastRow(parent,itemPosition)){
                bottom = 0;
            }
        }else{
            if (isLastColum(parent,itemPosition)){
                Log.d("isLastColum", "---bottom = 0;------");
                bottom = 0;
            }
            if (isLastRow(parent,itemPosition)){
                right = 0;
            }
        }

        outRect.set(0,0,right,bottom);
    }

    /**
     * 是否是最后一行
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastRow(RecyclerView parent, int itemPosition) {
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int childCount = parent.getAdapter().getItemCount();
            int rowCount = childCount%spanCount == 0 ? childCount/spanCount : childCount/spanCount + 1 ;
            itemPosition++;
            int currentRow= itemPosition%spanCount == 0 ? itemPosition/spanCount : itemPosition/spanCount + 1 ;
            if(currentRow == rowCount){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否为最后一行
     * @param parent
     * @param itemPosition
     * @return
     */
    private boolean isLastColum(RecyclerView parent, int itemPosition) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager){
            int spanCount = getSpanCount(parent);
            Log.d("isLastColum", itemPosition + "----------" +spanCount);
            if ((itemPosition + 1)%spanCount == 0){
                Log.d("isLastColum", "---true-------");
                return true;
            }
        }
        Log.d("isLastColum", "---false-------");
        return false;
    }

    //得到列数
    private int getSpanCount(RecyclerView parent){
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager){
            GridLayoutManager lm = (GridLayoutManager)layoutManager;
            int spanCount = lm.getSpanCount();
            return spanCount;
        }
        return 0;
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
//        if (mOrientation == LinearLayoutManager.VERTICAL){
//            drawVertical(c,parent);
//            drawHorizontal(c,parent);
//        }else{
//        }
        drawVertical(c,parent);
        drawHorizontal(c,parent);
    }

    /**
     * 画横向的线
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {

        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getLeft() - layoutParams.leftMargin;
            int top = childView.getBottom() + layoutParams.bottomMargin;
            int right = childView.getRight() + layoutParams.rightMargin;
            int bootom = top + mDevider.getIntrinsicHeight();
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }

    /**
     * 画纵向的线
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();
        for (int i = 0 ; i < childCount ; i++){
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() +  layoutParams.rightMargin;
            int top = childView.getTop() - layoutParams.topMargin;
            int right = left + mDevider.getIntrinsicWidth();
            int bootom = childView.getBottom() + layoutParams.bottomMargin;
            mDevider.setBounds(left,top,right,bootom);
            mDevider.draw(c);
        }
    }
}

其他用法

  • 在控件上画水印
  • 在控件侧面添加标记
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值