Functional operations over Views in ViewGroup using Kotlin
Collections, iterators, arrays, sequences… all share a good set of useful functions that help do transformations, sortings and other kind of operations over their items. But there are parts in Android SDK where this is not available due to the way the classes are constructed.
For instance, we can’t directly get a list of the views inside a ViewGroup
Sequence
. In our case, the sequence will be a set of View
s in sequential order. We just need to implement its single function, which returns an Iterator
If we have a Sequence
, the world of functional operations is open for us to use them. So let’s start with that.
Note: Read till the end of the article
As lakedaemon666 suggests in comments, there’s an easier way to get the same result without using a sequence. I’ll leave the original text for the record, but I suggest you to take a look to the alternative solution.
Creating a Sequence from a ViewGroup
As mentioned before, we’ll create an iterator, which must know if there is a next item, and which one it is. We can create an extension function, which will be available for any ViewGroup
and descendant classes A simple way to do that:
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!! } } }
Retrieving a recursive list of views
It’d be very useful to have a list of views we can apply functions to. So we could first create a list of the Views in the first level, and then use it to retrieve the views inside the rest of ViewGroup
s inside the parent in a recursive way.
Let’s create a new extension property for ViewGroup
. An extension property is very similar to an extension function, and can be applied to any class:
public val ViewGroup.views: List<View> get() = asSequence().toList()
With this, we could create a recursive function that returns all the View
s inside any ViewGroup
in the layout:
Want to learn Kotlin?
Check my free guide to create your first project in 15 minutes!
public val ViewGroup.viewsRecursive: List<View> get() = views flatMap { when (it) { is ViewGroup -> it.viewsRecursive else -> listOf(it) } }
With flatMap
, we convert all the multiple lists from every result into one single list. It will iterate over any view, and if it’s a ViewGroup
, it will ask for its own views. Otherwise, it’ll return a list with a single item.
Usage examples
Now we can get the viewsRecursive
property to perform any operation we can think of. Here you can see a couple of examples. I created a simple layout that looks like this:
<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>
This is the code that, for instance, can be applied in MainActivity.onCreate()
. It will convert the Hello Kotlin!
string to uppercase, and the even checkboxes to checked:
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) } }
Alternative solution
As lakedaemon666 mentions in comments (thanks for the explanation), there’s no much sense in creating a sequence if we are iterating over the whole sequence right after that. Sequences are meant for lazy iteration, for instance, when reading the lines of a file. Only the necessary items will be requested. However, in this situation we are using all of them, so a plain list will be enough.
Besides, there’s a much easier way to create a list of views. We can rely on ranges to generate a list of the indexes of the views and map them to the list of views we need. Everything in just one line:
public val ViewGroup.views: List<View> get() = (0..getChildCount() - 1) map { getChildAt(it) }
Conclusion
This is a silly example, but with this idea you’ll probably be able to make all your code more functional, and stop depending on loops and other flow controls which are more typical from iterative programming.
And remember you can learn this and many other things about Kotlin in the book I’m writing: Kotlin for Android Developers, where you will learn Kotlin by creating an Android App from the ground up.
I’m in love with Kotlin. I’ve been learning about it for a couple of years, applying it to Android and digesting all this knowledge so that you can learn it with no effort.
SharesLike this:
Loading...Related
相關推薦
Functional operations over Views in ViewGroup using Kotlin
Collections, iterators, arrays, sequences… all share a good set of useful functions that help do transformations, sortings and other kind of operatio
Functional operations with collections in Kotlin (KAD 11)
I must admit that, for me, one of the most frustrating things when writing Java ccode is the list handling. Java 8 has some improvements in this resp
Custom Views in Android with Kotlin (KAD 06)
When we saw the article about classes, you may remember that in general only one constructor is used. This is a problem for creating custom views. Th
[Free Guide] Create your first Android App using Kotlin in 15 minutes
The personal data that you provide through this form will be recorded in a file of Antonio Leiva Gordillo, in order to meet your request. The legitimation
API request in Android the easy way using Kotlin
Kotlin is a really powerful language aimed to write more code using less boilerplate. And this is specially true in Android. Apart from the language
Cannot create __weak reference in file using manual reference counting
解決 msu prop nsobject -s xcod pos create mod Xcode更新到7.3後會出現NSObject+MJProperty.h報Cannot create __weak reference in file using m
[Python] Create a minimal website in Python using the Flask Microframework
() turn ini pass work def col out code How to install Flask Use Flask to create a minimal website Build routes in Flask to respond to web
ORA-38301:can not perform DDL/DML Over Object in Recycle Bin 11.2.0.4
測試 style 操作 ask erro ora- cli pre ati 我們最近有兩臺測試服務器在oci direct load期間出現下列異常: 從表象上看,是我們在對表執行ddl操作,確實內部也是用了truncate table XXX,可是這個XXX並不是回收站
[WASM + Rust] Debug a WebAssembly Module Written in Rust using console.log
Having some kind of debugging tool in our belt is extremely useful before writing a lot of code. In this lesson we build a println!()-style syntax u
Context propagation over HTTP in Go
https://medium.com/@rakyll/context-propagation-over-http-in-go-d4540996e9b0 Context propagation over HTTP in Go Go 1.7 introduced a built-in
delete symlink in subversion using svn delete command
# svn delete etc/systemd/system/getty.target.wants/[email protected]@ D etc/systemd/system/getty.targ
Pandas文摘:Applying Operations Over pandas Dataframes
原文地址:https://chrisalbon.com/python/data_wrangling/pandas_apply_operations_to_dataframes/ Applying Operations Over pandas Dataframes 20 Dec 2017
Building an Excellent Mock Server in Swift using Vapor
What is a Mock Server?Before explaining what a mock server is, we’ll start with what a mock is and why you might use one. I’ll also avoid delving into the
Analyzing verbatim comments in spreadsheets using machine learning
Machine learning has made it more accessible to create meaningful insights in a data-rich world. This includes data from customer surveys, qualitative prim
sex parents born in China using stem cells and gene editing
"We were interested in the question of why mammals can only undergo sexual reproduction. We have made several findings in the past by combining reproducti
Authorization In GraphQL Using Custom Schema Directives
Let’s start to explore some options for adding authentication and authorization to our GraphQL API.Auth Check In ResolversAn initial approach might be to i
Creating a Personal Chatbot in Python3 using ChatterBot(Part 1)
Before we get started, we need to get all of the necessary pip installations. Open your terminal and run the following commands:Pip installations:pip3 inst
Calling Kotlin from Java: start using Kotlin today
One of the great wonders of Kotlin is that it’s fully integrated with Java. This means that although all your application code is written Java, you c
Architecture Components: Hack the API by using Kotlin extensions
I’ve been using Architecture Components for a while, and I must admit I love them. The Android team has managed to find a way to let us forget about
Building a Simple Chatbot from Scratch in Python (using NLTK)
So what is a chatbot?A chatbot is an artificial intelligence-powered piece of software in a device (Siri, Alexa, Google Assistant etc), application, websit