如何为RecyclerView添加分隔线

我在简书上发布了我个人的第一篇技术文档: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添加下拉刷新和上拉加载的功能。最后还是要感谢大家,感谢这个平台,能够让我们一起交流,一切学习。

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

(0)

相关推荐

  • RecyclerView的万能分割线

    什么是RecyclerView RecyclerView是Android 5.0 materials design中的组件之一,相应的还有CardView.Palette等.看名字我们就能看出一点端倪,没错,它主要的特点就是复用.我们知道,Listview中的Adapter中可以实现ViewHolder的复用.RecyclerView提供了一个耦合度更低的方式来复用ViewHolder,并且可以轻松的实现ListView.GridView以及瀑布流的效果. 先给大家展示下效果图: 使用方法: 添

  • Android中RecyclerView上拉下拉,分割线,多条目的实例代码

    //activity的xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity

  • android中RecyclerView自定义分割线实现

    最近一直在看RecyclerView,较之ListView它确实是灵活多变,给予开发者更多自定义的空间,比如:需要添加头部和尾部.item的点击事件.自定义的LayoutManager,还有就是下面要说的自定义的分割线. 1.如何理解分割线 经常听到有人说自定义分割线麻烦,为什么不把分割线写到item布局里,这样不是更简单吗?有些情况把分割线写到item布局里是很难达到我们想要的效果,例如RecyclerView里的GridLayoutManager,StaggeredGridLayoutMan

  • RecyclerView消除底部分割线的方法

    最近遇到一个问题,用RecyclerView显示数据,纵向列表显示,添加默认分割线. 问题是:底部也会显示分割线,这很影响美观. 怎么解决这个问题呢?我想了很多办法,毫无头绪... 最后,查看默认分割线的类DividerItemDecoration的源码: public class DividerItemDecoration extends ItemDecoration { private static final int[] ATTRS = new int[]{16843284}; publi

  • Android 给RecyclerView添加分割线的具体步骤(分享)

    [吐槽]RecyclerView没有提供分割线的方法,想要加个线还要自己画,点击事件的监听都要自己实现,不过真的好用. 给RecyclerView添加分割线的步骤 1.新建类继承于RecyclerView.ItemDecoration,此为是抽象类: public static abstract class ItemDecoration { public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, par

  • 如何为RecyclerView添加分隔线

    我在简书上发布了我个人的第一篇技术文档:RecyclerView系列之: RecyclerView系列之(1)为RecyclerView添加Header和Footer,也很有幸,能够得到那么多人的支持,这让我迫不及待的赶紧写第二篇文章.今天我将谈谈:为RecyclerView添加分隔线. 一. 理解ListView和RecyclerView中的ChildView 在讲为Item加入分割线本质的前,先来介绍,认识一下ChildView,也就是平时我们用到的ListView,RecyclerView

  • 如何为RecyclerView添加Header和Footer

    过去的两天,在项目中,抛弃了ListView, 想试一试RecyclerView, 在用的过程中,遇到了一些问题,比如:如何为RecyclerView添加Header和Footer? 如何为RecyclerView添加分割线?如何为RecyclerView添加下拉刷新和上拉加载? 在今后的一段时间里,我会针对这几个问题,通过写简书的方式一一讲述, 今天为大家带来的是第一个问题的解决方法,如何为RecyclerView添加Header和Footer?在这之前,我想分享一下我对RecyclerVie

  • Android RecyclerView添加上拉加载更多功能

    上一篇文章已经介绍了如何为RecyclerView添加FootView,在此基础上,要添加分页加载的功能其实已经很简单了. 上一篇文章地址:为RecyclerView添加FootView和HeadView 效果:(源码在文章结尾) 实现关键 在上一篇代码的基础上,只需要在onBindViewHolder(ViewHolder holder, int position)函数中添加一定修改就可以了,如下: @Override public void onBindViewHolder(ViewHold

  • Android RecyclerView实现悬浮吸顶、分隔线、到底提示效果

    本文中所有效果通过ItemDecoration实现,通过此实现的可以与业务解耦,让RecyclerView的模板更加简洁,不关心任何辅助性ui,github地址 一.顶部吸附效果图 二.顶部不吸附效果图 三.不满一屏效果 四.核心实现点 1.为什么通过ItemDecoration能够实现,原理? ①通过getItemOffsets()方法获取当前模板view的left.top.right.bottom边距,这些留出的间距用于绘制这些辅助性ui. // RecyclerView的measure c

  • iOS程序开发中设置UITableView的全屏分隔线的方法(不画线)

    ableView是app开发中常用到的控件,功能很强大,多用于数据的显示.下面给大家介绍设置UITableView的全屏分隔线的两种方法. 具体详情如下所示: 如图 添加如下代码 sTableView.separatorInset = UIEdgeInsetsZero; sTableView.layoutMargins = UIEdgeInsetsZero; cell.layoutMargins = UIEdgeInsetsZero; 第二种方法如下图 -(void)viewDidLayoutS

  • iOS利用UITableView设置全屏分隔线的3种方法总结

    前言 本文主要给大家总结了iOS用UITableView设置全屏分隔线的3种方法,一般TableView设置全屏分隔线有下面三种方法: 1.自定义cell,手动添加分割线 隐藏自带的 tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 可以通过addSubview的方式添加一条分割线:也可以自绘分割线. // 自绘分割线 - (void)drawRect:(CGRect)rect { CGContextRef context

  • 如何为MongoDB添加分片副本集

    背景是这样的:线上一个MongoDB集群保存了很多历史数据,这些历史数据是按照时间字段进行分片的,最近到了2021年,有些分片的截止时间要到了,为了能容纳更多的数据,需要在当前分片的基础上增加相应的分片. 线上环境中,每个分片本身也是一个3副本的副本集,所以添加的时候有些特定的流程需要注意,我再测试环境中简单测了一下这个过程,记录下来. 整个过程大概的流程是: 1.新建分片副本集 2.使用addShard命令将分片副本集添加到集群中 3.使用addShardTag命令为分片打标签 4.使用add

  • Android RecyclerView添加头部和底部的方法

    如果只是想添加头部,可是使用GitHub里面这个项目,它可以为LinearLayoutManager,GridLayoutManager ,StaggeredGridLayoutManager布局的RecyclerView添加header.使用起来也十分简单: 只需将RecyclerViewHeader布局放在RecyclerView的上层. <FrameLayout android:layout_width="match_parent" android:layout_heigh

  • Android RecyclerView添加上拉加载更多效果

    先看一下效果 刷新的时候是这样的: 没有更多的时候是这样的: 既然有刷新的时候有两种状态就要定义两个状态 //普通布局的type static final int TYPE_ITEM = 0; //脚布局 static final int TYPE_FOOTER = 1; 在特定的时候去显示: @Override public int getItemViewType(int position) { //如果position加1正好等于所有item的总和,说明是最后一个item,将它设置为脚布局

随机推荐