RecyclerView分割线

本文详细介绍了如何在RecyclerView中绘制分割线,包括两种方法:通过paint进行垂直/水平布局的分割线绘制,以及通过修改系统主题实现。内容涵盖RecyclerView.ItemDecoration原理,onDraw()和getItemOffsets()的使用,以及具体的Java类和XML布局文件示例。
摘要由CSDN通过智能技术生成
目录:
        1.概述
        2.分割线绘制方式与原理
        3.通过paint绘制垂直布局/ 水平布局 分割线
        4. 通过修改系统主题绘制垂直布局/ 水平布局 分割线

 1.概述
         RecyclerView不像ListView可以通过android:divider=""布局属性来设置分割线,它需要我们去继承RecyclerView.ItemDecoration类去自己绘制。
2.分割线绘制方式与原理
        2.1 走近RecyclerView.ItemDecoration
先来看看我要用到的3个方法
   
   
/*
* 该方法实现通过canvas参数绘制分割线
* */
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state){
 
}
 
/*
* 该方法与上一方法雷同,实现通过canvas参数绘制分割线,
* 与onDraw()不同的点在于onDrawOver()是在item view绘制之后调用
* */
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
 
/*
* 设置item偏移,在item之间添加分割线,后面一个item就需要往后偏移分割线的宽度
* */
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
   这三个方法中onDraw()和onDrawOver()基本作用基本相同,就是绘制我们的分割线,区别在于 onDraw()在item view绘制之前调用,而 onDrawOver()在item View绘制之后调用;最后剩下的getItemOffsets()的作用主要是设置分割线的高度,设置item的偏移值。
         
        2.2 onDraw()的中的分割线绘制
    其实分割线的绘制就是绘制一个稍微细一点的矩形而已,我们已知的实现方式就是通过画笔(Paint)绘制,或者绘制定义好的图片。而在布局中绘制的时候要根据每一条分割线的不同位置,来计算出每一条分割线的上下左右坐标点值,最终通过draw()绘制。我们绘制的难点其实主要就是计算每条分割线的上下左右坐标。我抽取了一段通过Paint绘制垂直布局分割线的代码:
   
   
/*
* paint绘制垂直方向布局的分割线
* */
private void drawVerticalDivider(Canvas c, RecyclerView parent) {
//计算item布局左边的x坐标没,也就是分割线的左边x坐标
final int left=parent.getPaddingLeft();
//计算item右边的x坐标,也就是分割线的右边x坐标
final int right=parent.getMeasuredWidth()-parent.getPaddingRight();
//获取item数量
final int item_num=parent.getChildCount();
 
//循环绘制item分割线
for (int i=0;i<item_num;i++){
View child_item=parent.getChildAt(i);
 
//获取到当前item的布局参数
RecyclerView.LayoutParams layoutParams= (RecyclerView.LayoutParams) child_item.getLayoutParams();
 
//绘制分割线的起始y坐标
int top=child_item.getBottom()+layoutParams.bottomMargin;
//绘制分割线底部y坐标
int bottom=top+divider_size;
 
//开始绘制
c.drawRect(left,right,top,bottom,paint);
}
}
    可以看到垂直方向的分割线绘制,左边起点坐标值left就等于父组件也就是RecyclerView左内边距,右边结束坐标就是RecyclerView宽度减去右内边距,这两个都比较简单,而下面分割线顶部开始位置坐标值和底部结束坐标值的计算就相对来说要难一点了,他需要我们根据item不同的位置去计算。
    顶部和底部的计算,首先我们要获取到RecyclerView有多少个子item项,然后根据不同的item获取他们相对的布局参数以此来计算top和bottom的值。恩,大概就是这样,不晓得描述清楚没有。
       
    2.3 getItemOffsets()设置偏移
getItemOffsets()相当于设置绘制分割线后,下一item的偏移值,水平布局的RecyclerView即为向右偏移一个分割线的宽度;垂直布局的RecyclerView即为向下偏移一个分割线的宽度,即set ( int  left,  int  top,  int  right,  int  bottom) 设置为set(0,0,divider_size,0)以及 set(0,0,0,divider_size)
 
3.通过paint绘制垂直布局/水平布局分割线
    3.1 效果截图
         
          (1) 不设置分割线效果                                                        (2)设置分割线的效果

    3.2 主布局文件activity_main.xml
    
    
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
 
<android.support.v7.widget.RecyclerView
android:id="@+id/divider_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
 
</android.support.v7.widget.RecyclerView>
</LinearLayout>

    3.3 单个数据项布局文件divider_item.xml
    
    
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@color/colorAccent"
android:layout_height="match_parent">
<TextView
android:id="@+id/item_text"
android:textSize="20dp"
android:textColor="#fff"
android:gravity="center"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="88dp" />
</RelativeLayout>

    3.4 主布局Java类DividerItemActivity.java
    
    
package com.example.recyclerviewdemo;
 
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
 
/**
* Created by elimy on 2017-01-02.
*/
public class DividerItemActivity extends AppCompatActivity {
 
private RecyclerView recyclerView;
private DecorationRecycViewAdapter adapter;
private String[] array=new String[]{"功高成怨府,权盛是危机。",
"春江秋月冬冰雪,不听陈言只听天。","不薄今人爱古人,清词丽句必为邻。",
"只看后浪催前浪,当悟新人胜旧人。","纵横正有凌云笔,俯仰随人亦可怜。",
"寅父犹能畏后生,丈夫未可轻少年。","文章自得方为贵,衣钵相传岂是真",
"近水楼台先得月,向阳花木易为春。","大直若屈,大巧若拙,大辩若讷。",
"昔我往矣,杨柳依依;今我来思,雨雪霏霏。","尔曹身为名俱灭,不废江河万古流。",
"请君莫奏前朝曲,听唱新翻杨柳枝。","年年岁岁花相似,岁岁年年人不同。",
"须教自我胸中出,切忌随人脚后行。","为人性僻耽佳句,语不惊人死不休。"
};
 
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_divider);
 
recyclerView= (RecyclerView) findViewById(R.id.divider_recycler_view);
 
//初始化布局管理器
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
//设置布局
recyclerView.setLayoutManager(linearLayoutManager);
 
//设置adapter
adapter=new DecorationRecycViewAdapter(this,array);
recyclerView.setAdapter(adapter);
 
//添加分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.VERTICAL));
}
 
}

    3.5 分割线绘制类DividerItemDecoration.java
    
    
package com.example.recyclerviewdemo;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
 
/**
* Created by elimy on 2017-01-02.
* 自定义的通过指定布局水平或者竖着来对应绘制分割线
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
 
 
public Context context;
//默认以垂直布局绘制分割线
private int orientation= LinearLayoutManager.VERTICAL;
 
//画笔,如果不通过修改系统分割线来设置,可以通过自定义的paint绘制
Paint paint;
 
//默认分割线的宽度
int divider_size=1;
/*
* 带参构造方法,可根据布局方式确定绘制特定的分割线
* */
public DividerItemDecoration(Context context, int orientation) {
this.context=context;
this.orientation=orientation;
 
//如果传入参数不对抛出异常
if (orientation!=LinearLayoutManager.VERTICAL&&orientation!=LinearLayoutManager.HORIZONTAL){
throw new IllegalArgumentException("参数不对哟,亲爱的");
}
 
//设置画笔
paint=new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(divider_size);
 
}
 
/*
* 该方法实现通过canvas参数绘制分割线
* */
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state){
if (orientation==LinearLayoutManager.VERTICAL){
drawVerticalDivider(c,parent);
}else {
drawHorizontalDivider(c,parent);
}
}
 
/*
* paint绘制水平方向布局的分割线
* */
private void drawHorizontalDivider(Canvas c, RecyclerView parent) {
 
//获取item布局顶部y坐标,也就是将要绘制分割线的顶部y坐标
final int top=parent.getPaddingTop();
//获取item布局底部y坐标,也就是将要绘制分割线的底部y坐标
final int bottom=parent.getMeasuredHeight()-parent.getPaddingBottom();
//获取item数量
final int item_num=parent.getChildCount();
//循环绘制item分割线
for (int i=0;i<item_num;i++){
View child_item=parent.getChildAt(i);
//获取到当前item的布局参数
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) parent.getLayoutParams();
int left=child_item.getLeft()+layoutParams.getMarginEnd();
int right=left+divider_size;
 
//开始绘制
c.drawRect(left,top,right,bottom,paint);
}
 
}
 
/*
* paint绘制垂直方向布局的分割线
* */
private void drawVerticalDivider(Canvas c, RecyclerView parent) {
//计算item布局左边的x坐标没,也就是分割线的左边x坐标
final int left=parent.getPaddingLeft();
//计算item右边的x坐标,也就是分割线的右边x坐标
final int right=parent.getMeasuredWidth()-parent.getPaddingRight();
//获取item数量
final int item_num=parent.getChildCount();
 
//循环绘制item分割线
for (int i=0;i<item_num;i++){
View child_item=parent.getChildAt(i);
 
//获取到当前item的布局参数
RecyclerView.LayoutParams layoutParams= (RecyclerView.LayoutParams) child_item.getLayoutParams();
 
//绘制分割线的起始y坐标
int top=child_item.getBottom()+layoutParams.bottomMargin;
//绘制分割线底部y坐标
int bottom=top+divider_size;
 
//开始绘制
c.drawRect(left,top,right,bottom,paint);
}
}
 
/*
* 该方法与上一方法雷同,实现通过canvas参数绘制分割线,
* 与onDraw()不同的点在于onDrawOver()是在item view绘制之后调用
* */
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
 
/*
* 设置item偏移,在item之间添加分割线,后面一个item就需要往后偏移分割线的宽度
* */
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//根据布局方向设置item偏移值
if (orientation==LinearLayoutManager.VERTICAL){
outRect.set(0,0,0,divider_size);
}else {
outRect.set(0,0,divider_size,0);
}
}
}

    3.6 适配器类DecorationRecycViewAdapter.java
    
    
package com.example.recyclerviewdemo;
 
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
/**
* Created by elimy on 2017-01-02.
*/
public class DecorationRecycViewAdapter extends RecyclerView.Adapter<DecorationRecycViewAdapter.TextViewHolder> {
 
private Context context;
private String[] array;
 
/*
* 带参构造函数
* */
public DecorationRecycViewAdapter(Context context, String[] array) {
this.context = context;
this.array = array;
}
 
 
/*
* 加载布局返回TextViewHolder实例
* */
@Override
public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(context).inflate(R.layout.divider_item,parent,false);
TextViewHolder holder=new TextViewHolder(view);
return holder;
}
 
/*
* 绑定数据和布局
* */
@Override
public void onBindViewHolder(TextViewHolder holder, int position) {
//为每一个item设置文本
holder.textView.setText(array[position]);
}
 
@Override
public int getItemCount() {
return array.length;
}
 
/*
* 自定义的viewHolder
* */
protected class TextViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public TextViewHolder(View itemView) {
super(itemView);
textView= (TextView) itemView.findViewById(R.id.item_text);
}
}
}

4.通过修改系统主题绘制垂直布局/水平布局分割线
      前面一致的我就不贴出了,大家可自行添加参看前面的
    4.1 效果截图


    4.2 单个item布局divider_item.xml
   
   
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/item_text"
android:textSize="20dp"
android:textColor="#000"
android:gravity="center"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="88dp" />
</RelativeLayout>

    4.3 分割线绘制类DividerItemDecoration.java
   
   
package com.example.recyclerviewdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by elimy on 2017-01-02.
* 自定义的通过指定布局水平或者竖着来对应绘制分割线
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
//系统内置主题中的list分割线
private static final int[] attrs=new int[]{android.R.attr.listDivider};
private Drawable mDivider;
public Context context;
//默认以垂直布局绘制分割线
private int orientation= LinearLayoutManager.VERTICAL;
/*
* 带参构造方法,可根据布局方式确定绘制特定的分割线
* */
public DividerItemDecoration(Context context, int orientation) {
this.context=context;
this.orientation=orientation;
//如果传入参数不对抛出异常
if (orientation!=LinearLayoutManager.VERTICAL&&orientation!=LinearLayoutManager.HORIZONTAL){
throw new IllegalArgumentException("参数不对哟,亲爱的");
}
//获取到系统分割线属性
TypedArray typedArray=context.obtainStyledAttributes(attrs);
//设置分割线drawable
mDivider=typedArray.getDrawable(0);
typedArray.recycle();
}
/*
* 该方法实现通过canvas参数绘制分割线
* */
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state){
if (orientation==LinearLayoutManager.VERTICAL){
drawVerticalDividerByInternalAttr(c,parent);
}else {
drawHorizontalDividerByInternalAttr(c,parent);
}
}
/*
* 通过系统主题设置水平分割线 android.R.attr.listDivider
* */
public void drawHorizontalDividerByInternalAttr(Canvas c, RecyclerView parent){
final int top=parent.getPaddingTop();
final int bottom=parent.getHeight()-parent.getPaddingBottom();
final int child_num=parent.getChildCount();
for (int i=1;i<child_num;i++){
View child_item=parent.getChildAt(i);
RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) child_item.getLayoutParams();
int left=child_item.getRight()+params.rightMargin;
int right=left+mDivider.getIntrinsicHeight();
//设置分割线的长宽高
mDivider.setBounds(left,top,right,bottom);
//绘制分割线
mDivider.draw(c);
}
}
/*
* 通过系统主题设置水平分割线 android.R.attr.listDivider
* */
public void drawVerticalDividerByInternalAttr(Canvas c, RecyclerView parent){
final int left=parent.getPaddingLeft();
final int right=parent.getWidth()-parent.getPaddingRight();
final int child_num=parent.getChildCount();
for (int i=1;i<child_num;i++){
View child_item=parent.getChildAt(i);
RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) child_item.getLayoutParams();
int top=child_item.getTop()+params.bottomMargin;
int bottom=top+mDivider.getIntrinsicHeight();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
 
/*
* 该方法与上一方法雷同,实现通过canvas参数绘制分割线,
* 与onDraw()不同的点在于onDrawOver()是在item view绘制之后调用
* */
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
/*
* 设置item偏移,在item之间添加分割线,后面一个item就需要往后偏移分割线的宽度
* */
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//根据布局方向设置item偏移值
if (orientation==LinearLayoutManager.VERTICAL){
outRect.set(0,0,0,mDivider.getIntrinsicHeight());
}else {
outRect.set(0,0,mDivider.getIntrinsicHeight(),0);
}
}
}

    4.4 主题设置styles.xml
   
   
<resources>
 
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:listDivider">@drawable/custom_list_divider</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
 
</resources>

    4.5分割线drawable绘制custom_list_divider.xml
   
   
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="5dp"/>
<solid android:color="@color/colorAccent"/>
</shape>

ps:小坑特别提示,item布局设置背景后,分割线的颜色无法正常显示,请注意避让,可能有伙伴会说那要是瀑布流布局和GridLayout布局那怎么办,其实只要理解绘制的原理以及每一个方法主要的作用是啥,自己去绘制也不是一件难事,绘制的原理大概就像上面说的那样,如果伙伴们还是不理解,可以参考鸿洋大神的博客: http://blog.csdn.net/lmj623565791/article/details/45059587

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值