初次嘗試用Kotlin實現Android項目


 

   起這個文內標題的原因很簡單,就是對Kotlin抱有希望——能使Android的開發更簡潔、高效及安全。知道Kotlin是從簡書的一篇短文,越來越覺得將自己學習、實踐的過程和想法總結成文字分享出來,不管文筆好壞,內容多少,若能拋磚引玉就足以。所以,感謝寫了那么多精彩文章的大神,而我才剛從山腳啟程。

  項目源碼放在Github上,感興趣的朋友可以下載,歡迎送星和討論。Demo運行的動態效果圖如下:

 

1. Kotlin在Android Studio中的環境配置

  按照下面兩篇文章的介紹操作,就能完成Kotlin在Android Studio中的環境配置(Eclipse就不推薦了),並能學習到基礎語法和使用案例。如果有問題可以百度、谷歌或參考分享的項目源碼中的Project及App的build.gradle設置,也可以留言大家一起討論。

  http://kotlinlang.org/docs/tutorials/kotlin-android.html

  http://kotlinlang.org/docs/tutorials/android-plugin.html

  其實和引入普通插件類似,說簡單點就是做兩件事情:

  a 安裝Kotlin插件;

  b Porject和App的build.gradle文件中添加引用;

  如果配置沒問題了,在Studio工具欄的Code欄最下方會出現可將Java代碼轉為Kotlin代碼的選擇項:

  如果本身就是kt格式,那該選項就是灰色不可點擊的。

 

2. Kotlin學習與編碼總結

   開發環境OK之后,作為剛接觸Kotlin的初學者,有兩種選擇:

  a 直接新建Android項目與Kotlin文件,在學習的同時從無到有得敲Kotlin代碼;

  b 打開之前的Android項目,通過上述的轉化工具先將有一個和若干Java文件轉為Kotlin代碼,通過閱讀轉化后的代碼可以快速熟悉Android項目的Kotlin的代碼;

  推薦從第二種方法開始,比較簡單,而且一般在將Java代碼轉過來之后會有一些小錯誤或者警告,在修改的過程中進步也是蠻大的。注明一下:Kotlin不只是能在Android項目中替代Java,在其官網有詳細的介紹。

  下面着重講述一下因為Kotlin使得代碼變得簡潔、安全以及巧妙的幾個點,這門新語言日漸成熟,不可能將其特點通過一兩篇文章就能覆蓋到。隨着學習的深入,之后會再進行補充。

2.1 text & setText()

1 text1.text = (Editable e)
1 text1.setText(CharSequence text)

  先來個簡單的,text1是某布局中TextView組件的id。就這樣一句代碼,就可以完成文本的設置了,沒有TextView類對象聲明,不用調用findViewById()查找,是不是簡潔好多。雖說這中間有些步驟還是需要Kotlin去默默處理,但是作為開發者,效率明顯提升了。一般常用的是后面一種,因為CharSequence或者String的使用頻率較高。但是Kotlin的簡潔形式都趨向於obj.field,而不是setField()這樣的方法,只不過這里將CharSequence轉為Editable稍微麻煩了一點:

1 text1.text = Editable.Factory().newEditable(message)

2.2 @ & when () {}

1 login_image_sel.setOnClickListener(this@LoginFragment)
2 login_in.setOnClickListener(this@LoginFragment)
3 login_reg.setOnClickListener(this@LoginFragment)
4 login_out.setOnClickListener(this@LoginFragment)

  給四個id代表的組件設置點擊監聽,方法參數為View.OnClickListener,如果類已經繼承了它並實現了其onClick()方法,那么直接寫成代表自己的this即可。而@Name部分則是強調類名稱(不寫也不影響編譯、運行),但是寫上之后可讀性更強了,可以說此@是寫成開發者(自己或未來看代碼的人)看的。

 1 override fun onClick(view: View) {
 2     val id = view.id
 3     when (id) {
 4         R.id.login_image_sel -> selectImageBtn()
 5         R.id.login_in -> loginInBtn()
 6         R.id.login_reg -> loginRegBtn()
 7         R.id.login_out -> loginOutBtn()
 8         else -> { }
 9     }
10 }

  第一行需要先解釋,Java代碼是這樣的:

1 @Override
2 public void onClick(View view) {}

  有幾個地方不同:

  a @Override->override;

  b default->public,Kotlin默認的訪問限制是public,所以可以省略;

  c void->: Unit,Kotlin無返回值可以省略,也可以寫成: Unit,當然看個人習慣了;

1 override fun onClick(view: View): Unit {

  d View view->view: View,形參聲明形式,具體的后面會再提到;

  Kotlin用when解放了switch,仔細琢磨一下可以發現,不管是代碼形式和行數都簡潔了不少。告別了原先Java的case、“:”、break及default,我認為最直接的好處是進入when之后,只會執行匹配項對應的"->"后面那一個分支,不用每次都要小心翼翼地想在哪加break。

2.3 var & val

1 val id = view.id

  接着上面的話題,來看一下變量的定義。看到上面這句代碼,熟悉腳本語言的朋友會有親切感,所以有時候覺得Kotlin是在慢慢地將各門語言的優點結合起來。

  val和var對應,前者定義不可能變量,后者定義可改變量。這時候將Java的final拿出來最合適了,Kotlin中限定不可變的重擔是由val來完成,它們限定的變量必須在聲明時就賦值(類直接屬性除外,具體的后面會再提到)。

  那么上面說到方法形參時(view: View),view后面有類型View,其實在聲明變量時也是如此,來看幾個例子:

1 var int1: Int = 1
2 var int2 = 2
3 var str3: String? = null
4 val str4: String? = null

  聲明了四個變量:兩個可變Int型,一個可變String型,一個不可變String型。既然不可變,那么在后面再次賦值時就會報錯了:

1 int1 = 2
2 int2 = 3
3 str3 = "Hello"
4 str4 = "Kotlin"  //會標紅線,鼠標移入顯示"Val cannot be reassigned"

  實踐過就會發現,Kotlin中不能再像Java那樣聲明變量了,比如:

1 var int3
2 var int4: Int

  這兩種都是不行的,會提示"Property must be initialized or  be abstract"。

  再來看是類型后的那個問號(String? = null),它表示聲明的變量是否允許為null。這要和另一種情況區分開:聲明變量的時候還不確定其值是什么,解決方式可以是先賦一個不影響程序的值。比如:

1 var str3: String = "will be reassigned later"

  ": String"類型限定部分可以不加,編譯器會自動推斷,這樣處理就沒有“?”。

  那么有人疑問加“?”和不加的根本區別在哪?就在於程序運行過程中對變量的賦值,如果給沒有加“?”的變量賦值了null,程序就會異常。一般用在函數的形參中,比如定義的方法形參如下:

1 fun testNotNull(str: String) {}

  調用時傳給形參str的值是不能為null的,這一特性非常有用。因為在大部分應用場景下都可以確定需要的參數是否可以null,比如讀取圖像的路徑不能為空,通過索引訪問元素時列表不能為空等等。這不是說可以完美地解決因為null引起的異常,而是可以將異常的點提前,或者說變量容易發現與消除。至於在不同場景怎么用,還得深入研究,並不是全部限定為NonNull就是最合適的。當然,非空限制在Java代碼中也可以通過注解@NonNull來實現。

  “?”還有一個很有用的地方是方法返回值:

1 fun getStringLength(obj: Any): Int? {
2     if (obj is String)
3         return obj.length // no cast to String is needed
4     return null
5 }

  在Java中聲明為int返回值類型后,是不允許返回null值的,可能會在沒有想要的結果時返回一個標記數值。而在Kotlin中只要檢查返回值是否為null,如果不是則返回值就是希望得到的結果。上面的代碼還可以簡寫為:

1 fun getStringLength(obj: Any): Int? = if (obj is String) obj.length else null

  這里有一個新的知識,is的作用是判斷obj是否是String類型實例應用,如果不是則直接返回null,Java是instanceof。

2.4 Any

  Any有點像Java中的Object,對象的祖先。直接上例子:

1 fun showLog(message: Any?) {
2     Log.i(LOG_TAG, message?.toString())
3 }

  這是在Utils文件中自定義的一個實現log的方法,形參message的類型時Any?,正好鞏固一下上一條的概念。對於傳入的實參,可以是null,也可以是任意類型的變量值;重點在於message后面的那個“?”,它會判斷message是否為null,如果是則直接返回字串“null”,如果不是才去調用toString()方法。注意這里假設傳入的實參對象繼承或重寫了toString(),否則可能會出錯。

2.5 Custom View

  這里說的並不是熟悉的自定義一個圓形View,然后在xml或者Java中進行使用,而是直接在代碼中生成布局與組件,這又是Kotlin的一個優點。來看定制一個Dialog的代碼:

 1 val dialog = Dialog(mContext, R.style.DialogNoTitle)
 2 dialog.setContentView(mContext.linearLayout {
 3     imageView {
 4         Utils.setImageToView(mContext, null, imageUri, this)
 5         onClick {
 6             dialog.dismiss()
 7         }
 8     }
 9 })
10 
11 dialog.show()

  代碼中的Style和Utils部分可以在項目源碼中查看,這里主要針對Kotlin定義布局部分。動態添加線性布局和一個圖像組件只需要聲明一個名稱即可,分別為linearLayout和imageView。可以理解為包含的元素以{}為界,imageView屬於linearLayout,而onClick {}和this則是屬於imageView。測試發現,顯示的Dialog默認就是居中的,想達到其他效果進行相應的調整即可。

2.6 Map

1 `object`.map {
2     var bulletinT = ReceiveBulletin(it.teacherName,
3             it.updatedAt,
4             it.bulletinContent,
5             it.bulletinImage?.fileUrl)
6     //do something
7 }
  這里的`object`可以是列表數據,也可以是數組等其他集合類型。map的作用就是遍歷集合中的每一個元素,對其在{}中進行處理,而每個元素的臨時名稱為“it”。這樣,是不是又可以不用看到for或者Iterator了。

2.7 Class

  2.3中提到過val聲明的變量也可以先不賦值,這種情況會在Class的聲明時出現:

1 class BulletinAdapter(private val mContext: Context,
2                       private val mBulletins: ArrayList<ReceiveBulletin>)
3         : RecyclerView.Adapter<BulletinAdapter.ViewHolder>() {}

  自定義了一個和RecyclerView結合使用的Adapter類,類的繼承由Java的extends變成了冒號“:”,仿佛進入了C++的世界。

  類名后面竟然跟了一個括號“()”,而且還多了那么多奇怪的參數,Kotlin的解釋是這樣寫相當於快捷的構造函數。其實可以理解為primary constructor方法省略了名稱,如果方法前有注解等特殊修改,那么名稱“constructor”是不能省略的。之所以說類后面跟着的方法為primary,是因為在類中還可以實現secondary constructors,以后用到時再深挖吧。而init是在類對象構造時就會調用一次,僅此一次,所以可以作為類實例時的標記,比如打印log:

1 init {
2     Utils.showLog("Create a BulletinAdapter object")
3 }

  val就不解釋了,傳入的實參賦給形參變量后,在本類中是不允許改變的。但是集合有點特殊,比如重新給mContext賦值不行,但是給mBulletins通過add()方法添加元素是可以的,這里涉及到對象指向的地址和包含的元素問題,先不展開。

  添加了private訪問限定符的目的和Java中類似,在類外不可見,即不允許在類外面對變量進行訪問與更改。

2.8 if ()  {} else () {}

  之前2.2中when分支->后面代碼都只有一句,如果有兩句呢?先看看if else的情形:

1 if (position == itemCount - 1)
2     itemView.bulletin_divider.visibility = View.GONE
3 else
4     itemView.bulletin_divider.visibility = View.VISIBLE

  當分支下有多個語句時,必須將屬於分支的所有代碼都包含在{}中,否則會出現下面的else匹配不到if的錯誤,這點和Java中的也是相同的。最后提一下Kotlin中不推薦在語句后寫分號“;”,寫上也沒錯,只是給變量名下面畫一條灰色的提醒而已。

 

3. 總結

3.1 項目介紹

  開頭給出了項目源碼下載地址和運行動態效果圖,現在來進行簡單的介紹。自己學習過程中練手的不能叫做App,確切地應該叫Demo。

  Demo一共三個頁面:消息接收、消息發送及用戶信息。

  a 消息接收:打開程序時,會從雲數據庫中讀取消息,如果有則加載,沒有則顯示提示信息(如稍后下拉刷新等);一條信息包括發送者頭像、名稱、發送時間、信息內容(文字或圖片至少有其一);如果有圖片內容,則點擊后放大;消息的接收沒有用戶登錄要求;

  b 發送消息:只有注冊並登錄的用戶才能進行消息的發送;發送的內容至少要有文字或圖片內容其中之一;

  c 用戶信息:先進行注冊,不能設置數據庫中已存在的用戶名,一定要選擇頭像,成功后一般會自動登錄;登陸后才能進行注銷操作;注銷后才能進行再次登錄操作;

這里說的雲數據庫指的是項目中用到的免費的Bmob雲平台,比較適合個人練習用,可以自己建表及定義表中的信息。

3.2 Kotlin未來學習計划

  文中提到的和項目中用到的知識點,都只是Kotlin語言的冰山一角。還有更有趣、奇妙的的地方值得去發現,相信以后可以在項目中將以前習以為常的、繁瑣的代碼進行更簡潔、高效的實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM