<?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”/>

第二张图的代码:

<?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”/>

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

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就要往下平移,平移的量就是分隔线的高度。不理解每关系,后面看代码就容易理解了。


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

android RecyclerView item水平间距 recyclerview设置间距_面试

分隔线的位置图

我现在拿画横线来说,从上面这个图中,我们很容易就可以看到,我们画分隔线的位置,是在每一个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”/>

这也没有什么可讲的,就是在里面添加一个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 mList;
private Context mContext;
MyAdapter(Context context, List 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 //在style.xml文件的AppTheme主题中中设定@drawable/divider ---- 见下文
};
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(); //横线的左端必须是paddngleft,如果用left则横线过长(不显示)
int right = parent.getWidth() - parent.getPaddingRight();//同上,getLeft()是控件左端距离屏幕左端的长度,right是控件右端距离屏幕左端的长度
final int childCount = parent.getChildCount();//接上:getwidth是控件的宽度(如不在view中的onDraw()使用measure方法测量,getMeasuredWidth同)
for (int i = 0; i < childCount; i++){
final View child = parent.getChildAt(i);
//获得child的布局信息(主要是获取LayoutParams中的params.`bottomMargin长度`)  final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();//getIntrinsicHeight()方法是返回drawable文件固有的高度,以dp为单位
mDivider.setBounds(left, top, right, bottom); //setBounds()方法主要是设定分割线控件的长宽
mDivider.draw©;
//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©;
}
}
//由于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 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();
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”>

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


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

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


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

android RecyclerView item水平间距 recyclerview设置间距_分隔线_02

竖屏效果图

android RecyclerView item水平间距 recyclerview设置间距_分隔线_03