在绘制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());
}
}
<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>
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中读取属性