Android的Kotlin秘方(I):OnGlobalLayoutListener

春节后,又重新“开张”。各位高手请继续支持。谢谢!

 

 

原文标题:Kotlin recipes for Android (I): OnGlobalLayoutListener

原文链接:http://antonioleiva.com/kotlin-ongloballayoutlistener/

原文作者:Antonio Leiva(http://antonioleiva.com/about/

原文发布:2016-03-16 ­

 

Android的Kotlin秘方(I):OnGlobalLayoutListener

 

今天一位同伴问我怎样恰当使用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,但是总是可以通过隐藏在其他结构背后的不确定因素,找到提升可读性和避免不确定因素的选择。至少,仅需要编写一次,而其它代码则非常漂亮!

 

posted @ 2016-03-23 23:33  figozhg  阅读(3775)  评论(2编辑  收藏  举报