如何为RecyclerView添加分隔线

如何为RecyclerView添加分隔线,第1张

概述我在简书上发布了我个人的第一篇技术文档: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,state);  }else {   drawHorizontalline(c,state);  } } //画横线,这里的parent其实是显示在屏幕显示的这部分 public voID drawHorizontalline(Canvas c,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,bottom);   mdivIDer.draw(c);   //Log.d("wnw",left + " " + top + " "+right+" "+bottom+" "+i);  } } //画竖线 public voID drawVerticalline(Canvas c,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,bottom);   mdivIDer.draw(c);  } } //由于divIDer也有长宽高,每一个Item需要向下或者向右偏移 @OverrIDe public voID getItemOffsets(Rect outRect,VIEw vIEw,RecyclerVIEw.State state) {  if(mOrIEntation == HORIZONTAL_List){   //画横线,就是往下偏移一个分割线的高度   outRect.set(0,mdivIDer.getIntrinsicHeight());  }else {   //画竖线,就是往右偏移一个分割线的宽度   outRect.set(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添加下拉刷新和上拉加载的功能。最后还是要感谢大家,感谢这个平台,能够让我们一起交流,一切学习。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的如何为RecyclerView添加分隔线全部内容,希望文章能够帮你解决如何为RecyclerView添加分隔线所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1147438.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存