天天看点

RecyclerView的分割线 - ItemDecoration

开篇介绍博客:

Android RecyclerView 使用完全解析 体验艺术般的控件 (Hongyang)

RecyclerView可以在xml文件中配置的特别属性 (u013147734的博客)

以上两篇博客,可以先看hy大神的,比较全比较基础一点,然后可以看第二篇博客,介绍的详细一点,不过这两篇博客都没有很详细的介绍 ItemDecoration 这个类,或者说只是给了个代码例子,但是却没有给出详细的思路,看的我一脸懵逼血。

上图:

RecyclerView的分割线 - ItemDecoration

没错,上图中除了toolbar整个画面其实就是一个RecyclerView,我想这样会比较有代表性一点。我想还是先来说一下数据的思路,其实整个数据都是从string.xml中拿出来的:

<string name="location_hot_city_des">热门城市</string>
    <string name="location_hot_area_des">热门景区</string>

    <string-array name="city_hot">
        <item>定位</item>
        <item>北京市</item>
        <item>天津市</item>
        <item>上海市</item>
        <item>重庆市</item>
        <item>沈阳市</item>
        <item>大连市</item>
        <item>长春市</item>
        <item>哈尔滨市</item>
        <item>郑州市</item>
        <item>武汉市</item>
        <item>长沙市</item>
        <item>广州市</item>
        <item>深圳市</item>
        <item>南京市</item>
    </string-array>

    <string-array name="hot_area">
        <item>故宫博物馆</item>
        <item>东方明珠塔</item>
        <item>黄果树瀑布</item>
        <item>黄山风景区</item>
        <item>庐山风景区</item>
        <item>清明上河园</item>
        <item>布达拉宫</item>
        <item>秦始皇陵</item>
        <item>云岗石窟</item>
        <item>镜泊湖</item>
        <item>桃花源</item>
        <item>黄鹤楼</item>
        <item>丽江古城</item>
        <item>乐山大佛</item>
        <item>南京夫子庙</item>
    </string-array>
           

在这样的xml的数据下,我另外创建了两个简单的数组,其实就是为了添加 热门城市/热门风景,然后把这些数组合并成一个数组:

ArrayUtils.mergeStringArray(
       ArrayUtils.mergeStringArray(new String[]{"", hotCityDes, ""}, hotCity),       ArrayUtils.mergeStringArray(new String[]{"", hotAreaDes, ""}, hotArea)),
           
public class ArrayUtils {

    private static final String TAG = "ARRAYUTILS";

    /**
     * 合并数组
     *
     * @param str1
     * @param str2
     * @return
     */
    public static String[] mergeStringArray(String[] str1, String[] str2) {
        int strLen1 = str1.length;// 保存第一个数组长度
        int strLen2 = str2.length;// 保存第二个数组长度
        str1 = Arrays.copyOf(str1, strLen1 + strLen2);// 扩容
        System.arraycopy(str2, , str1, strLen1, strLen2);// 将第二个数组与第一个数组合并
        TLog.d(TAG, Arrays.toString(str1));
        return str1;
    }

}
           

我想说的是上面这些内容,都只是为了自定义 ItemDecoration 做准备的,从上面的代码看下来,可以知道:首先, 0,1,2这三个position,就是热门城市那一行,而 0+hotCity.size , 1 + hotCity.size , 2 + hotCity.size,这是热门风景那一行,计算这些坐标是因为从上图中可以很明显看出,他们的分割线的高度是不同的,并且需要注意的是靠近两侧的item的分割线和中间的分割线是不同的(因为,如果相同的话,应该是中间空白处大,而两边小,这个应该比较好理解吧),并且最后一行的分割线和其它的分割线也是不同的,以上,就是自定义的 ItemDecoration 最重要的部分:思路!!!

ItemDecoration:

撇去 draw 方法不看,我们单独来看 getItemOffsets,这个方法有2个不同的参数,有一个已经被废弃了,不过无所谓,这不是重点,这个方法从我介绍的博客上来看:定义分割线的范围。这句话对于刚刚准备自定义分割线的我来讲,真的是太简洁了,简洁到根本不知道它在说什么,那么我的理解是,你可以把这看成定义 “margin”,而getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)方法中的 outRect则代表的是 item 所在的位置,outRect的 left,top,right,bottom则分别代表着 marginleft/top/right/bottom。好吧其实还是有些抽象,上代码,画图解释:

这就是上图城市选择分割线的所有代码

import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import yxmj.cjh.yxmj.api.AppConfig;

/**
 * Created by cjh on 16-8-18.
 */
public class LocationSpaceItemDecoration extends RecyclerView.ItemDecoration {

    private int mSpace;
    private String mSpecial_position;

    public LocationSpaceItemDecoration(int space, String special_position) {
        mSpace = space;
        mSpecial_position = special_position;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

        outRect.left = mSpace / ;
        outRect.top = mSpace / ;
        outRect.right = mSpace / ;
        outRect.bottom = mSpace / ;

        int position = parent.getChildAdapterPosition(view);
        if (mSpecial_position.contains(AppConfig.HORIZONTAL_LINE + position + AppConfig.HORIZONTAL_LINE)) {
            outRect.top = mSpace;
            outRect.bottom = ;
        }
        if (position %  == )
            outRect.left = mSpace;

        if (position %  == )
            outRect.right = mSpace;

    }
}
           

mSpace:代表的是一行 item,每个item 中间的距离。

把每个Item所在位置看成一个 outRect,那么正常情况下(也就是中间的item),为了保证水平方向所有间距相同,那么 left 和 right 就得是 mSpace 的一半。

outRect.left = mSpace / ;//marginLeft = mSpace/2;
        outRect.top = mSpace / ; // marginTop = mSpace/3;
        outRect.right = mSpace / ;// marginRight = mSpace/2;
        outRect.bottom = mSpace / ;// marginBottom = mSpace/3;
           

mSpecial_position也就是热门城市/风景的位置,那么当处于这个位置的 item,距离顶部要大一点,距离底部要小一点

if (mSpecial_position.contains(AppConfig.HORIZONTAL_LINE + position + AppConfig.HORIZONTAL_LINE)) {
            outRect.top = mSpace;//marginTop = mSpace
            outRect.bottom = ; //marginBottom = 0;
        }
           

.如果是左侧的item那么,left = mSpace

if (position %  == )
            outRect.left = mSpace;
           

如果是右侧的item,那么 right = mSpace

if (position %  == )
            outRect.right = mSpace;
           

希望大家看到这里就明白了,如果还不明白,我画了个简单的图(处女座的同学请忽视,继续看上面):

RecyclerView的分割线 - ItemDecoration

基本上我想表达的就是这些了,这个时候,你再去看 Hongyang 的博客,我想应该就会清晰点了吧,而 draw 方法,就是使用各种资源把这些margin的位置使用 drawable 去填充,可以是 shape,drawable.xml 中的颜色,虽然我没有试过图片,不过,我想应该可以把,不过,应该不会有这种奇葩无敌的需求吧。