0代码0侵入的安卓骨架屏框架----二期优化

本文是对自定义骨架屏框架的优化说明。

针对目前对骨架屏的需求及为了实现骨架屏而付出的繁重劳动,而设计的一款0编码0业务侵入的骨架屏框架。感兴趣的可以先去看看这篇文章:一种简单的Android骨架屏实现方案----0侵入0成本

额,如果不看,其实可能也不理解这篇是在说什么(⊙o⊙)…

 列表样式优化

将列表的 item UI样式二维数组化,通过配置对应的二维坐标,来实现列表样式到骨架屏灰条的准确映射。

问题

列表样式的妥协始终是一个无法抹去的问题,虽说一个项目的列表按理来说应该就几种主要样式,但实际列表都是各不相同的。原始列表样式到骨架屏灰块样式无法准确对应,再多的理由也无法说服产品同学妥协。

且从用户的角度来看,确实是一种非友好的体验,虽然可能并不影响用户使用。
从开发者的角度来看,这种妥协即使有补救的措施,但方式上并不可取。开发者在自定义样式时是需要直接修改源码的。很显然,违背了设计模式的原则-开闭原则:对修改关闭,对扩展开放。
用一句通俗的话来说,那就是不够优雅啊。


那如何优雅的实现定制化的列表灰块样式呢,并且依然尽可能少的敲代码?


灵感来源

框架之前内置了三种简单的列表样式(上文有提及和配图)。在完善代码注释的时候,想告诉开发者这三种样式是什么样子的。但是注释里又不能配图,如何表达呢?思来想去之际,脑海中忽然闪现过这个注释图:

可以用标点符号拼出一个图来示意。于是框架中就有了下面这段注释。

style 1 表示左边是图片,右边跟着两行字符串
style 2 表示上面是一个方图,下面跟着两行字符串。

不知大家能否看出来,这些组合是不就是一个二维数组,有的是1,表示灰色单位,多个1连起来就是一条灰块。有的是0,表示空白。

对于style 1来说,是不就是一个类似这样的二维数组:

表示,将屏幕宽度均分为 8 列,
第一行,坐标从0到1的位置值是1 ,表示是灰色,坐标从3到7的位置值是1 ,表示灰色,其余位置为0 ,表示空白。
第二行,坐标从0到1的位置值是1 ,表示是灰色,坐标从3到7的位置值是1 ,表示灰色,其余位置为0 ,表示空白。


方案设计

通过组装一个ItemParams 类型的对象,来实现定制列表样式。
listviewItemType 依然是用来配置列表样式的字段,其值改为 ItemParams 类类型

class ItemParams(var params: List<LineSegmentGroup>) {
    var columnNum = 20  //表示屏幕宽度分成多少列,影响的是一个单位的灰块的大小
    var rowNum = 10  //行数,暂时没用


   //将灰块按行分组
    class LineSegmentGroup(
        var line: Int, //行数
        var graySegments: List<GraySegment>  //当前行内的  各个灰块片段
    )

   //一个灰块片段, segment表示片段的起始坐标,mergeGap表示当前灰块和上一行的灰块是否融合为一起,没有间隙。
    class GraySegment(var segment: Pair<Int, Int>, var mergeGap: Boolean = false)
}

上面的二维数组转换成代码配置如下:

val line0 = ItemParams.LineSegmentGroup(
    0,
    listOf(
        ItemParams.GraySegment(0 to 1),
        ItemParams.GraySegment(3 to 7)
    )
)
val line1 = ItemParams.LineSegmentGroup(
    1,
    listOf(
        ItemParams.GraySegment(0 to 1, mergeGap = true),
        ItemParams.GraySegment(3 to 7)
    )
)

val itemType = ItemParams(listOf(line0, line1))
itemType.columnNum = 8

listviewItemType  =itemType //配置完毕

灰块如何计算出来的呢?


首先在父view RecyclerView或者listView 的Rect 范围内,将 rect按找 列数切分为一个个单元,然后根据配置的片段的起止坐标,来计算出灰块片段的长度,即 灰块rect 的 left和 right。根据当前所在的行数,来计算出灰块rect 的高度,即 top和bottom值。

不太清楚在说什么的还是建议去看一下之前的文章:
一种简单的Android骨架屏实现方案----0侵入0成本

项目实例

我们要实现下面列表UI的骨架屏灰块展示

我们可以大概将一个item对应到一个 8 × 10 的二维数组

展示UI的地方,就是红框围起来的,也就是骨架屏时要展示出灰色的地方,也就是数组值为1 的地方。

再将上面这个item准确的映射到一个数组中,黄色即灰块的位置。

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7

一个item的骨架屏样式就变成了一个包含着 0和1值的二维数组,
LineSegmentGroup类: 表示一行,其中包含本行中的各个小灰块,
line: 行数,
graySegments: 本行内的灰块列表

GraySegment类:表示一块单独的灰块片段
segment:Pair<Int, Int>: 表示一个灰块的从左到右的起止点坐标,
mergeGap: 表示是否顶部和上一行合并,即不保留此灰块的行间隙。
 

对应到代码配置中:

config {
    listviewItemType = ItemParams(
        listOf(
            ItemParams.LineSegmentGroup(
                line = 0,
                graySegments = listOf(
                    ItemParams.GraySegment(0 to 1),
                    ItemParams.GraySegment(3 to 5),
                    ItemParams.GraySegment(8 to 9),
                )
            ),
            ItemParams.LineSegmentGroup(
                line = 1,
                graySegments = listOf(
                    ItemParams.GraySegment(0 to 1, true),
                    ItemParams.GraySegment(3 to 5),
                    ItemParams.GraySegment(9 to 9)
                )
            ),
            ItemParams.LineSegmentGroup(
                line = 2,
                graySegments = listOf(
                    ItemParams.GraySegment(0 to 1, true),
                    ItemParams.GraySegment(3 to 7)
                )
            ),
            ItemParams.LineSegmentGroup(
                line = 4,
                graySegments = listOf(
                    ItemParams.GraySegment(3 to 9)
                )
            ),
            ItemParams.LineSegmentGroup(
                line = 5,
                graySegments = listOf(
                    ItemParams.GraySegment(3 to 9)
                )
            ),
            ItemParams.LineSegmentGroup(
                line = 6,
                graySegments = listOf(
                    ItemParams.GraySegment(3 to 4),
                    ItemParams.GraySegment(6 to 7)
                )
            ),
            ItemParams.LineSegmentGroup(
                line = 7,
                graySegments = listOf(
                    ItemParams.GraySegment(3 to 6),
                    ItemParams.GraySegment(8 to 8),
                    ItemParams.GraySegment(9 to 9)
                )
            )

            )
    )
    loadingAnim = ILoadingAnimtor.TYPE_BAI_JV_GUO_XI
    skeletonEnable = true
    customLoadingAnim = null
}

 展示出的骨架屏效果:

如果想要继续的精细化展示,需要调整分隔的最小块单元大小即可。
即调整对应的表格的行列长度。
ItemParams 类的两个参数:
columnNum: 将屏幕宽度分隔成多少行
rowNum: item 最多由多少行组成,这个参数目前没用,

也就是说  columnNum 值越大,越精细还原item样式。

下面的效果是将 columnNum 设置为20 的 灰块效果,

对比一下:

tools属性优化

解决tools属性设置导致展示骨架屏时页面元素都不展示的问题。
基本使用之前的逻辑,为text="" 的textview赋值,等到测量出位置信息时,再清空text值。
之前提到的赋值风险,通过设置一个特定格式的字符串来解决,
我这里设置的标识字符串是:HdFzX.
正常业务是不会出现这个字符串值的。同样在其他项目中,也设置一个类似的即可。
因此,对于在xml 布局中使用tools属性设置text占位文案的页面,在measure 之前,判断这些textview的text是否为空。空则赋值上面的字符串。
等到layout阶段,测量出textview的位置信息后,即可将text恢复为“”值。这里会增加判断当前的text值是否是上面设置的特殊字符串。
如果是,则置为空,如果不是,只能说明是有业务逻辑进来了,那就不动了。
以上赋值和置空的操作加了同步锁进行保护,并且只会在第一次进入页面时执行,在当前页面此后的loading ,normal状态切换时都不会载走这部分逻辑了。因为textview的灰块信息已经计算完了,此后的loading就是绘制这些灰块rect。
喜欢的小伙伴,快去愉快的用起来吧。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值