Kotlin Android Extensions: 與 findViewById 說再見 (KAD 04) -- 更新版

来源:http://www.cnblogs.com/figozhg/archive/2017/08/20/7400435.html
-Advertisement-
Play Games

在 Kotlin1.1.4版本 發佈後,原作者依據 Kotlin 新版本的一系列新特性和讀者的提問對幾個月前寫文章進行更新。這篇是重寫的KAD04。在這篇重寫的文章中,他涵蓋了所有KAE(1.1.4版本前後)可以完成的事情。現在你會喜歡在任何類(不只是activity, fragment 或 vie... ...


作者:Antonio Leiva

時間:Aug 16, 2017

原文鏈接:https://antonioleiva.com/kotlin-android-extensions/

 

Kotlin1.1.4版本 發佈後,原作者依據 Kotlin 新版本的一系列新特性,以及有讀者關於如何在 Fragment 和 custom view 中使用Kotlin 等等向他提問,原作者決定針對這些內容進行更新、重寫幾個月的文章。

 

在這篇重寫的文章中,他涵蓋了所有KAE(1.1.4版本前後)可以完成的事情。現在你會喜歡在任何類(不只是activity, fragment 或 view)使用它們,包括一個新的註釋來實現Parcelable。

 

 

你可能會厭倦日復一日地使用findViewById來恢復Androidview。或許你已放棄了,並開始使用著名的Butterknife庫。那麼你會喜歡上Kotlin Android Extensions

 

Kotlin Android Extensions:這是什麼?

 

 

Kotlin Android ExtensionsKotlin的一個插件,它包含在普通的那個插件中,這就允許以驚人的無縫方式從ActivitieFragmentView中恢復View

 

該插件將生成一些額外的代碼,允許你訪問佈局XMLView,就像它們是在佈局中定義的屬性一樣,你可以使用 id 的名稱。

 

 

它還構建本地視圖緩存。所以首次使用一個屬性時,它會做一個普通的findViewById。而接下來,View則是從緩存中恢復,因此訪問速度更快。

 

 

怎樣使用它們

 

讓我們看看它的使用是多麼容易。我會以一個Activity做第一個例子:

 

Kotlin Android Extensions集成到我們的代碼中

 

雖然這個插件被集成到普通的插件(你不需要安裝新的插件)中,但是,你要使用它,還必須在Android模塊中添加額外的應用:

1 apply plugin: 'com.android.application'
2 apply plugin: 'kotlin-android'
3 apply plugin: 'kotlin-android-extensions'

 

只需要做這些,可以開始使用它了。

 

XML中恢復 View

 

從這一刻起,恢復View就像將你在XML中定義的View ID直接用於你的Activity一樣簡單。

 

假設你有一個這樣的XML

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <FrameLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent">
 6  
 7     <TextView
 8         android:id="@+id/welcomeMessage"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_gravity="center"
12         android:text="Hello World!"/>
13  
14 </FrameLayout>

 

如你所見,TextViewwelcomeMessage ID。

 

在你的 MainActivity 中,僅僅需要這樣編寫:

1 override fun onCreate(savedInstanceState: Bundle?) {
2     super.onCreate(savedInstanceState)
3     setContentView(R.layout.activity_main)
4  
5     welcomeMessage.text = "Hello Kotlin!"
6 }

 

為了能夠使用它,你需要如下的import,而IDE能夠自動加入它。不是很容易嗎?

1 import kotlinx.android.synthetic.main.activity_main.*

 

如上所述,生成的代碼將包括View緩存,因此再次詢問視圖,就不需要另一個findViewById

 

讓我們看看其背後都是什麼。

 

Kotlin Android Extensions背後的魔力

 

 

在你開始使用Kotlin時,理解所使用特性時生成的位元組碼是非常有趣。這也有助於你瞭解在你的決定背後隱藏的成本

 

Tools-->Kotlin下,有一個強大的功能,稱為顯示Kotlin位元組碼(Show Kotlin Bytecode。如果你點擊它,你將看到當你打開的類文件編譯後生成的位元組碼。

 

 

對大多數人來說,位元組碼並不是很有用,但是還有另一個選擇:Decompile(反編譯)

 

 

這將顯示Kotlin生成的位元組碼的Java表示所以你可以或多或少地瞭解你寫的Kotlin代碼對應Java的代碼。

 

 

在我生成的Activity中使用它,並查看由Kotlin Android Extensions生成的代碼。

 

有趣的是這一個:

 

 1 private HashMap _$_findViewCache;
 2 ...
 3 public View _$_findCachedViewById(int var1) {
 4    if(this._$_findViewCache == null) {
 5       this._$_findViewCache = new HashMap();
 6    }
 7  
 8    View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
 9    if(var2 == null) {
10       var2 = this.findViewById(var1);
11       this._$_findViewCache.put(Integer.valueOf(var1), var2);
12    }
13  
14    return var2;
15 }
16  
17 public void _$_clearFindViewByIdCache() {
18    if(this._$_findViewCache != null) {
19       this._$_findViewCache.clear();
20    }
21  
22 }

 

 

這就是我們正在談論的視圖緩存(View Cache

 

 

在要求查看時,首先會嘗試在緩存中找。如果它不存在,它會查找它,並將其添加到緩存。非常簡單。

 

 

此外,它還添加了一個清除緩存的功能:clearFindViewByIdCache。如果你必須重建視圖,因為舊視圖將不再有效,就可以使用它。

 

 

那麼這一行:

 

1 welcomeMessage.text = "Hello Kotlin!"

 

 

則轉換為:

 

1 ((TextView)this._$_findCachedViewById(id.welcomeMessage)).setText((CharSequence)"Hello Kotlin!");

 

 

因此,屬性不是真實的,插件不會為每個視圖生成屬性。在編譯期間,只需要替換代碼即可訪問視圖緩存,將其轉換為正確的類型並調用該方法。

 

Fragment的 Kotlin Android Extensions

 

 

這個插件也能夠用於Fragment

 

Fragment的問題是View可以重新創建,而Fragment實例卻保持有效。這會怎麼樣?這就意味著緩存中的視圖將不再有效。

 

 

 

如果我們把View移動到Fragment中,我們來看看生成的代碼。這是我創建這個簡單的Fragment,它使用與上面寫的相同的XML

 1 class Fragment : Fragment() {
 2  
 3     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 4         return inflater.inflate(R.layout.fragment, container, false)
 5     }
 6  
 7     override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
 8         super.onViewCreated(view, savedInstanceState)
 9         welcomeMessage.text = "Hello Kotlin!"
10     }
11 }

 

onViewCreated中,我再次更改TextView的文本。生成怎樣的位元組碼呢?

 

 

一切都與Activity中的一樣,僅有一個微小的區別:

1 // $FF: synthetic method
2 public void onDestroyView() {
3    super.onDestroyView();
4    this._$_clearFindViewByIdCache();
5 }

 

View被銷毀時,此方法將調用clearFindViewByIdCache,所以我們是安全的!

 

自定義View的Kotlin Android extensions

 

在自定義視圖下,Kotlin Android extensions非常類似。假設我們有這樣的View

 1 <merge xmlns:android="http://schemas.android.com/apk/res/android"
 2               android:orientation="vertical"
 3               android:layout_width="match_parent"
 4               android:layout_height="match_parent">
 5     
 6     <ImageView
 7         android:id="@+id/itemImage"
 8         android:layout_width="match_parent"
 9         android:layout_height="200dp"/>
10     
11     <TextView
12         android:id="@+id/itemTitle"
13         android:layout_width="match_parent"
14         android:layout_height="wrap_content"/>
15  
16 </merge>

 

我創建一個非常簡單的自定義視圖,並使用@JvmOverloads註釋的新Intent生成構造函數:

 

 

1 class CustomView @JvmOverloads constructor(
2         context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
3 ) : LinearLayout(context, attrs, defStyleAttr) {
4  
5     init {
6         LayoutInflater.from(context).inflate(R.layout.view_custom, this, true)
7         itemTitle.text = "Hello Kotlin!"
8     }
9 }

 

在上面的示例中,我將文本更改為itemTitle。生成的代碼應該嘗試從緩存中查找View。再次複製所有相同的代碼已無意義了,但是你可以在更改文本的行中看到這一點:

1  ((TextView)this._$_findCachedViewById(id.itemTitle)).setText((CharSequence)"Hello Kotlin!");

 

太棒了!在自定義View中,我們只是首次調用findViewById

 

從另一個View中恢復View

 

Kotlin Android Extensions提供的最後一個選擇是直接從另一個視圖使用屬性

 

 

我用與上節非常相似的佈局。假設一下這是在適配器(Adapter)中對實例進行inflate

 

使用此插件,你還可以直接訪問子視圖(subview):

1 val itemView = ...
2 itemView.itemImage.setImageResource(R.mipmap.ic_launcher)
3 itemView.itemTitle.text = "My Text"

 

雖然這個插件會幫你填寫import,但是在這方面還是有點不同:

 

1 import kotlinx.android.synthetic.main.view_item.view.*

 

 

這裡有幾件事情,你需要瞭解:

  • 在編譯時,你可以從任何其他視圖(View)引用任何視圖。 這意味著你可以引用一個視圖,該視圖不是其的直接子節點。 但是,當試圖嘗試恢復不存在的視圖時,執行會失敗。
  • 在這種情況下,視圖不像ActivityFragment那樣被緩存。

 

 

為什麼會這樣?與之前的情況相反,這裡的插件沒有地方可以為緩存生成所需的代碼。

 

 

 

 

 

 

如果你再次查看代碼,它是當從視圖調用屬性時由插件生成的,你會看到:

 

1 ((TextView)itemView.findViewById(id.itemTitle)).setText((CharSequence)"My Text");

 

 

如你所見,沒有調用緩存。如果你的視圖很複雜,而且你是在適配器中使用,請註意,它可能會影響性能。

 

 

或者你可以選擇:Kotlin 1.1.4

 

版本1.1.4中的Kotlin Android Extensions

 

 

 

 

 

Kotlin新版本中,Android Extensions已經引入了一些新的有趣的功能:任何類中的緩存(有趣的包括ViewHolder)和一個新的@Parcelize註釋。還有一種方法可以自定義生成的緩存。

 

稍後,我們會看到它們,但你需要知道這些功能並不是最終的版本所以你需要將它們添加到build.gradle中來啟動它們

 

1 androidExtensions {
2     experimental = true
3 }

 

 

ViewHolder(或任何自定義類)中使用它

現在,你可以通過簡單的方式在任何類中構建緩存。唯一要求是你的類要實現LayoutContainer介面。該介面將提供一個視圖,它是由插件查找的子視圖。假設,我們有一個ViewHolder,它保持前面示例中講述的佈局視圖。你只需要做:

 

1 class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), 
2         LayoutContainer {
3  
4     fun bind(title: String) {
5         itemTitle.text = "Hello Kotlin!"
6     }
7 }

 

 

containerView是我們從LayoutContainer介面覆蓋的。但這就是你需要的。

 

 

之後,你就可以直接訪問視圖,無需使用前置itemView來訪問子視圖。

 

 

另外,如果你檢查生成代碼,你會看到它從緩存中獲取視圖:

 

1 ((TextView)this._$_findCachedViewById(id.itemTitle)).setText((CharSequence)"Hello Kotlin!");

 

 

這裡,我已經在ViewHolder上使用了它,但是你可以看到這是通用的,完全可以在任何類中使用。

 

Kotlin Android Extension實現Parcelable

 

用新的@Parcelize註釋,你可以以非常簡單的方式使任何類都能實現Parcelable

 

 

你只需要添加註釋,插件就會完成所有複雜的工作:

 

1 @Parcelize
2 class Model(val title: String, val amount: Int) : Parcelable

 

 

然後,如你所知,你可以將對象添加到任何intent中:

1 val intent = Intent(this, DetailActivity::class.java)
2 intent.putExtra(DetailActivity.EXTRA, model)
3 startActivity(intent)

 

並且在任何位置(在這種情況下是在在目標Activity)上,從intent恢復對象:

1 val model: Model = intent.getParcelableExtra(EXTRA)

 

自定義構建緩存

 

在這組實驗中包含的新功能是一個新註釋@ContainerOptions。這允許你以自定義方式構建緩存,甚至阻止類創建它

 

 

預設情況下,它將用Hashmap,如我們之前看到的那樣。但是,這可以用Android框架的SparseArray來改變,這在某些情況下可能會更有效率。如果由於某種原因,你不需要一個類的緩存,你也可以使用該選項。

 

這是它的用法:

 

1 @ContainerOptions(CacheImplementation.SPARSE_ARRAY)
2 class MainActivity : AppCompatActivity() {
3 ...
4 }

 

 

目前,已有的選擇是:

 

1 public enum class CacheImplementation {
2     SPARSE_ARRAY,
3     HASH_MAP,
4     NO_CACHE;
5  
6     ...
7 }

 

 

結論

 

 

 

 

 

 

 

 

 

 

你已經看到Kotlin處理Android視圖的如此簡易。通過一個簡單的插件,我們可以拋棄那些在inflation後視圖恢復的所有可怕的代碼。此插件將為我們創建所需的屬性,而不會出現任何問題。

 

此外,Kotlin 1.1.4增加了一些有趣的特性,這些特性在以前的插件沒有覆蓋的情況下,是非常有用。

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ViewPager實現引導頁 廢話就不多說了,直接上效果圖和代碼 fry.Activity01 fry.ImagePagerAdapter /viewPager/res/layout/activity01.xml 1、多個控制項(這裡指小圓點)可以用List來一起訪問 2、可用動態的控制項設置tag來區 ...
  • (c)hele 一、基本情況 (一)開發工具改版 在我的上篇博客里,工程構建還是Eclipse模式,但是這篇博客寫出來時工程已經改為Android Studio模式,主要是Gradle工具的應用,特別是第三方庫的引用,不用將庫放進libs文件夾下,只需要進行Gradle依賴配置,十分方便。 (二)內 ...
  • Windows下的Android遠程桌面助手(Android Remote Displayer and Controller),增加了輸入法的快速切換功能,支持通過Google拼音輸入法在PC端快速輸入中文,支持通過ARDC輸入法複製粘貼文本到Android端。 ...
  • 歡迎各位同學加入: React-Native群:397885169 大前端群:544587175 大神超多,熱情無私幫助解決各種問題。 最近項目需求需要用到輪播圖,所以寫兩Demo練練手,不過效果不太理想,希望大牛予以指正。 不多說,先上圖。 這種輪播很常見,但是一個問題是,總感覺有點卡的感覺,最氣 ...
  • 說明: ^.*[\u4e00-\u9fa5].*$ 是否包含中文^[\u4E00-\u9FA5]+$ 是否全中文 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range repla ...
  • 轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/76165252 本文出自 "【趙彥軍的博客】" 一:什麼是路由? 說簡單點就是映射頁面跳轉關係的,當然它也包含跳轉相關的一切功能。 二:為什麼需要路由 Android系統已經給我們提供 ...
  • 轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/75578109 本文出自 "【趙彥軍的博客】" 起因 最近在項目中遇到需要在界面上顯示一個本地的 GIF 圖。按照慣例我直接用了 Glide 框架來實現。 Glide 地址: http ...
  • 在瞭解`Masonry`框架之前,有必要先瞭解一下**自動佈局**的概念。在`iOS6`之前,`UI`佈局的方式是通過`frame`屬性和`Autoresizing`來完成的,而在`iOS6`之後,蘋果公司推出了`AutoLayout`的佈局方式,它是一種基於約束性的、描述性的佈局系統,尤其是蘋果的... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...