iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
1
Software Development

Kotlin for Android系列 第 24

Day 24. Android Activity 物件傳遞 - 6/6

  今天一開始要來整理一下昨天新增的檔案,自其它教學中學到的概念,到目前為止我們新增了許多 Activity,又建立了 ExtraConstants,檔案有點多,因此使用資料夾來做分類,在下圖所示的位置,新增兩個 Pakage。

https://ithelp.ithome.com.tw/upload/images/20181107/20111944XHZMrPVAuu.png

  其中一個命名為 Controllers,存放所有的 Activity,另一個為 Utilities 用來存放 ExtraConstants,歸類後的架構:

https://ithelp.ithome.com.tw/upload/images/20181107/20111944tBit3ddcKe.png


  先前有提到可以使用 data class 的方式進行 Activity 之間值的傳遞,在歸類架構中我們新增一個 Package -> Models,在內建立 Kotlin File/Calss,此檔案內宣告一個 data class 如下圖所示:

https://ithelp.ithome.com.tw/upload/images/20181107/20111944ehTyFxKfyY.png

  注意在類別後方加入介面 Parcelable,同時按下 Alt + EnterAndroid Studio 自動實作介面方法,程式碼會自動產生如下:

data class Schedule(
   var arctic: Boolean,
   var antarctic: Boolean,
   var finland: Boolean): Parcelable {
   constructor(parcel: Parcel) : this(
       parcel.readByte() != 0.toByte(),
       parcel.readByte() != 0.toByte(),
       parcel.readByte() != 0.toByte()
   ) {
   }

   override fun writeToParcel(parcel: Parcel, flags: Int) {
       parcel.writeByte(if (arctic) 1 else 0)
       parcel.writeByte(if (antarctic) 1 else 0)
       parcel.writeByte(if (finland) 1 else 0)
   }

   override fun describeContents(): Int {
       return 0
   }

   companion object CREATOR : Parcelable.Creator<Schedule> {
       override fun createFromParcel(parcel: Parcel): Schedule {
           return Schedule(parcel)
       }

       override fun newArray(size: Int): Array<Schedule?> {
           return arrayOfNulls(size)
       }
   }
}

  不用修改任何東西,回到第二頁面就能使用剛剛的資料類別,拿來裝載選項是否選取的資料:

var schedule = Schedule(
   swtArctic.isChecked,
   swtAntarctic.isChecked,
   swtFinland.isChecked)

  接著就能使用 putExtra 將此物件傳遞過去 (EXTRA_AURORA_DATA 是新增在 ExtraConstants.kt 的常數)。第三頁面使用 getParcelableExtra() 方法來接受,注意在 <> 中要指定該物件的類型,也就是 data class Schedule

var schedule = intent.getParcelableExtra<Schedule>(EXTRA_AURORA_DATA)

  額外的應用補充:先前我們會先判斷,是否有至少選擇一項才能繼續往下一頁,在第二頁中可以用 if (schedule.arctic || schedule.antarctic || schedule.finland) 判斷。不過更好的方法應該是將判斷寫在 Schedule 類別中,可以有兩種實作方式,一種是透過屬性 getter 另一種則是 fun 方法:

// 屬性必須初始化,所以要 = false
var hasAnyChecked: Boolean = false
   get () {
       return (arctic || antarctic || finland)
   }

fun hasAnyChecked(): Boolean {
   return (arctic || antarctic || finland)
}

  接著就可以使用這兩種方式來做判斷:

https://ithelp.ithome.com.tw/upload/images/20181107/20111944qyRLLzuca3.png


  到目前為止的各頁面如圖所示:

https://ithelp.ithome.com.tw/upload/images/20181107/20111944zmpY7ega1H.png
https://ithelp.ithome.com.tw/upload/images/20181107/20111944Mceedb4LRq.png
https://ithelp.ithome.com.tw/upload/images/20181107/20111944dSw8B851Qn.png

  Line: 23 測試印出的 Log 如下 (注意作者在這邊更換了一個 TAG 'Test',若之前使用 TAG 'Flow' 在 Logcat 篩選器搜尋列上,記得更換才能看到)。

D/Test: Schedule(arctic=false, antarctic=true, finland=true)

  回到應用程式上,第三個頁面還是空的,這裡先放入一個 textView,在 xml 中原本使用的是 android:text 存放顯示在應用程式畫面上的文字,但在某些情境下,顯示在畫面上的文字會是動態產生 (透過程式語言),在這之前我們不希望任何字樣顯示在該物件上,不過一旦把 android:text 清除掉後,你會發現在預覽視窗中該物件會變得很小,不方便進行設計排版,這時可以利用 tools:text 取代。

  一樣在 xml 中新增一個 tools:text 標籤,將文字內容放在這裡就能在設計窗格中看到文字,且實際執行應用程式時,由於值還是空的,所以是看不到這個 textView 的 (該物件還是有在設計的位置,只是沒有內容),透過 tools:text 還有一個好處,我們可以預知動態值的空間,例如下圖中,用地點兩字暫代空間,這樣能方便在版面設計時將文字長度預先考慮進去。

https://ithelp.ithome.com.tw/upload/images/20181107/20111944XMLpDa4CBy.png


  接著到 Design > 工具箱 > Widgets,將一個 ProgressBar 新增至畫面中:

https://ithelp.ithome.com.tw/upload/images/20181107/20111944jTpXV54okW.png

  下一個示範是:將第二頁選擇的項目地點顯示到第三頁的畫面上,並且輪播。因此我們先調整一下之前建立的 data class ScheduleLine: 11 加入一個容器用 <Key, Value> 來盛裝 <地點名稱, 是否選取>Line: 21 再加入一個方法將 true 的項目提取主鍵出來,之前設計的 hasAnyChecked 屬性 (Line: 16)稍微調整一下,改用 lambda 方式判斷,如下圖所示。

https://ithelp.ithome.com.tw/upload/images/20181107/20111944y03wKFKhoK.png
(hasAnyChecked() 方法因為與同名屬性是做一樣的事情,就移除不用了)


  第三頁面 onCreate() 使用剛設計於 Schedule 類別中的 getCheckedValues,傳送到 showScheduling() 方法。

var schedule: Schedule = intent.getParcelableExtra(EXTRA_AURORA_DATA)
showScheduling(schedule.getCheckedValues())

  由於這個方法我們尚未建立,所以在 IDE 會顯示紅字,用滑鼠左鍵點一下紅色部分,按 Alt + Enter,選擇 Create function 選項。


  在 showScheduling 方法中,使用 HandlerRunnable 來進行畫面的非同步更新,如果只使用迴圈從 results 裡面取值並更新到 textView 的話沒辦法看到輪播過程,讀者可以嘗試看看,會發現只能看到最後一個選擇的項目。

  透過下列方式,會以 2.5 秒為間隔,顯示 results 裡的每個值,並且當所有值已經顯示過後,將文字改為完成,並隱藏處理中的圓圈圖示。

private fun showScheduling(results: Array<String>) {
   var index = 0
   val handler = Handler()
   handler.post(object : Runnable {
       override fun run() {
           if (index < results.count()) {
               textView.text = results[index++]
               handler.postDelayed(this, 2500)
           } else {
               textView.text = "完成"
               progressBar.visibility = View.INVISIBLE
           }
       }
   })
}

  可以將程式執行起來查看結果,有沒有正確的將所有選到的項目都顯示在螢幕上,並且最終呈現會是”完成”的字樣呢?Activity 的課程到此告一個段落,接續要進入 ListView 我們明天見!

https://ithelp.ithome.com.tw/upload/images/20181107/20111944z7wxvCqpPz.png


資料參考

Kotlin for Android: Beginner to Advanced | Udemy
https://www.udemy.com/devslopes-android-kotlin/

Android Tutorial: Update TextView Every Second. Timer
https://www.youtube.com/watch?v=6sBqeoioCHE


上一篇
Day 23. Android Activity 物件傳遞 - 5/6
下一篇
Day 25. Android ListView - 1/2
系列文
Kotlin for Android30

尚未有邦友留言

立即登入留言