新普金娱乐Android——RecyclerView入门学习的ItemDecoration(一)Recyclerview详解(二)ItemDecoration定义分割线。

学资料:

Recyclerview中贯彻分割效果待为此到ItemDecoration,它是一个抽象类,用来绘制分割效果,使用的时光要再次写这办法,下面先押一下他的源码:

  • 张旭童同学的应用ItemDecoration为RecyclerView打造带悬停头部的分组列表
  • Piasy大神的深刻理解 RecyclerView
    系列之一:ItemDecoration

<pre>
public static abstract class ItemDecoration {
@param c Canvas to draw into
@param parent RecyclerView this ItemDecoration is drawing into
@param state The current state of RecyclerView
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, paren);
@param c Canvas to draw into
@param parent RecyclerView this ItemDecoration is drawing into
@param state The current state of RecyclerView.
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
@param outRect Rect to receive the output.
@param view The child view to decorate
@param parent RecyclerView this ItemDecoration is decorating
@param state The current state of RecyclerView.
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
State state) {
getItemOffsets(outRect, ((LayoutParams)
view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}
</pre>

Piasy大神的每篇博客质量还特别高,强烈推荐

消已经不建议采取的,还有三只根本个章程

网上发出很多关于RecyclerView学习博客,之前看了几乎首,但基本核心都是RecyclerView.Adapter。关于RecyclerView的侧滑删除,之前有过粗略修ItemTouchHleper实现RecyclerView侧滑删除,但对RecyclerView刺探远远不够。除了Adapter外,RecyclerView再有许多另强大的地方用上

onDraw(Canvas c, RecyclerView parent, State state)
用来绘制分割线,在ItemView绘制之前,如果分割线越界,会来得在ItemView上层
onDrawOver(Canvas c, RecyclerView parent, State state)
用来绘制分割线,在ItemView绘制之后。如果分割线越界,会显在ItemView下层
getItemOffsets来指定itemview对应之分割线位置及尺寸。outRect存放了decoration的季个比坐标。通过outRect.set(l, t, r, b)来设置。它的法则实际上是对ItemView来安Padding,从而也decoration留起相应之上空,单位凡px

天才木木同学收集整理的底Android开发的有好用的RecyclerView轮子非常好

打听了这些下,可以实现一个坏简单的划分效果了,这里针对一个Vertical方向的Recyclerview绘制黑色分割。


首先定义分割线,使用了ShapeDrawable,定义一个5px高的黑色图形(divider.xml):

修计划:

<pre>
<shape
xmlns:android=”http://schemas.android.com/apk/res/android"&gt;
<size android:height=”5px”></size>
<solid android:color=”@color/colorBlack”></solid>
</shape>
</pre>

  • ItemDecoration
  • LayoutManager
  • RecyclerView.Adapter
  • DiffUtil
  • SimpleOnItemTouchListener
  • SmoothScroller
  • ItemAnimator

下一场实现ItemDecoration,绘制分割线:


    private class BlackDecoration extends RecyclerView.ItemDecoration{
        private Drawable mDivider;
        public BlackDecoration(Context context) {
//获取我们定义的Drawable
            mDivider = context.getDrawable(R.drawable.divider);
        }
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
//指定Drawable的坐标,这里是竖直方向,所以每个分割线的左右都是一样的,获取Recyclerview的padding就可以了,与ItemView的尺寸相同
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
//分割线是一个一个单独绘制的,需要分别对每个Item的分割线获取到top和Bottom坐标
            for(int i = 0; i<parent.getChildCount(); i++){
                View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                        .getLayoutParams();
//top的位置应该是itemView的底部,另外还要加上itemView的MarginBottom,和竖直方向的位移。就是itemView 的下边界
                final  int top = child.getBottom() + params.bottomMargin +
                        Math.round(ViewCompat.getTranslationY(child));
//top加divider的高度就是bottom了
                final  int bottom = top + mDivider.getIntrinsicHeight();
//设置divider边界并进行绘制
                mDivider.setBounds(left,top,right,bottom);
                mDivider.draw(c);
            }
        }
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
//设置Itemview的padding,为divider留出位置。
            outRect.set(0,0,0,mDivider.getIntrinsicHeight());
        }
    }

1. ItemDecoration 条款装饰<p>

是一个抽象类,顾名思义,就是用来装饰RecyclerView的子item的,通过名字就是可清楚,功能并不仅仅是增长间距绘制分割线,是故来装点item的。源码中之描述:

An ItemDecoration allows the application to add a special drawing and
layout offset to specific item views from the adapter’s data set. This
can be useful for drawing dividers between items, highlights, visual
grouping boundaries and more.

主导的功效是好据此来叫RecyclerView的子item安四边边距,以及前后左右制图分割线。当然功能不断这些

ItemDecoration一个产生6只抽象方法,有3只还扔了,也就算剩下3个待上学

  • getItemOffsets(Rect outRect, View view, RecyclerView parent, State
    state) 设置四边边距
  • onDraw(Canvas c, RecyclerView parent, State state) 绘制装饰
  • onDrawOver(Canvas c, RecyclerView parent, State state) 绘制蒙层

最后便是针对Recyclerview设置分割了,mRecyclerView.setAdapter(new RecycleAdapter());
,运行后就可以看到效能了,每个item之间会产生黑色的分割线。

1.1 使用RecyclerView展示50长条字符串数据 <p>

一直使用RecyclerView显示50长纯字符串数据,代码:

public class MainActivity extends AppCompatActivity {
    private RecyclerView rv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
        rv = (RecyclerView) findViewById(R.id.rv_main_activity);
        //设置布局管理器
        LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        rv.setLayoutManager(manager);
        //设置ItemDecoration

        //适配器
        RecyclerViewAdapter adapter = new RecyclerViewAdapter(rv, R.layout.item_layout);
        rv.setAdapter(adapter);
        //添加数据
        addData(adapter);
    }

    /**
     * 添加数据
     */
    private void addData(RecyclerViewAdapter adapter) {
        List<String> listData = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            listData.add("英勇青铜5---->"+i);
        }
        adapter.setData(listData);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != rv) {
            rv.setAdapter(null);
        }
    }
}

代码中从来不为RecyclerView设置ItemDecorationLayoutManagerLineatLayoutManager


子item布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_item_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:textAllCaps="false"
        android:textColor="@android:color/white"
        android:textSize="20sp" />
</LinearLayout>

布局也特意简单,给TextView装了背景色,字体是反革命

运转效果:

新普金娱乐 1

不设置ItemDecroation

item里头就没有间隔,也远非其余的分割线,TextView背景色导致整RecyclerView扣押起都安装了背景色

下面为每个item底层添加间距


关于分割线的详实介绍好参照鸿扬大神的博客Android RecyclerView
使用完全解析
体验方式般的控件,可以经点名theme的<item name="android:listDivider">@drawable/divider_bg</item>来落实不同之分割效果

1.2 getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) 设置四止偏移量

打定义一个RVItemDecoration继承ItemDecroation,重写getItemOffsets()

代码:

public class RVItemDecoration extends RecyclerView.ItemDecoration {

    private static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;//水平方向
    private static final int VERTICAL = LinearLayoutManager.VERTICAL;//垂直方向
    private int orientation;//方向
    private final int decoration;//边距大小 px

    public RVItemDecoration(@LinearLayoutCompat.OrientationMode int orientation int orientation, int decoration) {
        this.orientation = orientation;
        this.decoration = decoration;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        final int lastPosition = state.getItemCount() - 1;//整个RecyclerView最后一个item的position
        final int current = parent.getChildLayoutPosition(view);//获取当前要进行布局的item的position
        Log.e("0000", "0000---->" + current);
        Log.e("0000", "0000state.getItemCount()---->" + state.getItemCount());
        Log.e("0000", "0000getTargetScrollPosition---->" + state.getTargetScrollPosition());
        Log.e("0000", "0000state---->" + state.toString());
        if (current == -1) return;//holder出现异常时,可能为-1
        if (layoutManager instanceof LinearLayoutManager && !(layoutManager instanceof GridLayoutManager)) {//LinearLayoutManager
            if (orientation == LinearLayoutManager.VERTICAL) {//垂直
                outRect.set(0, 0, 0, decoration);
                if (current == lastPosition) {//判断是否为最后一个item
                    outRect.set(0, 0, 0, 0);
                } else {
                    outRect.set(0, 0, 0, decoration);
                }
            } else {//水平
                if (current == lastPosition) {//判断是否为最后一个item
                    outRect.set(0, 0, 0, 0);
                } else {
                   outRect.set(0, 0,decoration,  0);
                }
            }
        }
    }
}

Acivity中,初始化RecyclerView的下以:

//设置ItemDecoration
rv.addItemDecoration(new RVItemDecoration(LinearLayoutManager.VERTICAL,30));

运行后效果

新普金娱乐 2

添加脚间距

由于是入门学习,暂时也唯有是对对LinearLayoutManager举行了好几简处理,最后1独item不再添加底部间距。实际支出之上考虑的将较就纷繁的大半。LinearLayoutManager大部早晚考虑itemposition就可以,但GridLayoutManagerStaggeredGridLayoutManager消考虑行和列,情况就算比较复杂。


道中出4独参数

  • Rect
    outRect:可以简单明了为item季限边去奉封装于这个目标吃,用来安Itempadding
  • View view:
    childView,就是item,可以领略啊item的根View,并无是item中之控件
  • RecyclerView parent:就是RecyclerView自身
  • RecyclerView.State state : RecyclerView的状态,但连无含滑动状态

1.2.1 RecyclerView.State <p>

这个看似是RecyclerView的一个静态内部类,源码中之说明:

Contains useful information about the current RecyclerView state like
target scroll position or view focus. State object can also keep
arbitrary data, identified by resource ids.

私知道:
这个State封装着RecyclerView时下之状态,例如滑动目标的Position抑或分段控件的问题。State对象啊得针对轻易的多寡通过资源id拓展封存要识别

State遭逢发生3单用于标记当前所处步骤的常量值:

  • STEP_START :布局开始
  • STEP_LAYOUT :布局中
  • STEP_ANIMATIONS :处于动画中

RecyclerView的劳作流程自然吗会见是measure,layout,draw。3个值在RecyclerViewonMeasure()有动,感觉是用来标识RecyclerView当测量过程中所处于的异时。目前连无亮具体的影响,RecyclerView工作流程需要以后再也拓展深入上

方法 作用
getItemCount() 得到整个RecyclerView中,目前的item的数量
isMeasuring() 是否正在测量
isPreLayout() 是否准备进行布局
get(int resourceId) 根据资源id获取item中的控件,建议使用R.id.*
put(int resourceId, Object data) 添加一个指定id映射的资源对象,建议使用R.id.*来避免冲突
remove(int resourceId) 根据使用R.id.*指定id来删除存入的控件对象
getTargetScrollPosition() 返回已经可见的滑动目标在Adapter的索引值,滑动目标由SmoothScroller来指定
hasTargetScrollPosition() 判断是否已经滑动到目标
willRunPredictiveAnimations() 判断是否进行预测模式的动画在布局过程中
willRunSimpleAnimations() 判断是否进行简单模式的动画在布局过程中

getItemCount()并无是了等于getAdapter.getItemCount(),在源码的诠释中,关于postion的精打细算,建议用State.getItemCount()一经不即直接通过Adapter

State聊计和性涉及到另外的好像,有些关系RecyclerView的做事过程,目前本人的习水平呢非是格外了解,暂时并无打算继续深开掘学习下,总看知道有错误,知道的同室要指出


1.3 onDraw(Canvas c, RecyclerView parent, State state)绘制装饰 <p>

本条用于绘制divider,绘制在item的下一层,也就是说item会盖在divider所在层的方

使还写了onDrawer()方法和onDrawOver()ItemDecoration后,对RecyclerView在绘制item时有些影响,主要是由于绘制顺序:

mItemDecoration.onDraw()-->item.onDraw()--->mItemDecoration.onDrawOver()

onDraw()办法好呢divier装绘制范围,并且绘制范围可以超越在
getItemOffsets
中设置的限定,但由是当item脚一重叠开展绘图,会在overdraw


简利用,完整代码

public class RVItemDecoration extends RecyclerView.ItemDecoration {
    private final int orientation;//方向
    private final int decoration;//边距大小 px
    private final int lineSize ;//分割线厚度
    private final ColorDrawable mDivider;

    public RVItemDecoration(@LinearLayoutCompat.OrientationMode int orientation, int decoration, @ColorInt int color, int lineSize) {
        mDivider = new ColorDrawable(color);
        this.orientation = orientation;
        this.decoration = decoration;
        this.lineSize = lineSize;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        final int lastPosition = state.getItemCount() -1;//整个RecyclerView最后一个item的position
        final int current = parent.getChildLayoutPosition(view);//获取当前要进行布局的item的position
        if (current == -1) return;
        if (layoutManager instanceof LinearLayoutManager && !(layoutManager instanceof GridLayoutManager)) {//LinearLayoutManager
            if (orientation == LinearLayoutManager.VERTICAL) {//垂直
               if (current == lastPosition) {//判断是否为最后一个item
                    outRect.set(0, 0, 0, 0);
                } else {
                    outRect.set(0, 0, 0, decoration);
                }
            } else {//水平
                if (current == lastPosition) {//判断是否为最后一个item
                    outRect.set(0, 0, 0, 0);
                } else {
                    outRect.set(0, 0, decoration, 0);
                }
            }
        }
    }

  /**
     * 绘制装饰
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (orientation == LinearLayoutManager.VERTICAL) {//垂直
            drawHorizontalLines(c, parent);
        } else {//水平
            drawVerticalLines(c, parent);
        }
    }

    /**
     * 绘制垂直布局 水平分割线
     */
    private void drawHorizontalLines(Canvas c, RecyclerView parent) {
         //  final int itemCount = parent.getChildCount()-1;//出现问题的地方  下面有解释
        final int itemCount = parent.getChildCount();
        Log.e("item","---->"+itemCount);
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        for (int i = 0; i < itemCount; i++) {
            final View child = parent.getChildAt(i);
            if (child == null) return;
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top +lineSize;
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 绘制水平布局 竖直的分割线
     */
    private void drawVerticalLines(Canvas c, RecyclerView parent) {
        final int itemCount = parent.getChildCount();
        final int top = parent.getPaddingTop();
        for (int i = 0; i < itemCount; i++) {
            final View child = parent.getChildAt(i);
            if (child == null) return;
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int bottom = child.getHeight() - parent.getPaddingBottom();
            final int left = child.getRight() + params.rightMargin;
            final int right = left +lineSize;
            if (mDivider == null) return;
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

运行后的意义:

新普金娱乐 3

绘制的有割线

平等这里呢单独是考虑了无与伦比简易的LinerLayoutManager一如既往种植状况。使用此措施时,注意绘制范围,尽量避免overdraw

当间距小于分割线的肥瘦时,分割线绘制的厚度会维持同间距一样


1.3 onDrawOver(Canvas c, RecyclerView parent, State state) 绘制蒙层<p>

斯点子是在itemonDraw()措施后进展回调,也即绘制在了不过上重叠

简言之利用,绘制一个颜料红黄渐变的一揽子

 @Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDrawOver(c, parent, state);
    //画笔
    final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //圆心 x 坐标
    final float x = parent.getWidth() / 2;
    ////圆心 y 坐标
    final float y = 100;
    //半径
    final float radius = 100;
    //渐变着色器 坐标随意设置的
    final LinearGradient shader = new LinearGradient(x-50, 0, x+100, 200, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT);
    paint.setShader(shader);
    //绘制圆
    c.drawCircle(x, y, radius, paint);
}

新普金娱乐 4

绘制一个到家

要是手指在RecyclerView达到拓展滑动,onDrawOver()方就会见受回调。但onDrawOver()每回调一不成,会拿上次之绘图清除,只有最终一坏的绘图会吃保存。也就是说绘制的蒙层在屏幕就会发一个


2. 赶上的问题<p>

以绘制的有割线的当儿,遇到一个题目:

新普金娱乐 5

撞的问题

当快速滑动时,底部会闪动,造成体验不好,如果分割线比较狭窄,不是坏明确,分割线宽的时候就杀肯定

现已解决 ,原因分析在下面


2.1 补充,问题修复 <p>

问题由:
题材有在drawHorizontalLines()方法中final int itemCount = parent.getChildCount()-1即时行代码,之所以减一设想的是为使末段一个item下,不用再绘制分割线。

RecyclerView.getChildCount()术的回来值并无是recyclerViewAdapter遭到享有的item的数,而是当前屏幕被冒出在RecyclerViewitem的数量,一个item单单使露出一点点,就算出现,就见面吃含有在内。

-1便会见导致RecycelrView统计已起的item常之数据少一个,就会见促成滑动过程中,屏幕中最终一个item的的有割线不开展绘图,造成闪屏


解决办法:

不减1,就OK,修改为:

final int itemCount = parent.getChildCount();

注意:
ViewGroupgetChildCount()计的返回值itemCount便是
getChildAt(int index)此办法index的间隔上限
[0,itemCount)。例如:

新普金娱乐 6

position示例

此时此刻屏幕显示的凡25--到-->42parent.getChildCount()的回到结果itemCount便是18。凡是在屏幕上首先单冒出的itemindex便是0,哪怕仅是漏出一点点。在parent.getChildAt(int index)中,index的取值范围就是0<= index < 18

2016.10.17 13:48


3.0 补充 官方推出DividerItemDecoration <p>

2016.10.20
Android support libraries更新了25.0.0,新增了BottomNavigationView,并增加了一个官方版的DividerItemDecoration,可以上下代码,有有科学的底细优化

上述信息于drakeet 博客得知,果然关注大神,能够多询问信息


3. 最后 <p>

作一个青铜5的健儿,也是热衷LOL的,也不无相同颗王者心,可RNG,EDG全输了,止步8强,郁闷

本人很菜,有误请指出

一个完好的习:TitleItemDecoration

慕课有一个科学的视屏勿雷同的RecyclerView优雅兑现复杂列表布局

共勉 :)

相关文章