iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 29
0
Software Development

Kotlin for Android系列 第 29

Day 29. Kotlin for Android Configuration | Dialog

  接下來的兩個章節會收錄一些獨立的小主題,主題之間不會有關聯,讀者可以另開一個專案跟著實作。

Configuration 組態

  在程式設計過程可以透過訪問組態取得一些資訊,透過 resources.configuration.xxx,可以得知許多設定參數值。

https://ithelp.ithome.com.tw/upload/images/20181112/201119449FiuhttfMw.png

  以下挑選五種,讀者可以將程式碼寫在 onCreate() 之中,觀察 Logcat 印出來的資訊:

Log.i("SHOW_CONFIG", "screenWidthDp: ${resources.configuration.screenWidthDp}")
Log.i("SHOW_CONFIG", "screenHeightDp: ${resources.configuration.screenHeightDp}")
Log.i("SHOW_CONFIG", "densityDpi: ${resources.configuration.densityDpi}")
Log.i("SHOW_CONFIG", "fontScale: ${resources.configuration.fontScale}")
Log.i("SHOW_CONFIG", "orientation: ${resources.configuration.orientation}")

I/SHOW_CONFIG: screenWidthDp: 411
I/SHOW_CONFIG: screenHeightDp: 750
I/SHOW_CONFIG: densityDpi: 560
I/SHOW_CONFIG: fontScale: 1.15
I/SHOW_CONFIG: orientation: 1

  使用模擬器旋轉一下設備的方向,可以觀察到 WidthHeightOrientation 的值皆產生變化,fontSacle 是手機系統設定字體大小,預設會是 1,作者有在設定 > 顯示 > 字體大小調整過,所以範例中會顯示 1.15 倍。


  要使用 orientation 來做判斷時,可以利用 Configuration.ORIENTATION_LANDSCAPE 取代使用 if (resources.configuration.orientation == 1) 這種方式,參照下列示範搭配 when 判斷:

Log.i("SHOW_CONFIG", "orientation: ${resources.configuration.orientation}")
Log.i("SHOW_CONFIG", "ORIENTATION_PORTRAIT: ${Configuration.ORIENTATION_PORTRAIT}")
Log.i("SHOW_CONFIG", "ORIENTATION_LANDSCAPE: ${Configuration.ORIENTATION_LANDSCAPE}")
when (resources.configuration.orientation) {
   Configuration.ORIENTATION_PORTRAIT -> {
       Log.i("SHOW_CONFIG", "直")
       // TODO
   }
   Configuration.ORIENTATION_LANDSCAPE -> {
       Log.i("SHOW_CONFIG", "橫")
       // TODO
   }
}

  在程式設計中,也可以透過 resources.configuration.screenWidthDp > 一個整數,判斷目前設備的顯示寬度,並提供適應大小的排版變化,例如昨天課程使用的 GridLayoutManager(this, 2),可以設計成當螢幕大於某個寬度時,改以多於 2 欄的方式排列。


Dialog

  接著的小節要介紹的是對話方塊,在 Android 中可以透過 AlertDialog API 來實作,首先在版面上增加一個按鈕當作觸發,按鈕名稱訂 alertBtn。對話方塊的實作很簡單,使用 AlertDialog.Builder 建立 AlertDialog 主體,再透過 show() 即可顯示方塊。

class MainActivity : AppCompatActivity() {
   lateinit var alertDialog: AlertDialog

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       // 設計對話方塊
       val alertBuilder = AlertDialog.Builder(this)
       alertBuilder
           .setTitle("標題")
           .setMessage("內容")
       // 建立對話方塊
       alertDialog = alertBuilder.create()
       // 按鈕觸發顯示對話方塊
       alertBtn.setOnClickListener { alertDialog.show() }
   }
}

  上方的程式碼為了方便介紹所以放在一起,設計與建立這兩個動作可以另外擷取成一個方法, 獨立出來讓程式不會混在一起。

https://ithelp.ithome.com.tw/upload/images/20181112/201119446Ufz8vmaTr.png


  下一步驟是在方塊上增加按鈕,在 alertBuilder.create() 上方插入新增按鈕的程式碼,Dialog 有三大區塊:標題、內容、動作按鈕,其中動作按鈕最多可以建立三種按鈕,分別是:

1. 正面:通常代表接受內容繼續執行
2. 負面:代表取消動作
3. 中立:使用者暫時不想決定時,例如稍後提醒的選項

  按鈕建立方式如下,可以在 set...Button() 時指定按鈕的文字,並利用 { } 實作按下按鈕後要執行的動作,其中可以使用兩個參數 dialog, witch。在下面的範例共用一個函式,透過 which 變數可以得知哪一個按鈕觸發,which 的值作者以註解方式加在各段設定按鈕的後方。

https://ithelp.ithome.com.tw/upload/images/20181112/201119444wxIosYQNz.png

alertBuilder
    .setPositiveButton("確定") { dialog, which ->
       showWitch(dialog, which) // -1
    }
    .setNegativeButton("拒絕") { dialog, which ->
       showWitch(dialog, which) // -2
    }
    .setNeutralButton("稍後決定") { dialog, which ->
       showWitch(dialog, which) // -3
    }

// 在 onCreate 之外建立的函式
private fun showWitch(dialog: DialogInterface?, which: Int) {
   Log.i("SHOW_CONFIG", which.toString())
}

  特別提醒:由於教學示範方便,所以並無使用文字資源管理的方式,正式開發應將 set...Button() 指定按鈕動作的名稱整合至 strings.xml。


  上面示範的是對話方塊,接著接著的是選擇清單,一樣有分三種型態:

  1. 傳統單一選項清單:使用者選擇其中一個選項後,就會自動關閉選擇清單,也可以在不選擇的情況下,透過點擊清單以外的螢幕區域關閉,`witch`` 值記錄的是選中的陣列的索引值。

https://ithelp.ithome.com.tw/upload/images/20181112/20111944H7lINOHuvW.png

val options = arrayOf("選項一", "選項二", "選項三")
val alertBuilder = AlertDialog.Builder(this)
alertBuilder
    .setTitle("標題")
    .setItems(options) { _, which  ->
        Log.i("SHOW_CONFIG", which.toString())
    }

  1. 確認式單一選項清單:傳統選單在 UI 上不會記錄使用者選過的項目,若有此需求可以使用 setSingleChoiceItems() 建立確認式的選項清單,並且需要再設定兩個按鈕作為正負面選擇。

https://ithelp.ithome.com.tw/upload/images/20181112/20111944iDtpntkfbQ.png

  setSingleChoiceItems() 需要兩個參數,第一個是選項組,第二個是預設選項的索引值,選項組的部分讀者可以沿用剛剛宣告的 options ,作者這邊另外再介紹一種方式:

alertBuilder
   .setTitle("標題")
   .setSingleChoiceItems(R.array.options, -1) { _, which ->
       Log.i("SHOW_CONFIG", which.toString())
   }

  於 res> values 新增一個 xml 資源檔案,名為 array.xml,依照下列格式建立資源,可以是 string-arrayinteger-arrayname 是決定陣列的名稱,在程式碼 R.array.[名稱] 會使用到。另外提醒同樣觀念,選項文字也應該整合至文字資源檔中。

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string-array name="options">
       <item>選項一</item>
       <item>選項二</item>
       <item>選項三</item>
   </string-array>
</resources>

  接著再加上兩個按鈕的設定就可以完成,注意在 setSingleChoiceItems() 這裡,因為用不到 dialog 可以用底線 _ 取代掉,which 會等於選到的項目索引。兩個 Button 裡面的 which 仍然是按鈕的識別碼,跟 setSingleChoiceItems() 之中的 which 是選項的識別碼不相同。

alertBuilder
   .setTitle("標題")
   .setSingleChoiceItems(R.array.options, -1) { _, which ->
       Log.i("SHOW_CONFIG", which.toString())
   }
   .setPositiveButton("確定") { dialog, which ->
       showWitch(dialog, which)
   }
   .setNegativeButton("取消") { dialog, which ->
       showWitch(dialog, which)
   }

  1. 確認式複選清單

https://ithelp.ithome.com.tw/upload/images/20181112/2011194416bS6SjKg8.png

  將剛才的 setSingle... 取代成 setMulti...第二個參數需要更換,現在的接收型態已變為布林陣列,如果沒有要預設選項,則可以用 null 表示全不選。

.setMultiChoiceItems(R.array.options, null) { _, which, isChecked ->
   Log.i("SHOW_CONFIG", "$which $isChecked")
}

  後方的 lambda 表示式也需要再加一個參數 isChecked,用來獲取每次點擊時是將選項勾選還是取消Log.i 的部分有加入一點變化,現在可以執行程式,觀察一下每次點選項目時會如何觸發。

  透過 Logcat 的資訊,可以知道每次勾選選項時,會得到該選項及變更值,因此可以使用一個容器統一裝載,以下程式碼示範如何記錄勾選的項目:

// 取得 XML 中陣列長度
val arraySize = resources.getStringArray(R.array.options).size
// 宣告一個相同大小的布林陣列,預設為 false,也可指定為 true 讓開啟對話方塊預設全選
var arrOpts = BooleanArray(arraySize) { false }
val alertBuilder = AlertDialog.Builder(this)
alertBuilder
   .setTitle("標題")
   // 第二參數 null 改為 arrOpts
   .setMultiChoiceItems(R.array.options, arrOpts) { _, which, isChecked ->
       // 每次勾選項目時,將陣列對應值更新
       arrOpts[which] = isChecked
       Log.i("SHOW_CONFIG", "${arrOpts.toList()}")
   }
   .
   .
   .

  今天只介紹了 Dialog 基礎部分,所展示的都是系統預設樣式,對話方塊也可以跟之前示範的 FoodGroup、FoodItem 清單項目一樣,透過 xml 自訂對話方塊的顯示樣式,可以參考這裡的步驟完成自訂樣式,不過官方文件上還是以 Java 語言格式呈現,讀者可以參照這篇教學了解 JavaKotlin 之間的轉換要如何替代,雖說官方標榜 Android Studio IDE 會自動轉換,但在撰寫本篇文章時,太過複雜的部分自動轉換後仍是需要自行調整的。

  因此本篇重點在於 Kotlin 的撰寫方式,比起 Java 寫法,在查詢語言設計方式的相關資源相對較少,作者也花了點時間從官方版本轉成可執行的 Kotlin 版本,至於 Dialog 共通與進階使用的概念部分已經有很多豐富的線上資源,就不再重複介紹了,我們明天見!


資料參考

Log-Android Developers
https://developer.android.com/guide/topics/ui/dialogs


上一篇
Day 28. Android RecyclerView - 2/2
下一篇
Day 30. 常用快捷鍵 | 應用程式 Icon
系列文
Kotlin for Android30

尚未有邦友留言

立即登入留言