Android使用RecyclerView和StaggeredGridLayoutManager实现瀑布流效果-装饰

版权声明:本文为博主原创文章,未经博主允许不得转载。https://blog.csdn.net/sinat_25074703/article/details/83216019

8、实现StaggeredItemDecoration定制分割线

在实际的项目开发过程中,为了更好的用户体验,分割线是必不可少的,甚至有异想天开的产品会脑补出千姿百态的分割效果。这里主要通过简单的直线分割来阐述RecyclerView#ItemDecoration的风采。

8.1从定义说起

Google官方对RecyclerView#ItemDecoration的定义如下:

/**
* 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.
*
*

All ItemDecorations are drawn in the order they were added, before the item
* views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
* and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
* RecyclerView.State)}.


*/

译文:

ItemDecoration允许(既然是允许,不添加也不犯法)应用添加一种特殊的绘制和布局补偿系数(注意这两个词汇,绘制是为了渲染,绘在什么地方由补偿系数说了算)到指定的item views中,这些item views来自于adapter数据源(这里的adapter指的是前面提到的RecyclerView#Adapter,具体到本项目是StaggeredAdapter)。这种被允许的方式一定(注意情态动词can be)是有用的,哪里有用?对于绘制items之间的分割线,高亮,可视化分组分割线等一系列需求都是有用的。
*
所有的ItemDecorations按照被添加的顺序绘制(这一句信息量大啊!他告诉我们ItemDecorations可以随意添加,无论多少个。但是他们要按照添加的顺序排队绘制,先绘制的或许会被覆盖看不到。砌墙的砖头——后来居上嘛!),所有的这些ItemDecorations的添加时间段是在ItemDecoration#onDraw()方法被调用之前(我能理解,不然无法绘制), 在ItemDecoration#onDrawOver()之后(我理解不了啊,因为没有意义啊!在此,我大胆推测Google想表达的是,绘制期间不能有添加逻辑,否则就会报异常)。

看完了定义,我们可以确定RecyclerView#ItemDecoration的一些关键信息:使用前需要先绘制,而绘制使用的onDraw()和onDrawOver()方法。那么我们来看一下这个类吧!

8.2类及其方法

    public abstract static class ItemDecoration{
    

    }

// 这是一个共有的静态抽象内部类,从这点来说跟ViewHolder和Adapter没啥两样,都是待价而沽,静候明主。
既然是抽象类必然有抽象方法,如下图:

截图中清晰地展示ItemDecoration的三个抽象方法onDraw()、onDrawOver()、getItemOffsets(),有人说你瞎吗?明明是6个。我不瞎,另外三个是同名过期的方法。

  1. onDraw()
        /**
         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
         * Any content drawn by this method will be drawn before the item views are drawn,
         * and will thus appear underneath the views.
         *
         * 绘制任何适合的decorations进入到画布中,这张画布是提供给RecyclerView使用的(这个当然,否则如何保证同步呢)
         * 任何被这个方法绘制的内容都是在item views(指的是RecyclerView的每一个ItemView)被绘制之前绘制,并且(该方法绘制的东西)将出现在所有view的最底层。【这一段传递两个信息,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, parent);
        }
  1. onDrawOver()
       /**
         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
         * Any content drawn by this method will be drawn after the item views are drawn
         * and will thus appear over the views.
         * 绘制任何适合的decorations进入到画布中,这张画布是提供给RecyclerView使用的(这个当然,否则如何保证同步呢)
         * 任何被这个方法绘制的内容都是在item views(指的是RecyclerView的每一个ItemView)被绘制之后绘制,并且(该方法绘制的东西)将出现在所有view的最顶层。【这一段传递两个信息,ItemDecoration会被最后绘制,它在最顶层。换句话说,如果不做布局上的调整,天天都能看到它】
         * @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);
        }
  1. getItemOffsets()
       /**
         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
         * the number of pixels that the item view should be inset by, similar to padding or margin.
         * The default implementation sets the bounds of outRect to 0 and returns.
         * 译文:
         * 取回指定item的任何补偿系数(这里指的是布局补偿系数)。参数outRect的每个属性是以item应该被插入的像素数为基准的(就是说item上下左右都可以插入,但插入的是一个矩形,就算他在视觉上是一条线,它的本质是一个矩形),类似于margin或padding
         * 默认outRect的边界设置为0,就是没有布局。
         * <p>
         * If this ItemDecoration does not affect the positioning of item views, it should set
         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
         * before returning.
         * 译文:
         * 如果该ItemDecoration不影响item views的位置(比如绘制的位置没有交叉重叠),那么outRect的四个属性值都可以设置为0,但是需要在返回之前设置。
         * <p>
         * If you need to access Adapter for additional data, you can call
         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the View.
         * 译文:如果你需要访问Adapter里的其他数据的话,可以通过调用RecyclerView#getChildAdapterPosition(View)方法来获取中指定位置的view
         * 
         * @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);
        }

通过源码追踪不难发现,getItemOffsets()最终会被StaggeredGridLayoutManager的onLayoutChildren()调用【onLayoutChildren()—>fill()—>measureChildWithDecorationsAndMargin()—>calculateItemDecorationsForChild()—>getItemDecorInsetsForChild()—>getItemOffsets()】,StaggeredGridLayoutManager是RecyclerView#LayoutManager的派生类,onLayoutChildren()是其派生方法,定义在RecyclerView中,用于完成布局任务。结合View的绘制流程:测量(onMeasure)——布局(onLayout)——绘制(onDraw),ItemDecoration的执行流程应该是线调用getItemOffsets,接着调用onDraw,最后调用onDrawOver

8.3实现RecyclerView#ItemDecoration绘制分割线

在项目文件夹中找到staggered文件夹右键创建StaggeredItemDecoration并继承自RecyclerView.ItemDecoration,代码如下:

package com.edwin.idea.staggered;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值