关于RecyclerView的使用,不是本文介绍的重点,还不清楚的同学可以参考这篇文章: Android RecyclerView 使用完全解析 体验艺术般的控件。

RecyclerView替代ListView势在必行,唯一比较遗憾的是官方没有内置几个好用的ItemDecoration,这使得很多人觉得使用起来比较麻烦。

有幸站在巨人的肩膀上,github上有大神实现了一个:RecyclerView-FlexibleDivider,经过一番使用,我发现对于GridLayoutManager(不支持SpanSizeLookup)和StaggeredGridLayoutManager的支持并不好。

RecyclerView-FlexibleDivider的实现思路和网上多数方案是一致的:
* 从左到右绘制横向divider
* 从上至下绘制垂直divider

这种实现的方式弊端很明显,就是遇到GridLayoutManager(使用了SpanSizeLookup)和StaggeredGridLayoutManager的时候,就没法正常工作了,因为它们不是“规则”的占据一个“单元”。所以,本文的实现思路是这样的
* 横向divider绘制在每个item的下方
* 垂直divider绘制在每个item的右侧


在开始之前,我们有必要了解下ItemDecoration的几个方法
* onDraw(Canvas c, …):在绘制item views**之前**绘制decorations,一般我们都用这个方法
* onDrawOver(Canvas c, …):与上面的方法相反,在绘制item views**之后**绘制decorations
* getItemOffsets(Rect outRect, …):为outRect设置left,top和right,bottom,简单的理解就是设置item view的margin(marginLeft、marginTop、marginRight、marginBottom)

绘制decorations的方法就是使用onDraw方法中的参数Canvas,比如
* 绘制一条线:canvas.drawLine
* 绘制图片:drawable.draw(canvas)
* 等等…

不管哪种绘制都需要的一个参数,那就是绘制的范围:Rect;另外针对横向和垂直divider,getItemOffsets方法中的outRect的设置,应该也是不一样的。所以抽象出一个父类FlexibleDividerDecoration,继承自RecyclerView.ItemDecoration。

FlexibleDividerDecoration

public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {    protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);    protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        int validChildCount = parent.getChildCount();        for (int i = 0; i < validChildCount; i++) {            View child = parent.getChildAt(i);            int childPosition = parent.getChildAdapterPosition(child);            // 当前item是否需要divider(通常最后一个不需要)            if (!hasDivider(parent, childPosition)) {                continue;            }            // 绘制divider的区域,由子类具体实现            Rect bounds = getDividerBound(childPosition, parent, child);            switch (mDividerType) {                case DRAWABLE: // 图片类型的divider                    Drawable drawable = mDrawableProvider.drawableProvider(childPosition, parent);                    drawable.setBounds(bounds);                    drawable.draw(c);                    break;            }        }    }    @Override    public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {        int position = parent.getChildAdapterPosition(v);        if (!hasDivider(parent, position)) {            return;        }        setItemOffsets(rect, position, parent);    }    /**     * Whether child has divider     *     * @param parent     * @param childPosition     * @return true if child has divider     */    public boolean hasDivider(RecyclerView parent, int childPosition) {        if (this instanceof VerticalDividerItemDecoration) { // 垂直方向是否有divider            return hasVerticalDivider(parent, childPosition);        } else if (this instanceof HorizontalDividerItemDecoration) { // 水平方向是否有divider            return hasHorizontalDivider(parent, childPosition);        }        return false;    }}

上面的代码看注释应该比较好理解(注意:实际代码会比文章中的复杂些许)。具体实现的子类有两个,分别是HorizontalDividerItemDecorationVerticalDividerItemDecoration
我们看下HorizontalDividerItemDecoration

public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration {  protected HorizontalDividerItemDecoration(Builder builder) {      super(builder);  }  @Override  protected Rect getDividerBound(int position, RecyclerView parent, View child) {      Rect bounds = new Rect(0, 0, 0, 0);      RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();      bounds.left = child.getLeft();      bounds.right = child.getRight();      // divider大小      int dividerSize = getDividerSize(position, parent);      bounds.top = child.getBottom() + params.bottomMargin;      bounds.bottom = bounds.top + dividerSize;      return bounds;  }  @Override  protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {      outRect.set(0, 0, 0, getDividerSize(position, parent));  }}

这里只贴出了关键代码,因为实际情况,我们可能还需要考虑更多的问题,比如:
1. 水平divider的marginLeft和marginRight,还需要判断item是否是最边上的,因为中间的divider是不需要margin的
2. 垂直divider的marginTop和marginBottom,还需要判断item是否是最边上的,因为中间的divider是不需要margin的
3. 水平和垂直交叉位置的空白问题
4. getReverseLayout为true和false时,不同的处理

为了支持不同的LayoutManager,以上的判断策略还不尽相同,因为篇幅限制,具体的代码请查看文章最后的源码,最后我们来看下最终实现的效果吧~


本文源码地址:https://github.com/hiphonezhu/RecyclerView-FlexibleDivider(由于改动较大,fork过来的代码没有pull给作者,如需使用请下载本文的源码,而不是通过Gradle引入)

更多相关文章

  1. Android使用AudioManager修改系统音量的方法
  2. 自定义Android(安卓)Studio方法注释模板
  3. android 版本号适配 9.0网络请求方法
  4. Android里监视数据库的变化[转]
  5. 关闭应用的4种方法
  6. 3天打造一个 android 小游戏
  7. PhoneGap在Android上的插件开发方法介绍
  8. Android(安卓)中文 API(123) ―― AbsListView(二)
  9. Android(安卓)旋转屏幕时禁止Activity重建

随机推荐

  1. android:shape的使用
  2. Android(安卓)Property Animation属性动
  3. android的一些常用属性的说明
  4. Android中使用jiecaovideoplayer播放视频
  5. Android(安卓)Studio中隐藏状态栏和标题
  6. Android中http请求(C# MVC接收)
  7. Android模拟器简介—更新到最新的3.0 Hon
  8. android SharedPreferences(供选项使用)
  9. Android线程调度机制
  10. android手机打电话代码分析