RecyclerView 并没有 divider 属性,但是我们可以通过 RecyclerView 的 addItemDecoration() 来添加分割线,该方法参数为 RecyclerView.ItemDecoration。
介绍
当 RecyclerView 添加 ItemDecoration 后,RecyclerView 在绘制每个 item 的时候,会去绘制 decorator,也就是会调用 ItemDecoration 的 onDraw() 和 onDrawOver() 方法。
RecyclerView.ItemDecoration 是抽象类,主要提供三个方法:
- onDraw(Canvas c, RecyclerView parent, State state): 在绘制item(drawChild) 前调用
- onDrawOver(Canvas c, RecyclerView parent, State state): 在绘制item(drawChild) 后调用
- getItemOffsets(Rect outRect, View view, RecyclerView parent, State state):outRect设置 item 的偏移量,用于绘制 decorator(也就是divider)
关于 getItemOffsets 函数:
RecyclerView 添加分割线,实际上就是 RecyclerView 的 item 之间添加了用作分割线的View,自然而然后续的 item 就会有偏移量,所以用 getItemOffsets 中的 outRect 来保存 item 的偏移量,从而便于绘制 decorator。
实现
实际上在当前版本的 RecyclerView (25.3.1)
中已经有 ItemDecoration
关于分割线的默认实现类 DividerItemDecoration
package android.support.v7.widget;
import android.annotation.SuppressLint;
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.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.widget.LinearLayout;
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
// 如果不设置,则默认的分割线为 android.R.attr.listDivider 指定的 drawable
private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
private Drawable mDivider;
/**
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
private int mOrientation;
private final Rect mBounds = new Rect();
/**
* Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
* {@link LinearLayoutManager}.
*
* @param context Current context, it will be used to access resources.
* @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
/**
* Sets the orientation for this divider. This should be called if
* {@link RecyclerView.LayoutManager} changes orientation.
*
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
*/
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException(
"Invalid orientation. It should be either HORIZONTAL or VERTICAL");
}
mOrientation = orientation;
}
/**
* Sets the {@link Drawable} for this divider.
*
* @param drawable Drawable that should be used as a divider.
*/
public void setDrawable(@NonNull Drawable drawable) {
if (drawable == null) {
throw new IllegalArgumentException("Drawable cannot be null.");
}
mDivider = drawable;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null) {
return;
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
// 绘制 RecyclerView 为垂直布局时的分割线,此时分割线为水平分割线
@SuppressLint("NewApi")
private void drawVertical(Canvas canvas, RecyclerView parent) {
canvas.save();
final int left;
final int right;
// 需要考虑clipToPadding的boolean值
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
}
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
parent.getDecoratedBoundsWithMargins(child, mBounds);
final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
final int top = bottom - mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
canvas.restore();
}
// 绘制 RecyclerView 为水平布局时的分割线,此时分割线为垂直分割线
@SuppressLint("NewApi")
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
canvas.save();
final int top;
final int bottom;
// 需要考虑clipToPadding的boolean值
if (parent.getClipToPadding()) {
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
canvas.clipRect(parent.getPaddingLeft(), top,
parent.getWidth() - parent.getPaddingRight(), bottom);
} else {
top = 0;
bottom = parent.getHeight();
}
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));
final int left = right - mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
canvas.restore();
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (mOrientation == VERTICAL) { // 垂直方向的RecyclerView, item 的 bottom 偏移量 = 分割线高度
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else { // 水平方向的RecyclerView, item 的 right 偏移量 = 分割线宽度
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
在代码中添加:
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), mLayoutManager.getOrientation()));
就有了分割线。 默认的分割线效果是系统自带的 listDivider
的效果,我们也可以在主题配置文件中自定义全局的分割线,或者调用 setDivider
为每个 RecyclerView
设置单独的分割线。
小结
总的来说,目前添加分割线只需要使用 recyclerview-v7
包下的 DividerItemDecoration
类即可,分割线可以通过 setDivider 来个性化指定,也可以通过配置主题中的 android:listDivider
来全局指定。
例如:
RecyclerView mRecyclerView;
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
这个能满足一般需求,但是如果我想使用自定义的分割线呢,看以下代码
DividerItemDecoration divider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
divider.setDrawable(ContextCompat.getDrawable(this, R.drawable.divider));
mRecyclerView.addItemDecoration(divider);
package com.example.administrator.myapplication;
import android.content.ContextWrapper;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.RecyclerView;
import java.util.List;
import java.util.ArrayList;
public class BodyActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_body);
//1.获取控件
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//设置布局方式
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));//线性布局
// mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));//网格布局
mRecyclerView.setHasFixedSize(true); //是否重新计算大小
//3.准备数据
List<News> newsList = new ArrayList<>();
News news;
for (int i = 1; i <= 20; i++) {
news = new News();
news.setNewsTitle("java是世界上最好的语言" + i);
news.setNewsSource("新华网" + i);
news.setPublishTime("2018-8-6");
newsList.add(news);
}
//添加Android自带的分割线
// mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
//添加自定义的分割线
DividerItemDecoration divider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
divider.setDrawable(ContextCompat.getDrawable(this, R.drawable.divider));
mRecyclerView.addItemDecoration(divider);
//4.准备适配器
NewsAdapter newsAdapter = new NewsAdapter(newsList);
mRecyclerView.setAdapter(newsAdapter);
}
}
标签:parent,int,分割线,添加,import,android,RecyclerView From: https://blog.51cto.com/u_4387387/6078238