一覺醒來Kotlin成了Android的新寵兒【附代碼】
前言
凌晨的谷歌I/O 2017開發者大會直播你有沒有觀看呢?安卓雖然已經成為了移動設備***操作系統,全球采用Android操作系統的激活設備超過了20億臺。不過對于谷歌來說,需要依靠java來做安卓開發一直是一個心病,因為oracle公司因為java和谷歌在安卓系統上的訴訟搞得心力憔悴。
現在好了,谷歌官方正式支持Kotlin,成為官方認可的安卓開發***語言,而且從Android Studio 3.0開始,將直接集成Kotlin而無需安裝任何的插件。
正文
開始做安卓UI開發一直是使用XML文件來實現。雖然理論上,UI可以使用Java語言來實現,但并沒有太多的用處。不久前,JetBrains推出了Kotlin,一種面向JVM的現代語言,可以很好的實現安卓UI。
Jetbrains宣稱Anko是Android中更快,更輕松的開發風格。Kotlin提供Anko庫來作為DSL(領域專用語言)去設計安卓界面,一個簡單的例子:
下面的界面由一個圖片和一個按鈕組成:
使用Anko實現如下:
- verticalLayout{
- imageView(R.drawable.anko_logo).
- lparams(width= matchParent) {
- padding = dip(20)
- margin = dip(15)
- }
- button("Tap to Like") {
- onClick { toast("Thanks for the love!") }
- }
- }
我們定義了一個垂直的線性布局作為容器包含圖片和按鈕,使用lparams定義了布局的位置信息,由Kotlin的內聯函數也實現了按鈕的點擊事件。
使用Anko的優點:
- 我們可以將UI布局嵌入到代碼中,從而使其類型安全。
- 由于我們不用XML編寫,所以它增加了效率,因為在分析XML浪費CPU時間。
- 在UI的程序化轉換之后,我們可以將Anko DSL片段放入一個函數中。這樣便于代碼重用。
- 顯然,代碼更簡潔,可讀和可掌握性更高。
現在我們使用Anko Layout和Kotlin構建一個to-do app,來列出我們今天需要做的事。
你可以在GitHub上找到這個項目 to-do app
將Anko庫添加到Android Studio:
在streamline-android-java-code-with-kotlin去學習如何添加Kotlin到你的安卓項目中,有了Kotlin,我們需要添加Anko依賴在app/build.gradle中,這樣我們就可以順利編譯項目了。
- compile [size=1em]'org.jetbrains.anko:anko-sdk15:0.8.3'
- // sdk19,21,23 也可以使用
可以根據你項目的minSdkVersion來添加這個依賴,上面的例子說明15<=minSdkVersion<19,你可以在Anko的GitHub庫中找到自己需要的其他Anko依賴庫。
我們準備使用下面的依賴庫:
- compile 'org.jetbrains.anko:anko-design:0.8.3'
- compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.3'
在Activity中調用Anko布局:
我們不再使用XML來寫布局文件,所以我們不需要XML View,所以也不需要findViewById()方法了。這里我們假設我們的Anko布局類為MainUI,然后我們可以開始寫我們的activit內容:
- var ui =MainUI() //MainUI類代替了XML布局
- ui.setContentView(this) //this代表Activity類
現在我們創建一個Kotlin文件MainActivity.kt,寫上如下代碼:
- class MainActivity : AppCompatActivity() { val task_list = ArrayList<String>() //任務清單表
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- savedInstanceState?.let { val arrayList = savedInstanceState.get("ToDoList")
- task_list.addAll(arrayList as List<String>)
- } var adapter=TodoAdapter(task_list) //定義適配器
- var ui = MainUI(adapter) //定義將要使用的Anko UI 布局
- ui.setContentView(this) //給Activity設置Anko布局
- } override fun onSaveInstanceState(outState: Bundle?) {
- outState?.putStringArrayList("ToDoList", task_list)
- super.onSaveInstanceState(outState)
- }
- }
task_list是ArrayList,將填充ListView的TodoAdapter。MainUI(adapter)是我們的Anko UI文件,它采用TodoAdapter類作為適配器參數。所以,接下來我們再創建一個TodoAdapter類。
用于ListView的TodoAdapter適配器
TodoAdapter類有一個ArrayList<String>類型的list,并且繼承了BaseAdapter。所以我們需要重寫一下四個方法:
public int getCount()public Object getItem(int i)public long getItemId(int i)public View getView(int i, View view, ViewGroup viewGroup)
在getView()方法中我們需要使用Anko設計一個表元素的布局。
- public int getCount()public Object getItem(int i)public long getItemId(int i)public View getView(int i, View view, ViewGroup viewGroup)
- 在getView()方法中我們需要使用Anko設計一個表元素的布局。
- override fun getView(i : Int, v : View?, parent : ViewGroup?) : View {
- return with(parent!!.context) { //任務數從1開始
- var taskNum: Int = i +1
- //清單表元素布局
- linearLayout {
- lparams(width = matchParent, height = wrapContent)
- padding = dip(10)
- orientation = HORIZONTAL //任務號
- textView {
- id = R.id.taskNum
- text=""+taskNum
- textSize = 16f
- typeface = Typeface.MONOSPACE
- padding =dip(5)
- } //任務名
- textView {
- id = R.id.taskName
- text=list.get(i)
- textSize = 16f
- typeface = DEFAULT_BOLD
- padding =dip(5)
- }
- }
- }
- }
- 在這個方法中,我們返回一個包含一個horizontalListView布局列表項的視圖。這是使用Kotlin的with語法完成的,它允許我們一次在對象實例上調用很多方法。
- 每個列表項包含兩個textview用于顯示任務號和任務名稱。
- linearLayout,textView是擴展功能。擴展功能使我們有能力啟用具有新功能的任何類。
- text,textSize,typeface在android.widget.TextView有getter和setter方法,padding是Anko添加的屬性。
繼續下一步,我們需要定義列表的操作功能。因此,我們需要在TodoAdapter中定義add(String)和delete(Int)方法。add(String)將任務名稱作為參數添加到任務中。delete(Int)將任務所在的位置作為參數來刪除任務。下面是具體的實現:
- //將任務添加到任務清單的方法
- fun add(text: String) {
- list.add(list.size, text)
- notifyDataSetChanged() //更新數據 } //將任務從任務清單中移除的方法
- fun delete(i:Int) {
- list.removeAt(i)
- notifyDataSetChanged() //更新數據
- }
所以,現在我們設計了列表,我們也可以添加和刪除項目到我們的列表中。接下來完成此適配器類的代碼。
- TodoAdapter(val list: ArrayList<String> = ArrayList<String>()) : BaseAdapter() {
- override fun getView(i : Int, v : View?, parent : ViewGroup?) : View {
- return with(parent!!.context) {
- //taskNum will serve as the S.No. of the list starting from 1
- var taskNum: Int = i +1
- //Layout for a list view item
- linearLayout {
- id = R.id.listItemContainer
- lparams(width = matchParent, height = wrapContent)
- padding = dip(10)
- orientation = HORIZONTAL
- textView {
- id = R.id.taskNum
- text=""+taskNum
- textSize = 16f
- typeface = Typeface.MONOSPACE
- padding =dip(5)
- }
- textView {
- id = R.id.taskName
- text=list.get(i)
- textSize = 16f
- typeface = DEFAULT_BOLD
- padding =dip(5)
- }
- }
- }
- }
- override fun getItem(position : Int) : String {
- return list[position
- }
- override fun getCount() : Int {
- return list.size
- }
- override fun getItemId(position : Int) : Long {
- //can be used to return the item's ID column of table
- eturn 0L
- }
- //function to add an item to the list
- fun add(text: String) {
- list.add(list.size, text)
- notifyDataSetChanged()
- }
- //function to delete an item from list
- fun delete(i:Int) {
- list.removeAt(i)
- notifyDataSetChanged()
- }
- }
注意,使用Anko DSL類中必須要導入org.jetbrains.anko.*。
設計項目的外觀
Anko為我們提供了在單獨的Kotlin類中為Activity使用UI的便利。因此,每個屏幕都可以被認為是Kotlin類的UI-Activity匹配對。這個UI類是通過繼承在org.jetbrains.anko包中定義的AnkoComponent<T>接口的功能來實現的。
除了這個接口,JetBrains還提供免費的DSL布局預覽功能。下面是Anko DSL布局預覽在Android Studio中的樣子:
Anko Preview的相應插件可以從這里下載。請注意,在撰寫本文時,Android Studio 2.2的Anko DSL 布局預覽被列為開源issue。
回到正題,我們接下來設計MainUI類展示所有任務列表。MainUI類繼承了AnkoComponent<T>接口,其中T指的是UI的所有者,activity的內容將會是這個UI。在我們的例子中,所有者就是我們已經在上面定義的MainActivity。接下來,在初始化時,我們必須將TodAadapter對象傳遞給此類,因為此適配器將用于填充列表。所以,MainUI聲明變成:
- class MainUI(val todoAdapter : TodoAdapter) : AnkoComponent<MainActivity>
現在我們需要重寫方法 createView() ,使用 AnkoContext 對象作為參數并返回一個View 類型:
- override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) {
- }
我們在createView() 方法中UI定義返回給所有者即activity,在這里也就是MainActivity,所以接下來寫createView() 方法:
Step1-設計首頁
最初,首頁是空列表。所以,我們有一個textView要求用戶創建一天的Todo List:
- return relativeLayout {
- //聲明ListView
- var todoList : ListView? =null
- //當沒有任務時顯示textView內容"What's your Todo List for today?"
- val hintListView = textView("What's your Todo List for today?") {
- textSize = 20f
- }.lparams {
- centerInParent()
- }
- }
centerInParent() 是將視圖的布局定義為垂直和水平相對中心的輔助方法。因為它是一個todo性質的應用,其本質在于顯示任務的列表。所以,我們在這里定義listView:
- //listView
- verticalLayout {
- todoList=listView {
- //assign adapter
- adapter = todoAdapter
- }
- }.lparams {
- margin = dip(5)
- }
todoAdapter是我們在MainUI類聲明中定義成員變量。我們用todoAdapter的值初始化listView的adapter,這是一個TodoAdpater類的對象,將會用于填充列表。
為了幫助用戶添加任務,我們在主屏幕的右下方提供了一個Material design風格的floatingActionButton。所以我們使用Anko編程floatingActionButton為:
- floatingActionButton {
- imageResource = android.R.drawable.ic_input_add
- }.lparams {
- //設置按鈕在屏幕的右下方
- margin = dip(10)
- alignParentBottom()
- alignParentEnd()
- alignParentRight()
- gravity = Gravity.BOTTOM or Gravity.END
- }
篇幅所限,Step2、Step3請點擊左下角“閱讀原文”查看全部文章。
***的想法
我們在開發此to-do app時沒有使用任何XML布局資源,但我們能夠以類似的風格設計應用程序。Anko從應用程序的activity或fragments中消除了呈現數據的負擔,響應用戶交互,連接數據庫等這些負擔。另外,隔離UI和Activity類使得應用程序更接近MVP(Model-View-Presenter)架構。你可以從這里了解Anko的高級功能。
雖然它有一些缺點,如較慢的編譯和應用程序體積大,因為它在復用,維護和測試代碼方面打包了很多東西。
***次翻譯這么長的文章,純手打,如果有什么錯誤的地方還請指出包涵!也是因為自己對Kotlin的興趣所以找了這篇文章來翻譯,有興趣的朋友可以看看學習一下。