带有吸附效果的RecyclerView装饰器

通过本文你讲了解到: 1.RecyclerView.ItemDecoration的一般写法 2.View的DrawingCache的相关内容 3.Kotlin的简单语法 4.这个一个支持任意分组的,任意布局的Item装饰。外加可以顶部吸附效果(当然你不喜欢,也可以不启用)

灵感来源:(http://www.jianshu.com/p/b335b620af39 ) 先偷一张图,想要的效果就是这样的。

然后是我写的代码的效果:

####1.先说说ItemDecoration#### ItemDecoration主要一下三个方法,可以给Item添加装饰,这就是装饰器的意思,一般用来做分割线。

public void onDraw(Canvas c, RecyclerView parent, State state)
public void onDrawOver(Canvas c, RecyclerView parent, State state)
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
复制代码

onDraw是在Item内容之前绘制,onDrawOver是在Item内容之上绘制,getItemOffsets是给Item设置一个偏移量,来给装饰器留下绘制的空间。

再偷一个图:

本次的效果,顶部悬浮的装饰是在onDrawOver中绘制,其他的装饰是在onDraw中绘制,getItemOffsets中判断分组的第一个,设置偏移。

####2.然后是DrawingCache####

绘制装饰器是先通过cache机制,将装饰器保存为bitmap,然后用canvas绘制上去。

我们要获取cache首先要通过setDrawingCacheEnable方法开启cache,然后再调用getDrawingCache方法就可以获得view的cache图片了。

buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,若果cache没有建立,系统会自动调用buildDrawingCache方法生成cache。因为会更新装饰器的内容, 所以要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。

当调用setDrawingCacheEnabled方法设置为false, 系统也会自动把原来的cache销毁。

####3.最后是代码####

package top.greendami.mykotlinapp

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup


/**
 *
 * Created by GreendaMi on 2017/6/14.
 */
class SectionDecoration<T>(var context: Context, var dataList: ArrayList<T>, val layoutId: Int, val groupListener: GroupListener,var isFloat : Boolean = true, var sectionLayout: ViewGroup = LayoutInflater.from(context).inflate(layoutId, null) as ViewGroup) : RecyclerView.ItemDecoration() {

    var lastBitmap: Bitmap? = null
    var firstTop: Int = 0

    init {
        val layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
        sectionLayout.layoutParams = layoutParams
        sectionLayout.measure(
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
    }

    //绘制分割区域
    override fun onDraw(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        super.onDrawOver(c, parent, state)
        val left = parent!!.paddingLeft
        for (i: Int in 0..parent.childCount) {
            if (groupListener.isFirst(parent.getChildAdapterPosition(parent.getChildAt(i)))) {
                //设置内容
                groupListener.setContnt(sectionLayout, parent.getChildAdapterPosition(parent.getChildAt(i)))
                //设置内容后重新测量(此处默认父布局宽度)
                sectionLayout.measure(
                        View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY),
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
                sectionLayout.layout(0, 0, parent.width - parent.paddingRight, sectionLayout.measuredHeight)
                sectionLayout.isDrawingCacheEnabled = true
                sectionLayout.buildDrawingCache(true)
                c!!.drawBitmap(sectionLayout.drawingCache, left.toFloat(), (parent.getChildAt(i).top - sectionLayout.measuredHeight).toFloat(), null)
                //获取图像(保存滚出屏幕的最后一个)
                if ((parent.getChildAt(i).top - sectionLayout.measuredHeight) <= 0) {
                    lastBitmap = Bitmap.createBitmap(sectionLayout.drawingCache)
                }
                sectionLayout.destroyDrawingCache()
                sectionLayout.isDrawingCacheEnabled = false
            } else {
                continue
            }
        }
    }

    //如果开启了悬浮,绘制悬浮的那个分割区域
    override fun onDrawOver(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        super.onDrawOver(c, parent, state)
        if(!isFloat){
            return
        }
        val left = parent!!.paddingLeft
        for (i: Int in 0..parent.childCount) {
            if (groupListener.isFirst(parent.getChildAdapterPosition(parent.getChildAt(i)))) {
                //parent.getChildAt(i).top是Item内容的高度,不包含Decoration的高度;sectionLayout.measuredHeight是Decoration的高度
                if (parent.getChildAt(i).top in sectionLayout.measuredHeight..sectionLayout.measuredHeight * 2) {
                    firstTop = parent.getChildAt(i).top - sectionLayout.measuredHeight * 2
                    c!!.drawBitmap(lastBitmap, left.toFloat(), firstTop.toFloat(), null)
                    //发现是交换的过程,绘制完交换后的Decoration后,不再绘制top位置是0的Decoration
                    return
                } else {
                    firstTop = 0
                }
            } else {
                firstTop = 0
            }
        }
        //绘制top位置是0的Decoration
        c!!.drawBitmap(lastBitmap, left.toFloat(), firstTop.toFloat(), null)
    }

    //每个Item给留出分割区域的绘制控件
    override fun getItemOffsets(outRect: Rect?, itemPosition: Int, parent: RecyclerView?) {
        super.getItemOffsets(outRect, itemPosition, parent)
        if (groupListener.isFirst(itemPosition)) {
            outRect!!.top = sectionLayout.measuredHeight
        }
    }

}

abstract class GroupListener {
    abstract fun isFirst(position: Int): Boolean
    abstract fun setContnt(contentView: ViewGroup, position: Int)
}
复制代码

//获取图像(保存滚出屏幕的最后一个) if ((parent.getChildAt(i).top - sectionLayout.measuredHeight) <= 0) { lastBitmap = Bitmap.createBitmap(sectionLayout.drawingCache) } 是在寻找最后一个滚出屏幕的装饰器,用于绘制吸附效果。

if (parent.getChildAt(i).top in sectionLayout.measuredHeight..sectionLayout.measuredHeight * 2) { firstTop = parent.getChildAt(i).top - sectionLayout.measuredHeight * 2 c!!.drawBitmap(lastBitmap, left.toFloat(), firstTop.toFloat(), null) //发现是交换的过程(后一个把前一个顶出的过程),绘制完交换后的Decoration后,不再绘制top位置是0的Decoration return }

后一个把前一个顶出的过程,计算被顶出的装饰器的top坐标。

外部回调,list是一个普通的RecycleView,R.layout.header是装饰器的布局。


list.addItemDecoration(SectionDecoration(this, datas, R.layout.header, object : GroupListener() {
override fun isFirst(position: Int): Boolean {
return position % 5 == 0
}

override fun setContnt(contentView: ViewGroup, position: Int) {
(contentView.findViewById(R.id.text) as TextView).text = "标题:" + datas[position]
}

}))
复制代码

Github( https://github.com/GreendaMi/PPColorPicker/blob/master/app/src/main/java/top/greendami/mykotlinapp/PPSectionDecoration.kt )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值