春节后,又重新“开张”。各位高手请继续支持。谢谢!
原文标题:Kotlin recipes for Android (I): OnGlobalLayoutListener
原文链接:http://antonioleiva.com/kotlin-ongloballayoutlistener/
原文作者:Antonio Leiva(http://antonioleiva.com/about/)
原文发布:2016-03-16
今天一位同伴问我怎样恰当使用OnGlobalLayoutListener,而不需要太多的模板。这是一个棘手的问题,我们需要进一步深入研究。
OnGlobalLayoutListener是干什么的?
这个侦听器对于任何试图的ViewTreeObserver都适用,并且在已知视图宽度和高度进行各种计算、动画等等时,为扩展和测量视图常常回调它。
幸亏Kotlin提供很好的与Java互操作性,我们能够以一种非常清晰的方法 —— 使用它的模拟属性和Lambda表达式 —— 实现单一方法接口:
1 recycler.viewTreeObserver.addOnGlobalLayoutListener { 2 // do whatever 3 }
这里有什么问题吗?为了预防泄漏,推荐的实践是在完成使用它后,立即删除这个侦听器。但是由于使用了Lambda表达式,Lambda没有对象那么精确,我们没有对象的引用。
原方式还是可以用,但是在Kotlin中直接用匿名对象,每次都会有一只小猫死去。如果仍然需要做下面这样的事,没法改用更好开发语言:
1 recycler.viewTreeObserver.addOnGlobalLayoutListener( 2 object : ViewTreeObserver.OnGlobalLayoutListener { 3 override fun onGlobalLayout() { 4 recycler.viewTreeObserver.removeOnGlobalLayoutListener(this); 5 // do whatever 6 } 7 });
找一个更佳替换方法
好了,既已知不要那样做。那么有什么更好的方法吗?我们被迫使用一种看上去没有那么好看的方法,但是可能是一种好的选择,将扩展函数隐藏起来。
为视图接收另一个函数创建一个新函数,由它自己创建和删除侦听器。就像这样:
1 inline fun View.waitForLayout(crossinline f: () -> Unit) = with(viewTreeObserver) { 2 addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { 3 override fun onGlobalLayout() { 4 removeOnGlobalLayoutListener(this) 5 f() 6 } 7 }) 8 }
现在你就可以调用这个函数,确保其自己添加和删除侦听器。除非,你永远不会忘记删除:
1 recycler.waitForLayout { 2 // do whatever 3 }
如果喜欢,可以用扩展ViewTreeObserver的函数,而不是直接用View。这取决你。
但是我们仍可以改进它
这是在测试视图后布局侦听器通常要做的一些事,所以需要等待宽度和高度大于0。而且可能要在视图中调用它时做一些事,这为什么不能转换参数函数到扩展函数?
我还泛型该函数使它能够在任何继承View的对象中使用,也能够从编写的函数中访问所有它的指定函数和属性。
1 inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) { 2 viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { 3 override fun onGlobalLayout() { 4 if (measuredWidth > 0 && measuredHeight > 0) { 5 viewTreeObserver.removeOnGlobalLayoutListener(this) 6 f() 7 } 8 } 9 }) 10 }
这个afterMeasured函数非常类似前者,但是在Lambda表达式内直接用视图的属性和public方法。例如,我们能够得到recycler的宽度和基于它用列的动态数组设置布局。
1 recycler.afterMeasured { 2 val columnCount = width / columnWidth 3 layoutManager = GridLayoutManager(context, columnCount) 4 }
总结
在Android中运行时,这确实仍有些事情做的不是很好,即使是移植Kotlin,但是总是可以通过隐藏在其他结构背后的不确定因素,找到提升可读性和避免不确定因素的选择。至少,仅需要编写一次,而其它代码则非常漂亮!