问题描述
最近在写一个自定义天气折线图的时候遇到一个问题。
在定义一个View的itemCount属性的时候需要用到View的width,我思路是在onLayout()中通过width初始化它的值,因为此时View已经确定了它的宽高了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private var itemCount = 1 set(value) { field = value itemWidth = width / value }
private var itemWidth = width / itemCount set(value) { field = value beginX = value / 2 }
private var beginX = itemWidth / 2
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) itemCount = 5 }
|
但是这段代码出现了一个意料之外的错误,我在为View应用属性动画的时候出现了问题,itemWidth和beginX等属性在动画一开始的时候属性值都为0,在运行了很短的时间后,它们的值才变成我预期的值。
1 2 3 4 5 6 7 8
| //Log信息 D/test: drawPath: itemWidth=0,beginX=0 D/test: drawPath: itemWidth=0,beginX=0 D/test: drawPath: itemWidth=216,beginX=108 D/test: drawPath: itemWidth=216,beginX=108 D/test: drawPath: itemWidth=216,beginX=108 D/test: drawPath: itemWidth=216,beginX=108 D/test: drawPath: itemWidth=216,beginX=108
|
原因探究
我认为是在动画开始之前,View的onLayout()方法未得到调用,此时View.width属性的值还是0,导致我定义的属性值计算出来也是0。在动画运行了一段时间才对onLayout()进行调用,此时我定义的属性值才恢复正常。
解决办法
我的思路是将使用View.width的代码块暂时阻塞,等到onLayout()方法被调用,View.width的值不为0的时候,再唤醒前面的代码块,这样使得,在使用到View.width时,其必然值不等于0,因此,也能正确计算出itemWidth和beginX等属性的值。
最终,我采用Kotlin的协程async()将代码块暂时阻塞,在isReady=true(代码onLayout()已经被调用)时,唤醒代码块。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private var isReady = false
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) itemCount = 5 isReady = false }
private fun drawPath(posX: Float, maxPosY: Float, minPosY: Float) { MainScope().launch { val defferd = async { } if (isReady) defferd.await() } }
|
近段时间发现这个解决办法存在问题
async在调用之时就会立刻执行它内部的代码块,而不是等到调用await方法后。
因此需要思考其他解决办法了。