編輯:關於android開發
原文標題: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的更簡單的方法可以獲得同樣的結果。我會保留原文記錄,但是建議你看看替代的解決方案。
如前所述,我們將創建迭代器(iterator),它必須知道是否有下一項,下一項是哪個。我們還創建一個擴展函數,為了任何 ViewGroup 和繼承類提供一個簡單的方法做那項工作:
1 fun ViewGroup.asSequence(): Sequence<View> = object : Sequence<View> { 2 3 override fun iterator(): Iterator<View> = object : Iterator<View> { 4 private var nextValue: View? = null 5 private var done = false 6 private var position: Int = 0 7 8 override public fun hasNext(): Boolean { 9 if (nextValue == null && !done) { 10 nextValue = getChildAt(position) 11 position++ 12 if (nextValue == null) done = true 13 } 14 return nextValue != null 15 } 16 17 override fun next(): View { 18 if (!hasNext()) { 19 throw NoSuchElementException() 20 } 21 val answer = nextValue 22 nextValue = null 23 return answer!! 24 } 25 } 26 }
獲得一個視圖列表,並對其進行函數操作是非常有用的。因此,我們可以先創建頂層視圖列表,然後,用它以遞歸方式逐級檢索ViewGroup
中視圖。
讓我們為ViewGroup
創建新擴展屬性。擴展屬性非常類似擴展函數,可用於任何類:
1 public val ViewGroup.views: List<View> 2 get() = asSequence().toList()
我們可以用這,創建遞歸函數,它返回布局中任何ViewGroup
內部的所有View
:
1 public val ViewGroup.viewsRecursive: List<View> 2 get() = views flatMap { 3 when (it) { 4 is ViewGroup -> it.viewsRecursive 5 else -> listOf(it) 6 } 7 }
用flatMap
,我們把全部結果的多個列表轉換到一個列表中。它將遍歷任何視圖;如果是ViewGroup,它還會遍歷自己的視圖。否則,就返回僅有一項的列表。
現在,我們得到viewRecursive
屬性,執行我們想要的任何操作。這裡你可以看到兩個例子。我創建這樣一個簡單的布局:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:id="@+id/container" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:paddingBottom="@dimen/activity_vertical_margin" 7 android:paddingLeft="@dimen/activity_horizontal_margin" 8 android:paddingRight="@dimen/activity_horizontal_margin" 9 android:paddingTop="@dimen/activity_vertical_margin" 10 tools:context=".MainActivity"> 11 12 <TextView 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:text="@string/hello_world"/> 16 17 <FrameLayout 18 android:layout_width="match_parent" 19 android:layout_height="wrap_content" 20 android:layout_centerInParent="true"> 21 22 <TextView 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:text="Hello Java"/> 26 27 <TextView 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_gravity="center_horizontal" 31 android:text="Hello Kotlin"/> 32 33 <TextView 34 android:layout_width="wrap_content" 35 android:layout_height="wrap_content" 36 android:layout_gravity="end" 37 android:text="Hello Scala"/> 38 39 </FrameLayout> 40 41 <LinearLayout 42 android:layout_width="match_parent" 43 android:layout_height="wrap_content" 44 android:layout_alignParentBottom="true" 45 android:orientation="horizontal"> 46 47 <CheckBox 48 android:layout_width="wrap_content" 49 android:layout_height="wrap_content" 50 android:text="Check 1"/> 51 52 <CheckBox 53 android:layout_width="wrap_content" 54 android:layout_height="wrap_content" 55 android:text="Check 2"/> 56 57 <CheckBox 58 android:layout_width="wrap_content" 59 android:layout_height="wrap_content" 60 android:text="Check 3"/> 61 62 <CheckBox 63 android:layout_width="wrap_content" 64 android:layout_height="wrap_content" 65 android:text="Check 4"/> 66 67 </LinearLayout> 68 69 </RelativeLayout>
例如,在MainActivity.onCreate()
中,
可應用這段代碼。它將Hello Kotlin!
串轉換為大寫字母,選中偶數復選框:
1 val container: ViewGroup = find(R.id.container) 2 val views = container.viewsRecursive 3 4 // Set Kotlin TextView to Upper 5 val kotlinText = views.first { 6 it is TextView && it.text.toString().contains("Kotlin") 7 } as TextView 8 kotlinText.text = kotlinText.text.toString().toUpperCase() 9 10 // Set even checkboxes as checked, and odd as unchecked 11 views filter { 12 it is CheckBox 13 } forEach { 14 with(it as CheckBox) { 15 val number = text.toString().removePrefix("Check ").toInt() 16 setChecked(number % 2 == 0) 17 } 18 }
如lakedaemon666在評論中所提及的那樣(謝謝解釋),如果之後我們遍歷整個sequence,那麼創建sequence就沒有什麼意義了。例如,當按行讀取文件時,sequence是懶惰迭代。只有要求必要的項目。而我們卻要使用所有項,所以簡單的列表就足夠了。
另外,有許多簡單的方法可以產生視圖列表。我們可以依賴值域產生視圖的索引列表,將它們映射到我們需要的視圖列表中。所有這一切就一行:
1 public val ViewGroup.views: List<View> 2 get() = (0..getChildCount() - 1) map { getChildAt(it) }
這是個無聊的例子,但是這個概念或許可使你的所有代碼函數化,停止依靠那些典型迭代式編程的循環和其它控制流。
記住從我寫的書《Android開發者的Kotlin》中,你能夠學習到Kotlin的這點以及許多其它能力,你將通過從0開始創建Android APP學習Kotlin。
仿Android印象筆記底部導航欄 最近用上了印象筆記,覺得android 版的底部導航欄挺不錯的,好多應用裡面都有用到,想著自己動手實現一下,不多說,先上圖:
Drawable Animation 幀動畫,drawableanimationMySurfaceView類: package com.fm; import andr
Android中使用RecyclerView和CardView實現瀑布流效果(StaggeredGrid) 在Android 5.0 中引入了Material Des
RxJava 和 RxAndroid 四(RxBinding的使用),rxjavarxandroid 對Rxjava不熟悉的同學可以先看我之前寫的幾篇文章 RxJava