使用Kotlin对ViewGroup的视图进行函数使操作
原文标题:Functional operations over Views in ViewGroup using Kotlin
原文链接:http://antonioleiva.com/functional-operations-viewgroup-kotlin/
原文作者:Antonio Leiva(http://antonioleiva.com/about/)
原文发布:2015-07-29

集合、迭代、数组、序列 ... 所有这些共用一套好用的函数,这组函数可帮助对它们的元素进行转换、排序以及其它操作。但是,由于类的构造方法,在Android SDK中,有部分函数还不能用。
例如,我们不能直接获得ViewGroup内部视图列表,所以这些操作是不可能使用的。但是并非所有一切都失去了。在Kotlin中,我们有方法为这些操作准备任何数据。诀窍简单:我们只需要创建一个Sequence。在我们的例子中,Sequence将是一组有序的View。我们只需要实现一个函数,其返回Iterator。
如果我们有了Sequence,函数式操作领域就为我们打开了使用它们的大门。所以让我们从它开始。
注:阅读文章结尾
如lakedaemon666在评论中所建议的那样,有一个不用Sequence的更简单的方法可以获得同样的结果。我会保留原文记录,但是建议你看看替代的解决方案。
创建ViewGroup的Sequence
如前所述,我们将创建迭代器(iterator),它必须知道是否有下一项,下一项是哪个。我们还创建一个扩展函数,为了任何 ViewGroup 和继承类提供一个简单的方法做那项工作:
fun ViewGroup.asSequence(): Sequence<View> = object : Sequence<View> {
override fun iterator(): Iterator<View> = object : Iterator<View> {
private var nextValue: View? = null
private var done = false
private var position: Int = 0
override public fun hasNext(): Boolean {
if (nextValue == null && !done) {
nextValue = getChildAt(position)
position++
if (nextValue == null) done = true
}
return nextValue != null
}
override fun next(): View {
if (!hasNext()) {
throw NoSuchElementException()
}
val answer = nextValue
nextValue = null
return answer!!
}
}
}
检索视图递归列表
获得一个视图列表,并对其进行函数操作是非常有用的。因此,我们可以先创建顶层视图列表,然后,用它以递归方式逐级检索ViewGroup中视图。
让我们为ViewGroup创建新扩展属性。扩展属性非常类似扩展函数,可用于任何类:
public val ViewGroup.views: List<View>
get() = asSequence().toList()
我们可以用这,创建递归函数,它返回布局中任何ViewGroup内部的所有View:
public val ViewGroup.viewsRecursive: List<View>
get() = views flatMap {
when (it) {
is ViewGroup -> it.viewsRecursive
else -> listOf(it)
}
}
用flatMap,我们把全部结果的多个列表转换到一个列表中。它将遍历任何视图;如果是ViewGroup,它还会遍历自己的视图。否则,就返回仅有一项的列表。
用法实例
现在,我们得到viewRecursive属性,执行我们想要的任何操作。这里你可以看到两个例子。我创建这样一个简单的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"/> <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Java"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Hello Kotlin"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="Hello Scala"/> </FrameLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"> <CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check 1"/> <CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check 2"/> <CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check 3"/> <CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check 4"/> </LinearLayout> </RelativeLayout>
例如,在MainActivity.onCreate()中,可应用这段代码。它将Hello Kotlin!串转换为大写字母,选中偶数复选框:
val container: ViewGroup = find(R.id.container)
val views = container.viewsRecursive // Set Kotlin TextView to Upper
val kotlinText = views.first {
it is TextView && it.text.toString().contains("Kotlin")
} as TextView
kotlinText.text = kotlinText.text.toString().toUpperCase() // Set even checkboxes as checked, and odd as unchecked
views filter {
it is CheckBox
} forEach {
with(it as CheckBox) {
val number = text.toString().removePrefix("Check ").toInt()
setChecked(number % 2 == 0)
}
}

替代的解决方案
如lakedaemon666在评论中所提及的那样(谢谢解释),如果之后我们遍历整个sequence,那么创建sequence就没有什么意义了。例如,当按行读取文件时,sequence是懒惰迭代。只有要求必要的项目。而我们却要使用所有项,所以简单的列表就足够了。
另外,有许多简单的方法可以产生视图列表。我们可以依赖值域产生视图的索引列表,将它们映射到我们需要的视图列表中。所有这一切就一行:
public val ViewGroup.views: List<View>
get() = (0..getChildCount() - 1) map { getChildAt(it) }
总结
这是个无聊的例子,但是这个概念或许可使你的所有代码函数化,停止依靠那些典型迭代式编程的循环和其它控制流。
记住从我写的书《Android开发者的Kotlin》中,你能够学习到Kotlin的这点以及许多其它能力,你将通过从0开始创建Android APP学习Kotlin。
使用Kotlin对ViewGroup的视图进行函数使操作的更多相关文章
- 用Kotlin实现Android定制视图(KAD 06)
作者:Antonio Leiva 时间:Dec 27, 2016 原文链接:https://antonioleiva.com/custom-views-android-kotlin/ 在我们阅读有关c ...
- onAttachedToWindow () 和 onDetachedFromWindow () ; 以及更新视图的函数ondraw() 和dispatchdraw()的区别
protected void onAttachedToWindow() This is called when the view is attached to a window. At this po ...
- MS SQL Server中数据表、视图、函数/方法、存储过程是否存在判断及创建
前言 在操作数据库的时候经常会用到判断数据表.视图.函数/方法.存储过程是否存在,若存在,则需要删除后再重新创建.以下是MS SQL Server中的示例代码. 数据表(Table) 创建数据表的时候 ...
- .Net程序员学用Oracle系列(7):视图、函数、过程、包
<.Net程序员学用Oracle系列:导航目录> 本文大纲 1.视图 1.1.创建视图 2.函数 2.1.创建函数 2.2.调用函数 3.过程 3.1.创建过程 3.2.调用过程 4.包 ...
- .Net程序员学用Oracle系列(7):视图、函数、存储过程、包
1.视图 1.1.创建.删除及调用普通视图 1.2.高级视图介绍 2.函数 2.1.系统函数介绍 2.2.创建.删除及调用自定义函数 3.存储过程 3.1.创建.修改及删除存储过程 3.2.调用存储过 ...
- [转]动态管理视图和函数 (Transact-SQL)
动态管理视图和函数返回可用于监视服务器实例的运行状况.诊断故障以及优化性能的服务器状态信息. 重要提示 动态管理视图和函数返回特定于实现的内部状态数据. 在未来的 SQL Server 版本中,它们的 ...
- .Net程序员学用Oracle系列:视图、函数、存储过程、包
1.视图 在实际操作过程中,本人发现 Oracle 视图定义有一个缺陷,就是不大方便注释,每次写好的注释执行之后再打开视图定义所有注释就全都没了.后来我发现把注释写到末尾就不会被清除,但这样总感觉乖乖 ...
- flask 在视图函数里操作数据库
在视图函数里操作数据库 在视图函数里操作数据的方式和在python shell中的联系基本相同,只不过需要一些额外的工作.比如把查询结果作为参数 传入模板渲染出来,或是获取表单的字段值作为提交到数据库 ...
- navicat上如何导出视图,函数等
如何导出视图,函数,一般通过linux命令行,如果简单点就用navicat把. image.png 这样函数,视图都可以导出来后续更新.....
随机推荐
- VS2013中的MVC5模板部署到mono上的艰辛历程
部署环境:CentOS7 + Mono 3.10 + Jexus 5.6 在Xamarin.Studio创建的asp.net项目,部署过程非常顺利,没有遇到什么问题:但在VS2013中创建的asp.n ...
- ABP理论之时间
返回总目录 本篇目录 介绍 Clock 时区 绑定器和转换器 介绍 虽然有些应用针对的是一个特定的时区,但是也有一些应用针对多个不同的时区.为了满足这些需求,ABP为datetime操作提供了通用的基 ...
- U3D DrawCall优化手记
在最近,使用U3D开发的游戏核心部分功能即将完成,中间由于各种历史原因,导致项目存在比较大的问题,这些问题在最后,恐怕只能通过一次彻底的重构来解决 现在的游戏跑起来会有接近130-170个左右的Dra ...
- .NET - 基于事件的异步模型
注:这是大概四年前写的文章了.而且我离开.net领域也有四年多了.本来不想再发表,但是这实际上是Active Object模式在.net中的一种重要实现方法,因此我把它掏出来发布一下.如果该模型有新的 ...
- [转] STM32各种时钟的区别
[原创]:http://m.oschina.net/blog/129357 我在原创的基础又从另一位博主处引用了一些内容. 时钟系统是处理器的核心,所以在学习STM32所有外设之前,认真学习时钟系统是 ...
- No result defined for action com.lk.IndexAction and result success
意图访问一个 /es/index.action 竟然出现: [SAE ] ERROR [05-11 13:54:32] [http-80-5] com.opensymphony.xwork2.util ...
- redis成长之路——(六)
redis配置 为了码农在代码上只关心业务以及代码上的统一性,wenli.drive.redis内部使用配置来完成那些不同的场景,也就是说随便填填配置就能适应不同的场景! 当然配置多了码农也会受不了, ...
- Python的魔法函数之 - __len__,__getitem__,__setitem__,__delitem__
# 对象作为len()函数的参数是必须实现该方法 __len__ # 使用类似字典方式访问成员时必须实现 dic['pro_name'] __getitem__ # 使用类似字典方式设置成员时必须实现 ...
- linux tree 命令
使用cmder确实是方便了很多,想看命令帮助信息: $ help tree 以图形显示驱动器或路径的文件夹结构. TREE [drive:][path] [/F] [/A] /F 显示每个文件夹中文件 ...
- 【Win 10 应用开发】导入.pfx证书
这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...