自定义LayoutManager

在做了这么长时间的Android开发,还没有遇到过这个需求,不过看了别人的很多效果,感觉很棒,所以找了时间就研究了一下,现在做一些记录,等以后有了相关需要可以快速回顾。

在我学习的过程中,不可避免的遇到了很多问题,有的已经解决,有的还未解决,所以这个 Demo 就是看个乐呵吧。

自定义一个 LayoutManager 整体给我的感觉是与实现自定义 ViewGroup 的 onLayout 比较像。其他的测量绘制方法都不需要我们实现,测量方法还有很多可以直接使用的:

androidx.recyclerview.widget.RecyclerView.LayoutManager#measureChild
androidx.recyclerview.widget.RecyclerView.LayoutManager#measureChildWithMargins

这两个方法可以测量 child 的大小,一个不计算 child.layoutParams,一个计算。

测量完成之后,我们就可以获取 child 的大小了:

androidx.recyclerview.widget.RecyclerView.LayoutManager#getDecoratedMeasuredWidth
androidx.recyclerview.widget.RecyclerView.LayoutManager#getDecoratedMeasuredHeight

这里不直接使用 child.getMeasureWidth 显然是因为 RecyclerView 是有一个 ItemDecorate 可以设置,不设置就是获取的 getMeasureWidth 的值。

更完美的是,layout child 的时候,也有相应的方法:

androidx.recyclerview.widget.RecyclerView.LayoutManager#layoutDecoratedWithMargins

使用这个方法,我们就可以将 child 摆到我们想要摆放的位置了,我们只需要传递四大金刚的位置:l,t,r,b。网上看到一个效果就是,它计算出某个 path 的所有点,然后存起来,根据滚动的距离,来取相应的点,然后根据这个点以及child的大小,就可以将这个 child 摆到 path 的路径上,实现一个 item 跟随 path 的效果。只要想清楚了还是不难的。

了解这些,我们还需要了解一下 RecyclerView 的回收机制,因为 RecyclerView 最吊的地方就是回收复用,如果你搞了一个 LayoutManager 但是却无法回收复用,那岂不是很沙雕,关于回收这里就不仔细讲了,看[我的另一篇文章](https://github.com/aprz512/blog4aprz512/blob/master/Blog/Android-View/RecyclerView 的缓存机制.md) 吧。

有了上面这些基础,我们就可以开始动手写了。

首先,我们需要确定我们想要的效果,我们先看一下这个效果图:

[外链图片转存失败(img-FzCl7VoA-1565004606632)(https://github.com/aprz512/pic4aprz512/blob/master/Blog/Android-View/layoutmanager/Gif_20190805_140935.gif?raw=true)]

可以看到:

  • 最左边,是有几个 item 堆在一起了的,那么是怎么实现的呢,其实就是 layout 的时候将 item 之间摆进一点就好了。比如:item 的宽度是 100,高度是 150,第一个item的位置为 【(0,0),(100, 150)】,第二个 item 的位置(假设 item 的 divider 宽度为 0)为 【(100, 0),(200, 150)】。但是我在摆第二个 item 的时候,我偏不从 100 开始摆,我从 20 开始摆,那么第二个 item 就叠在第一个 item 上面了。
  • 右边的就简单了,按照通常的摆法就好了,不搞啥幺蛾子。

对效果了然于胸,我们就可以开始敲代码了,首先自然是继承父类:

androidx.recyclerview.widget.RecyclerView.LayoutManager#LayoutManager

它只有一个抽象方法:

    override fun generateDefaultLayoutParams(): LayoutParams {
   
        return LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT
        )
    }

首先,网上一致都是这样实现的,我就很蛋疼了,都不说为什么。

然后,我就去查看了注释,它是这样说的:为 RecyclerView 的 child 生成一个默认的 LayoutParams。那么为何要生成一个默认的 LayoutParams 呢?比如,有的同学在 Adapter 里面加载布局的时候,parent 会传 null,这个时候 child 的就是没有 LayoutParams 的,所以需要生成一个。同样的,PopupWindow 也会遇到。这里我就清楚了,传递 WRAP_CONTENT 是一个保险行为,有最好,没有就使用这个。

注释还说了,这个返回的是 RecyclerView.LayoutParams,所以如果你还想带一些自带的信息在 LayoutParams 里面,你可以继承这个 LayoutParams,然后实现下面3个方法:

androidx.recyclerview.widget.RecyclerView.LayoutManager#checkLayoutParams
androidx.recyclerview.widget.RecyclerView.LayoutManager#generateLayoutParams(android.view.ViewGroup.LayoutParams)
androidx.recyclerview.widget.RecyclerView.LayoutManager#generateLayoutParams(android.content.Context, android.util.AttributeSet)

就 OK 了。

搞定了唯一的一个抽象方法,我们就可以正常运行了,但是没啥效果,我们还需要实

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值