RecyclerView中绘制不相同的分割线

博客内容介绍了如何在RecyclerView中绘制不相同的分割线,包括水平线和垂直线。通过自定义drawable资源和使用TypedArray,可以实现不同列表或列表内不同风格的分割线,而不仅仅是修改系统的listDivider。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在绘制RecyclerView的分割线时我们通常是设置系统属性的android:listDivider为自定义的样式,然后再通过TypedArray获取一个drawable对象,通过回执drawable对象来绘制分割线。例如:

package com.example.admin.bannerstudy.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;

/**
 * 适用于RecyclerView.setLayoutManager(new GridLayoutManager());
 *
 * 主要是测试怎么画分割线,没有考虑最后一行和最后一列不要画线的情况
 *
 */
public class GridItemDecorationExample extends RecyclerView.ItemDecoration {

    public static final int[] ATTRS = new int[] {android.R.attr.listDivider};

    public static final int HORIZONTAL_LIST = LinearLayout.HORIZONTAL;
    public static final int VERICAL_LIST = LinearLayout.VERTICAL;

    private Drawable mDivider;
    private int mOrientation = VERICAL_LIST;

    public GridItemDecorationExample(Context context) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    public GridItemDecorationExample(Context context, int orientation) {
        this(context);
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if(orientation != HORIZONTAL_LIST && orientation != VERICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

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

    private int getSpanCount (RecyclerView parent) {
        int spanCount = -1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager) {
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }

    // 绘制水平线,drawVertical表示每条线是垂直分布的
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount  = parent.getChildCount();

        if(mOrientation == VERICAL_LIST) {  // 当设置列表垂直排布时
            // 获取列表的总行数
            int rows = (int)Math.ceil(((double)childCount / (double)getSpanCount(parent)));
            for(int i = 0; i < rows; i++) {
                // 取到每行的第一个childView
                View childView = parent.getChildAt(i * getSpanCount(parent));
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int top = childView.getBottom() + params.bottomMargin;
                int bottom = top + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        } else if(mOrientation == HORIZONTAL_LIST) {    // 但设置列表水平排布时
            // 获取列表的总行数
            int rows = getSpanCount(parent) >= childCount ? childCount : getSpanCount(parent);
            for(int i = 0; i < rows; i++) {
                // 取到每行的第一个childView
                View childView = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int top = childView.getBottom() + params.bottomMargin;
                int bottom = top + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }


    }

    // 绘制垂直线,drawHorizental表示每条线是水平分布的
    public void drawHorizental(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        if(mOrientation == VERICAL_LIST) {  // 当设置列表垂直排布时
            // 获取列表的总列数
            int clumn = getSpanCount(parent) >= childCount ? childCount : getSpanCount(parent);
            for(int i = 0; i < clumn; i++) {
                // 取到每列的第一个childView
                View childView = parent.getChildAt(i);
                RecyclerView v = new RecyclerView(parent.getContext());
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int left = childView.getRight() + params.rightMargin;
                int right = left + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        } else if(mOrientation == HORIZONTAL_LIST) {    // 但设置列表水平排布时
            // 获取列表的总列数
            int clumn  = (int)Math.ceil(((double)childCount / (double)getSpanCount(parent)));
            for(int i = 0; i < clumn; i++) {
                // 取到每列的第一个childView
                View childView = parent.getChildAt(i * getSpanCount(parent));
                RecyclerView v = new RecyclerView(parent.getContext());
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int left = childView.getRight() + params.rightMargin;
                int right = left + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
    }


    // 用来设置item的左上右下的间距用于回执分割线
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.set(0, 0, mDivider.getIntrinsicHeight(),mDivider.getIntrinsicHeight());
    }
}




values/styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:listDivider">@drawable/divider_bg1</item>
    </style>

</resources>



drawable/divider_bg1.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#ffff0000"></solid>

    <size android:height="4dp"></size>
</shape>


此处指设置了高度为4dp,没有设置宽度的值,所以在GridItemDecorationExample中通过拿到的drawable来获取固有宽高时:

getIntrinsicHeight()返回的是此处设置的高度4dp-->??px;

getIntrinsicWidht()返回的则是-1;



activity

        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));;
        mRecyclerView.setAdapter(adapter);
        mRecyclerView.addItemDecoration(new GridItemDecorationExample(this,DividerGridItemDecoration.VERICAL_LIST));





这样实现了自定义的分割线,但是只有一种风格,而且是通过修改系统的listDivider属性来设置的,那么要是在一个工程中不同的列表需要不同的分割线或者在

同一个列表中要实现水平和垂直分割线不一样了?

这样我觉得修改系统的listDivider就无法满足了,那么就只有通过另外的渠道设置了;



package com.example.admin.bannerstudy.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;

import com.example.admin.bannerstudy.R;

import static com.example.admin.bannerstudy.custom.DividerGridItemDecoration.ATTRS;

/**
 * 适用于RecyclerView.setLayoutManager(new GridLayoutManager());
 *
 * 主要是测试怎么画分割线,没有考虑最后一行和最后一列不要画线的情况
 *
 */
public class GridItemDecorationExample extends RecyclerView.ItemDecoration {

//    public static final int[] ATTRS = new int[] {android.R.attr.listDivider};

    public static final int[] ATTRS1 = new int[] {R.attr.a_divider};

    public static final int HORIZONTAL_LIST = LinearLayout.HORIZONTAL;
    public static final int VERICAL_LIST = LinearLayout.VERTICAL;

//    private Drawable mDivider;
    private Drawable mDivider1;
    private Drawable mDivider2;

    private int mOrientation = VERICAL_LIST;

    public GridItemDecorationExample(Context context) {
//        final TypedArray a = context.obtainStyledAttributes(ATTRS);
//        mDivider = a.getDrawable(0);

        final TypedArray a1 = context.obtainStyledAttributes(R.style.Divider1,ATTRS1);
        mDivider1 = a1.getDrawable(0);
        final TypedArray a2 = context.obtainStyledAttributes(R.style.Divider2,ATTRS1);
        mDivider2 = a2.getDrawable(0);

        a1.recycle();
	a2.recycle();
    }

    public GridItemDecorationExample(Context context, int orientation) {
        this(context);
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if(orientation != HORIZONTAL_LIST && orientation != VERICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

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

    private int getSpanCount (RecyclerView parent) {
        int spanCount = -1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if(layoutManager instanceof GridLayoutManager) {
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }

    // 绘制水平线,drawVertical表示每条线是垂直分布的
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount  = parent.getChildCount();

        if(mOrientation == VERICAL_LIST) {  // 当设置列表垂直排布时
            // 获取列表的总行数
            int rows = (int)Math.ceil(((double)childCount / (double)getSpanCount(parent)));
            for(int i = 0; i < rows; i++) {
                // 取到每行的第一个childView
                View childView = parent.getChildAt(i * getSpanCount(parent));
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int top = childView.getBottom() + params.bottomMargin;
                int bottom = top + mDivider1.getIntrinsicHeight();
                mDivider1.setBounds(left, top, right, bottom);
                mDivider1.draw(c);
            }
        } else if(mOrientation == HORIZONTAL_LIST) {    // 但设置列表水平排布时
            // 获取列表的总行数
            int rows = getSpanCount(parent) >= childCount ? childCount : getSpanCount(parent);
            for(int i = 0; i < rows; i++) {
                // 取到每行的第一个childView
                View childView = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int top = childView.getBottom() + params.bottomMargin;
                int bottom = top + mDivider1.getIntrinsicHeight();
                mDivider1.setBounds(left, top, right, bottom);
                mDivider1.draw(c);
            }
        }


    }

    // 绘制垂直线,drawHorizental表示每条线是水平分布的
    public void drawHorizental(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        if(mOrientation == VERICAL_LIST) {  // 当设置列表垂直排布时
            // 获取列表的总列数
            int clumn = getSpanCount(parent) >= childCount ? childCount : getSpanCount(parent);
            for(int i = 0; i < clumn; i++) {
                // 取到每列的第一个childView
                View childView = parent.getChildAt(i);
                RecyclerView v = new RecyclerView(parent.getContext());
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int left = childView.getRight() + params.rightMargin;
                int right = left + mDivider2.getIntrinsicWidth();
                mDivider2.setBounds(left, top, right, bottom);
                mDivider2.draw(c);
            }
        } else if(mOrientation == HORIZONTAL_LIST) {    // 但设置列表水平排布时
            // 获取列表的总列数
            int clumn  = (int)Math.ceil(((double)childCount / (double)getSpanCount(parent)));
            for(int i = 0; i < clumn; i++) {
                // 取到每列的第一个childView
                View childView = parent.getChildAt(i * getSpanCount(parent));
                RecyclerView v = new RecyclerView(parent.getContext());
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
                int left = childView.getRight() + params.rightMargin;
                int right = left + mDivider2.getIntrinsicWidth();
                mDivider2.setBounds(left, top, right, bottom);
                mDivider2.draw(c);
            }
        }
    }


    // 用来设置item的左上右下的间距用于绘制分割线
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.set(0, 0, mDivider2.getIntrinsicWidth(),mDivider1.getIntrinsicHeight());
    }
}

mDivider1是水平线的drawble,mDivider2是垂直线的drawable;


values/styles.xml

<resources>



    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

        <attr name="a_divider" format="reference"></attr>

    <style name="Divider1">
        <item name="a_divider">@drawable/divider_horizental</item>
    </style>
    <style name="Divider2">
        <item name="a_divider">@drawable/divider_vertical</item>
    </style>


</resources>

我们自定义了一个属性"a_divider" 值类型是资源文件;创建两个样式Divider1和Divider2,给属性"a_divider"分别赋值为两个不一样的drawable文件


drawable/divider_horizental.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#ffff0000"></solid>
    <size android:height="4dp"></size>
</shape>
设置的是高


drawable/divider_vertical.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#ff00ff00"></solid>
    <size android:width="4dp"></size>
</shape>
设置的是宽





可以看出主要的区别在于一个是从系统主题中获取attrs属性,一个是从资源文件中定义的sytle中读取属性,也即调用的TypedArray构造方法不一样

obtainStyledAttributes(@StyleableRes int[] attrs)	//从系统主题中获取attrs中的属性

obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs)	//从资源文件定义的style中读取属性


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值