RecyclerView 性能优化 _ 把加载表项耗时减半 (三),android模拟器手机版

isAntiAlias = true
textSize = [email protected]
color = textColor
}
}

if (staticLayout == null) {
staticLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaint!!, textWidth)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(spaceAdd, spcaeMult)
.setIncludePad(false)
.build()
}
}

override fun draw(canvas: Canvas?) {
staticLayout?.draw(canvas)
}
}

这样的设计符合依赖倒置原则,即上层类OneViewGroup不依赖下层类Text,它们都依赖一个抽象Drawalbe

这样一来OneViewGroup就又符合开闭原则了,即新增可绘制类型时不需要修改OneViewGroup类,只需要新建一个Drawable的子类即可。

再定义一个扩展方法用于构建Text对象:

inline fun OneViewGroup.text(init: Text.() -> Unit) =
// 构建 Text 实例并应用属性,再加入到 OneViewGroup 中
Text().apply(init).also { addDrawable(it) }

方法被定义为OneViewGroup的扩展方法,这样的好处是只要在OneViewGroup上下文环境中就可以轻松的构建Text实例。

扩展方法传入的参数是一个带接收者的 lambda,它是一种特殊的 lambda,kotlin 中特有的。可以把它理解成“为接收者声明的一个匿名扩展函数”。

带接收者的 lambda 的函数体除了能访问其所在类的成员外,还能访问接收者的所有非私有成员,这个特性使它能够轻松地构建结构。

Text.() -> Unit的接收者是Text,意味着,可以在 lambda 函数体中轻松的设置Text实例的属性。

再配合构造OneViewGroup的扩展法方法:

// 在 Context 上下文中轻松地构建 OneViewGroup 实例
inline fun Context.OneViewGroup(init: OneViewGroup.() -> Unit): OneViewGroup =
return OneViewGroup(this).apply(init)

就可以用声明式的语法来构建布局了:

OneViewGroup {
layout_width = match_parent
layout_height = match_parent

text {
text = “title”
textSize = 40f
textColor = “#ffffff”
textWidth = 200
}

text {
text = “content”
textSize = 30f
textColor = “#ffffff”
textWidth = 300
}
}

上述代码会在OneViewGroup控件的左上角绘制两行文字,不过这两行文字是重叠在一起的,因为还没有指定他们的相对位置。

文字相对布局

staticLayout.draw(canvas)并没有提供绘制坐标的参数。所以只能通过平移画布来实现在不同位置绘制文字:

class Text : Drawable {
var left: Float = 0f
var right: Float = 0f

override fun draw(canvas: Canvas?) {
canvas?.save() // 记忆当前画布位置
canvas?.translate(left, top) // 平移画布到绘制点(left, top)
staticLayout?.draw(canvas) // 绘制文字
canvas?.restore() // 还原当初画布位置
}
}

然后就可以像这样指定文字的绝对位置:

OneViewGroup {
layout_width = match_parent
layout_height = match_parent

text {
text = “title”
textSize = 40f
textColor = “#ffffff”
textWidth = 200
left = 10 // 距离父控件左边 10 像素
top = 20 // 距离父控件顶部 20 像素
}

text {
text = “content”
textSize = 30f
textColor = “#ffffff”
textWidth = 300
left = 10 // 距离父控件左边 10 像素
top = 50 // 距离父控件顶部 50 像素
}
}

用绝对像素值显然不能满足实际项目的要求。像素布局无法解决多屏幕适配的问题,用相对于父控件的绝对位置来布局也不能满足子控件间相对布局的需求。

还记得在RecyclerView 性能优化 | 把加载表项耗时减半 (一)中介绍的PercentLayout吗?,它是一个自定义ViewGroup,其中的子控件有一组相对属性来指定相对位置。将这套相对布局方法移植过来。

相对属性不是Text独有的,应该将它们上提到Drawable中:

abstract class Drawable {
// 用 Int 值作为唯一标识
var id: Int = -1
// 距离父控件左上角的百分比值
var leftPercent: Float = -1f
var topPercent: Float = -1f
// 相对布局属性
var startToStartOf: Int = -1
var startToEndOf: Int = -1
var endToEndOf: Int = -1
var endToStartOf: Int = -1
var topToTopOf: Int = -1
var topToBottomOf: Int = -1
var bottomToTopOf: Int = -1
var bottomToBottomOf: Int = -1
var centerHorizontalOf: Int = -1
var centerVerticalOf: Int = -1
// 业务层指定的宽高
var width = 0
var height = 0
// 上下左右边距
var topMargin = 0
var bottomMargin = 0
var leftMargin = 0
var rightMargin = 0
// 用于保存测量宽高结果的变量
var measuredWidth = 0
var measuredHeight = 0
// 上下左右用于描述可绘制对象所处矩形
var left = 0
var right = 0
var top = 0
var bottom = 0
// 上下左右内边距
var paddingStart = 0
var paddingEnd = 0
var paddingTop = 0
var paddingBottom = 0

// 如何测量及绘制由子类定义
abstract fun measure(widthMeasureSpec: Int, heightMeasureSpec: Int)
abstract fun draw(canvas: Canvas?)

// 布局的结果保存在上下左右四个变量组成的矩形中
fun setRect(left: Int, top: Int, right: Int, bottom: Int) {
this.left = left
this.right = right
this.top = top
this.bottom = bottom
}

// 测量的结果保存在 measuredWidth 和 measuredHeight
fun setDimension(width: Int, height: Int) {
this.measuredWidth = width
this.measuredHeight = height
}
}

Drawable新增了很多属性,用于描述它的尺寸及相对位置。还新增了两个方法用于保存测量和布局的结果。因为同时存在抽象和非抽象方法,就把原先的接口重构成了抽象类。

然后重写onLayout()以定位所有Drawable对象相对于父控件的位置:

class OneViewGroup
&

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值