当前位置: 移动技术网 > 移动技术>移动开发>Android > 如何为RecyclerView添加分隔线

如何为RecyclerView添加分隔线

2019年07月24日  | 移动技术网移动技术  | 我要评论
我在简书上发布了我个人的第一篇技术文档:recyclerview系列之: recyclerview系列之(1)为recyclerview添加header和footer,也很

我在简书上发布了我个人的第一篇技术文档:recyclerview系列之: recyclerview系列之(1)为recyclerview添加header和footer,也很有幸,能够得到那么多人的支持,这让我迫不及待的赶紧写第二篇文章。今天我将谈谈:为recyclerview添加分隔线。

一. 理解listview和recyclerview中的childview
在讲为item加入分割线本质的前,先来介绍,认识一下childview,也就是平时我们用到的listview,recyclerview中的getchildat(int position)这个返回的childview是哪一部分?到底是哪一部分呢?一开始的时候,我理解错了,但是经过下面两张图这么一比较,你就明白了:


item布局layout_margin == 0

item布局layout_margin == 16dp

下面看代码的区别:
第一张图的代码, 也就是每一个list_item的布局文件(下同)如下:

<?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="50dp"> 
  <textview  
  android:id="@+id/list_item"  
  android:layout_width="match_parent" 
  android:layout_height="match_parent"  
  android:gravity="center"  
  android:textsize="20sp"  
  android:textcolor="#262526"  
  android:background="#08da1d"/>
</linearlayout>

第二张图的代码:

<?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="50dp"
android:layout_margin="16dp"> 
  <textview  
  android:id="@+id/list_item"  
  android:layout_width="match_parent" 
  android:layout_height="match_parent"  
  android:gravity="center"  
  android:textsize="20sp"  
  android:textcolor="#262526"  
  android:background="#08da1d"/>
</linearlayout>

仔细看一下,它们的不同之处, 就是第二个图的代码中多了:

android:layout_margin = "16dp"
就多这一句而已。

所以到这里我们应该知道了childview是哪一部分了,就是图二中绿色这一部分,边距这一部分并不属于childview, 而是属于childview的布局。

这样我们了解childview之后,下面再来理解加入分隔线的原理就简单多了。

二. 理解加入分隔线的原理

在listview中,google为我们提供了setdivider(drawable divider)这样的方法来设置分隔线,那么在recyclerview中,google又为我们提供了什么样的方法去添加分隔线呢?通过查看官方文档,它,提供了:additemdecoration(recyclerview.itemdecoration decor)这个方法了设置分隔线,那问题又来了,recyclerview.itemdecoration是什么东西呢?继续查:然后发现如下:它原来是一个类,里面封装了三个方法:
(1)void getitemoffsets ()
(2)void ondraw ()
(3)void ondrawover ()

通过上面的三个方法,可以看出,这是要自己直接画上去,准确的说这几个方法是:添加divider,主要是找到添加divider的位置, 而divider是在drawable文件中写好了的。 利用ondraw和ondrawover都差不多,我们在创建自己的decoration类继承recyclerview.itemdecoration的时候,我们只要重写getitemoffsets(),还有ondraw()和ondrawover两者其中之一就可以了.

那getitemoffsets()方法有什么用呢?从字面意思就是item要偏移, 由于我们在item和item之间加入了分隔线,线其实本质就是一个长方形,也是用户自定义的,既然线也有长宽高,就画横线来说,上面的item加入了分隔线,那下面的item就要往下平移,平移的量就是分隔线的高度。不理解每关系,后面看代码就容易理解了。

现在我们知道了如何添加了,就是通过画,那到底是画在哪里呢?画的位置又怎么确定呢?下面看图:


分隔线的位置图

我现在拿画横线来说,从上面这个图中,我们很容易就可以看到,我们画分隔线的位置,是在每一个item的布局之间,注意:是布局之间。

好了,我们确定了画在哪里,那我们怎么确定画线的具体的坐标位置呢?也就是我们要确定:分隔线的left, top, right, bottom. 在adapter中,我们很容易通过parent(这个parent它其实就是我们能看到的部分)获取每一个childview:
(1)left:parent.getpaddingleft()
(2)right: parent. getwidth()-parent.getpaddingright();
(3)top : 就是红线的上面:我们通过childview.getbottom()来得到这个item的底部的高度,也就是蓝线位置,蓝线和红线之间间距:就是这个item布局文件的:layout_marginbottom, 然后top的位置就是两者之和。
(4)bttom: 就是top加上分隔线的高度:top+线高

通过上面的解析,你也许知道了加入分隔线的原理,不理解也没有关系,说也不是说得很清楚,下面直接上代码,通过代码来理解。

三. talk is cheap, show you the code.

(1)首先,先来看主布局文件:activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.coordinatorlayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:fitssystemwindows="true"
 tools:context="com.study.wnw.recyclerviewdivider.mainactivity">

 <android.support.v7.widget.recyclerview
  android:id="@+id/recyclerview"
  android:layout_width="match_parent"
  android:layout_height="match_parent"> 
 </android.support.v7.widget.recyclerview>
</android.support.design.widget.coordinatorlayout>

我在这里面仅仅加入了一个recyclerview

(2)recyclerview里面每个item的布局文件:item_view.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="50dp"
    android:layout_margin="16sp">
 <textview
  android:id="@+id/list_item"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:textsize="20sp"
  android:textcolor="#f7f4f7"
  android:background="#08da1d"/>
</linearlayout>

这也没有什么可讲的,就是在里面添加一个textview用来显示文本

(3)我们recyclerview的适配器myadapater.java:

package com.study.wnw.recyclerviewdivider;
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;
import java.util.list;
import java.util.concurrent.copyonwritearraylist;
/** * created by wnw on 16-5-22. */
public class myadapter extends recyclerview.adapter<recyclerview.viewholder> {

 //定义一个集合,接收从activity中传递过来的数据和上下文
 private list<string> mlist;
 private context mcontext;

 myadapter(context context, list<string> list){
  this.mcontext = context;
  this.mlist = list;
 }

 //得到child的数量
 @override
 public int getitemcount() {
  return mlist.size();
 }

 //创建childview
 @override
 public recyclerview.viewholder oncreateviewholder(viewgroup parent, int viewtype) {
  view layout = layoutinflater.from(mcontext).inflate(r.layout.item_view, parent, false);
  return new myholder(layout);
 }

 //将数据绑定到每一个childview中
 @override
 public void onbindviewholder(recyclerview.viewholder holder, int position) {
  if (holder instanceof myholder){
   final string itemtext = mlist.get(position);
   ((myholder)holder).tv.settext(itemtext);
  }
 }

 // 通过holder的方式来初始化每一个childview的内容
 class myholder extends recyclerview.viewholder{
  textview tv;
  public myholder(view itemview) {
   super(itemview);
   tv = (textview)itemview.findviewbyid(r.id.list_item);
  }
 }
}

好了,这里也没有什么好讲的,也不是我们这篇文章的重点,下面重点来了。

(4)我们自定义的mydecoration.java:(继承recyclerview.itemdecoration)

package com.study.wnw.recyclerviewdivider;
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.linearlayoutmanager;
import android.support.v7.widget.recyclerview;
import android.util.log;
import android.view.view;

/** * created by wnw on 16-5-22. */

public class mydecoration extends recyclerview.itemdecoration{

 private context mcontext;
 private drawable mdivider;
 private int morientation;
 public static final int horizontal_list = linearlayoutmanager.horizontal;
 public static final int vertical_list = linearlayoutmanager.vertical;

 //我们通过获取系统属性中的listdivider来添加,在系统中的apptheme中设置
 public static final int[] atrrs = new int[]{
   android.r.attr.listdivider
 };

 public mydecoration(context context, int orientation) {
  this.mcontext = context;
  final typedarray ta = context.obtainstyledattributes(atrrs);
  this.mdivider = ta.getdrawable(0);
  ta.recycle();
  setorientation(orientation);
 }

 //设置屏幕的方向
 public void setorientation(int orientation){
  if (orientation != horizontal_list && orientation != vertical_list){
   throw new illegalargumentexception("invalid orientation");  }  morientation = orientation;
 } 

 @override
 public void ondraw(canvas c, recyclerview parent, recyclerview.state state) {
  if (morientation == horizontal_list){
   drawverticalline(c, parent, state);
  }else {
   drawhorizontalline(c, parent, state);
  }
 }

 //画横线, 这里的parent其实是显示在屏幕显示的这部分
 public void drawhorizontalline(canvas c, recyclerview parent, recyclerview.state state){
  int left = parent.getpaddingleft();
  int right = parent.getwidth() - parent.getpaddingright();
  final int childcount = parent.getchildcount();
  for (int i = 0; i < childcount; i++){
   final view child = parent.getchildat(i);

   //获得child的布局信息
   final recyclerview.layoutparams params = (recyclerview.layoutparams)child.getlayoutparams();
   final int top = child.getbottom() + params.bottommargin;
   final int bottom = top + mdivider.getintrinsicheight();
   mdivider.setbounds(left, top, right, bottom);
   mdivider.draw(c);
   //log.d("wnw", left + " " + top + " "+right+" "+bottom+" "+i);
  }
 }

 //画竖线
 public void drawverticalline(canvas c, recyclerview parent, recyclerview.state state){
  int top = parent.getpaddingtop();
  int bottom = parent.getheight() - parent.getpaddingbottom();
  final int childcount = parent.getchildcount();
  for (int i = 0; i < childcount; i++){
   final view child = parent.getchildat(i); 

   //获得child的布局信息
   final recyclerview.layoutparams params = (recyclerview.layoutparams)child.getlayoutparams();
   final int left = child.getright() + params.rightmargin;
   final int right = left + mdivider.getintrinsicwidth();
   mdivider.setbounds(left, top, right, bottom);
   mdivider.draw(c);
  }
 }

 //由于divider也有长宽高,每一个item需要向下或者向右偏移
 @override
 public void getitemoffsets(rect outrect, view view, recyclerview parent, recyclerview.state state) {
  if(morientation == horizontal_list){
   //画横线,就是往下偏移一个分割线的高度
   outrect.set(0, 0, 0, mdivider.getintrinsicheight());
  }else {
   //画竖线,就是往右偏移一个分割线的宽度
   outrect.set(0, 0, mdivider.getintrinsicwidth(), 0);
  }
 }
}

从上面的代码中,我们还通过系统属性来适应屏幕的横屏和竖屏,然后确定画横的,还是竖的divider,其实在里面我们做了三件事,第一件是:获取到系统中的listdivider, 我们就是通过它在主题中去设置的,下面第(6)小点看一下代码就知道了。第二件事:就是找到我们需要添加divider的位置,从ondraw方法中去找到,并将divider添加进去。第三个是:得到item的偏移量。

(5)看看我们的mainactivity.java

package com.study.wnw.recyclerviewdivider;
import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.support.v7.widget.linearlayoutmanager;
import android.support.v7.widget.recyclerview;
import java.util.arraylist;
import java.util.list;
public class mainactivity extends appcompatactivity {
 //定义recyclerview
 private recyclerview mrecyclerview = null;

 //定义一个list集合,用于存放recyclerview中的每一个数据
 private list<string> mdata = null;

 //定义一个adapter
 private myadapter madapter; 

 //定义一个linearlayoutmanager
 private linearlayoutmanager mlayoutmanager;

 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);

  //recyclerview三步曲+layoutmanager
  initview();
  initdata();
  madapter = new myadapter(this,mdata);
  mrecyclerview.setlayoutmanager(mlayoutmanager);
  mrecyclerview.setadapter(madapter); 

  //这句就是添加我们自定义的分隔线
  mrecyclerview.additemdecoration(new mydecoration(this, mydecoration.vertical_list));
 }

 //初始化view
 private void initview(){
  mlayoutmanager = new linearlayoutmanager(this);
  mrecyclerview = (recyclerview)findviewbyid(r.id.recyclerview);
 }

 //初始化加载到recyclerview中的数据, 我这里只是给每一个item添加了string类型的数据
 private void initdata(){
  mdata = new arraylist<string>();
  for (int i = 0; i < 20; i++){
   mdata.add("item" + i);
  }
 }
}

(6)分隔线divider的drawable文件:divider..xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle">
 <solid android:color="#7b7a7a"/>
 <size android:height="1dp"/>
</shape>

我们在这里面,画了一个:rectangle, 给它填充颜色,还有高度,这样就搞定了,高度小,显示出来也是一条线:其实线的本质就是长方形。这里可以根据个人需要,画不同类型的divider

(7)在styles.xml的apptheme中,设置listdivider为我们的divider.xml文件:

<style name="apptheme" parent="theme.appcompat.light.darkactionbar">
 <item name="android:listdivider">@drawable/divider</item>
</style>

这样,我们将系统的listdivider设置成我们自定义的divider. 还记得我们在mydecoration中获取系统的listdivider这个属性吗,这样通过这个属性,我们就可以将我们的divider.xml文件和mydecoration.java进行关联了。

到这里所有的工作就完成了,下面展示一下运行结果:


竖屏效果图

横屏效果图

经过几个小时的写作,终于搞定了,虽然仅仅是一个添加分隔线的功能,但是还是想尽可能的通过自己的语言去理解,去认知它的原理,这样做起来就简单多了。一开始的时候,我夜不知道怎么去用,也参考了别人写的文章,特别是鸿洋大神的:android recyclerview 使用完全解析 体验艺术般的控件, 写得特别的棒,从中也学到了一些知识。

好了,这篇文章暂时写到这里了,简单的介绍了一些recyclerview分隔线的原理和添加方法,希望大家能够多多交流,过几天我会继续写下一篇文章,recyclerview系列之(3):为recyclerview添加下拉刷新和上拉加载的功能。最后还是要感谢大家,感谢这个平台,能够让我们一起交流,一切学习。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网