[Google Course] Android Basics in Kotlin(第4篇) —Android xml Layout
在這篇文章中我們將會學習如何在Android xml File 設計我們的使用者介面、保存我們的資源定義,透過製作一個簡單的小費計算App,學習下列主題:
● xml 介紹
● xml Layout開發
● QA
以及我們會介紹下列Material Design 的 Android 元件:
完成的專案畫面會是這樣:
專案畫面功能有:
● 輸入總共花費
● 選擇服務品質(Amazing(20%)、Good(18%)、OK(15%))
● 是否要小數無條件進位
為了怕有些人沒有看之前的文章,文章中會使用到的編譯環境與參考內容也在這邊列出給大家:
Kotlin 線上編譯環境
Android Studio 安裝
Google Course 的 Android Basics in Kotlin
而這次是介紹Android Basic in Kotlin 的單元2 Layouts — XML介面設計部分
首先我們在Android Studio 使用 Empty Activity 創造一個新的Kotlin 專案,將它取名為Tip Time,這次的文章跟之前的有一點不同的是我們將學習如何透過xml檔案來修改我們的UI,而不是使用Layout Design Layout Editor
兩者都可以達到相同的事情,但修改及了解xml檔案是身為Android 開發人員必須學會的技能之一啊!
[xml 介紹]
xml 的全名為「可延伸標示語言」(Extensible Markup Language),是一個以文字為基礎的標示語言,是一種容易給人類閱讀、拓展及有彈性的語言格式,同時又很容易讓電腦程式來辨識,因此可以用在許多地方,回想一下我們之前文章提到的Android內的 strings.xml,就是一種應用!
他的格式主要包含這些元素:
● 開始標籤(start-tag):以<, >, 標籤名稱組成,在標籤內可以包含屬性
● 屬性(attribute):一個標籤可以擁有多個屬性,彼此以空白間隔開即可
● 內容(content):通常指的是可顯示給使用者看的文字,亦或是再次包含其他 xml標籤元素(element)
● 結束標籤(end-tag):與開始標籤成雙成對
● 標籤元素(element):整個開始標籤、屬性、內容、結束標籤稱為一個標籤元素
因為xml是屬於巢狀排列的規則格式,因此我們常常將他用在儲存及傳輸資料,我們想像如果我們是大賣場後台管理人員,主管給我們一張下圖的商品架構(當然名稱我已經優化過了……):
我們可以用規劃好相對應的xml文件,如此一來以後有新的商品或分類都可以輕鬆的加入到我們的系統內,很輕鬆讓其他人及系統程式都能辨識這份xml文件:
[xml Layout開發]
而在Android Layout 中 xml格式內容有什麼含義呢??我們以剛剛創建的Tip Time App來做說明,打開activity_main.xml
(res > layout > activity_main.xml)
在文件右上角會有三個選項,我們可以從這裡去切換編輯Layout的模式,我們都是使用Design拖拉方式設計使用者介面,接下來我們切換到Code模式,使用Code模式用xml來開發設計;而Split 模式是Code與Design同時顯示,待會我們也會用到!
在Android 的Layout 檔案中,每一個UI元件都是用xml的一個標籤(Tag )來表示,裡面有屬性(Attribute)及內容(Content):
上面是一個簡單的Android UI Layout排版,由ConstraintLayout作為xml的根元素,在裡面放置另一個TextView元素,而這邊我們看到TextView的結束標籤是/>
,和剛剛的結束標籤不相同
這是因為這裡的TextView內不會在放置其他的元素,因此可以直接使用/>
作為結束標籤,而當然你也可以用第一種方法表示TextView元素:
那屬性都是在說些什麼呢?元素的屬性代表什麼意思我們之後設計Layout,新增元件的時候會一併提到,大家繼續看下去吧~
[EditText]
首先我們第一個要新增的UI元件是EditBox,我們一樣進到activity_main.xml
,但是這次我們使用的編輯模式為Split,應該會看到以下的畫面:
左邊是xml 程式編輯區,右邊是Design Edit區,此時我們將鼠標移至程式編輯TextView的地方,會發現右邊設計區將他highlight起來,是不是超級清楚的呢!那我們現在在程式區塊將TextView元素整個刪除,會發現右邊的設計區TextView也不見了,記得要從<TextView
刪除到 />
,同時我們也會發現設計區的Textview也會刪除
接著我們嘗試在xml加入編輯文字盒(EditText),這是一個讓使用者能夠編輯及修改文字的元件:
- 我們到 EditText 官方文件,然後找到編輯文字盒的範例程式,複製下來
- 在ConstraintLayout的開始標籤和結束標籤中間找一個空白行貼上範例程式,你的程式應該會長成這樣子:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="text"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3. 我們可以看到目前EditText是以紅色顯示的,顯示有"MissingConstraints" attribute的錯誤,就是少了一些在ConstraintLayout內的元件應該要有的限制屬性,像是上下左右的位置限制
4. 那我們就在EditText的屬性內加入平貼父元件上方與元件左方的限制條件屬性:
<EditText
android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:inputType="text"/>
5. 我們來看看 EditText 的屬性,並且修改:
● android:id
是在App內獨一無二的元件id,我們的其他xml 內的元件及Kotlin的程式都可以用這個id 來判斷,我們將這裡的id 改成一個較適當的id @+id/cost_of_service
,讓程式較具有可讀性
● android:layout_height
是設定為"wrap_content"
,他將EditText設定為一行文字高的元件
● android:layout_width
是設定為"match_parent"
,而這個設定在ConstraintLayout是不被允許的,所以我們將他改為160dp
● android:inputType
是之前我們在Designed也沒設定過的屬性,他目前被設定為"text"
,只能讓使用者輸入像是字母、符號等,但是我們要讓使用者輸入數字,因此我們將"text"
改為"numberDecimal"
,限制使用者輸入小樹的數字型態內容
●接著我們加入 android:hint="Cost of Service"
這個屬性,描述使用者應該輸入什麼樣的內容
現在我們的EditText應該長這個樣子:
<EditText
android:id="@+id/cost_of_service"
android:layout_height="wrap_content"
android:layout_width="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:inputType="numberDecimal"
android:hint="Cost of Service"
/>
[TextView]
我們第二個要加入的元件是TextView,用來告訴使用者這個編輯文字盒是要輸入什麼樣的訊息:
- 在EditText的結束標籤
/>
下一行打上<TextView
- 接著在新增一行並且打上下面的TextView的屬性:
android:id="@+id/service_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="How was the service?"
3. 用/>
在下一行結束TextView這個元素
目前可以看到我們的UI呈現下圖的樣子,這顯然不是這麼的正確,我們必須幫他修改位置屬性:
● 我們將 app:layout_constraintTop_toTopOf
這個屬性名稱改為 app:layout_constraintTop_toBottomOf
,告訴程式TextView元件的上方邊緣要貼齊某個其他元件的下方邊緣
● 從"parent"
改成 "@+id/cost_of_service"
,TextView的上方邊緣會從貼齊父元件變成貼齊id 為 "cost_of_service" 的元件
完成後兩個元件的相對位置應該會是如下圖:
[RadioButton]
接著我們來設計要讓使用者選擇這次服務的品質選項,在Android 裡面這個元件叫做RadioButton,在Google 搜尋 radio button android
,到官網看RadioButton的描述,會知道一般來說這個元件需要搭配RadioGroup一起來做設計,將選項群組起來,如果只有一個選項應該被選起來的話
現在我們回到Android Studio ,在TextView下方,但是還是在ConstraintLayout的範圍內,新增一行空白行打上<Ra
,可以看到Android Studio 提供一些有用的元件給你:
我們選擇RadioGroup
,預設會有兩個屬性 android:layout_width
、android:layout_height
,我們都幫他填入”wrap_content
”,完成後在RadioGroup屬性後方加上>
,Android Studio 會自動幫我們加上RadioGroup的結束標籤
這邊我們要注意到的是,RadioGroup的結束標籤和之前TextView還有EditText的結束標籤
/>
不一樣,這是因為RadioGroup本身就是需要有其他元件被包括在內的,就像是ConstaintLayout
接著我們先來完成RadioGroup的屬性設定,幫他加上下列的屬性,用來設定id、擺放位置限制、群組內選項擺放方式(android:orientation
):
android:id="@+id/tip_options"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/service_question"
接著我們新增第一個RadioButton來表示Amazing (20%)
在RadioGroup內,需要自行加入android:id
及android:text
兩個屬性:
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Amazing (20%)"
/>
嘗試加上另外兩個服務品質選項(Good (18%)
、OK (15%)
),完整的RadioGroup程式會長這樣子:
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/service_question"
app:layout_constraintStart_toStartOf="parent"
android:orientation="vertical">
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Amazing (20%)" />
<RadioButton
android:id="@+id/option_eighteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Good (18%)" />
<RadioButton
android:id="@+id/option_fifteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK (15%)" />
</RadioGroup>
目前我們的RadioButton沒有任何一個被選擇,但是他如果在一開始有一個初始選項的話,對使用者來說會更加直覺
而在RadioGroup裡面有一個屬性就可以幫全組內的選項做一個初始的選擇,那就是android:checkedButton
,在RadioGroup加上下列屬性,將初始選項設定成@id/option_twenty_percent
:
<RadioGroup
android:id="@+id/tip_options"
android:checkedButton="@id/option_twenty_percent"
...
現在執行程式,應該可以看到服務品質選項了!!
接著我們要來製作我們介面的最後一部分,我們會加入Switch、Button、TextView來讓使用者決定是否要無限小數無條件進位,及顯示最後的計算結果
[Switch]
在 RadioGroup 下方新增一行空白行,一樣打上<Sw
選擇Switch,將android:layout_width
設為 “0dp”
,如此一來這個元件的寬度將會符合我們待會限制的位置,android:layout_height
設為"wrap_content"
接著依序新增下列屬性:
1. id 設定為 "@+id/round_up_switch"
2. text 設定為 "Round up tip?"
3. layout_constraintEnd_toEndOf
設為"parent"
,因為我們需要Switch 元件的右邊貼在父元件的右邊緣
4. layout_constraintStart_toStartOf
設為"parent"
,因為我們需要Switch 元件的左邊貼在父元件的左邊緣
5. layout_constraintTop_toBottomOf
設為"@id/tip_options"
使Switch上邊緣貼齊RadioGroup的下邊緣
6. Switch另外還有一個是Switch開關的狀態: android:checked
,我們將他的值預設為"true"
7. 加上結束標籤 "/>"
Switch 完整程式碼應該長得像這樣子:
<Switch
android:id="@+id/round_up_switch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Round up tip?"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tip_options"
android:checked="true" />
[Button]
接著我們新增一個按鈕,如此一來使用著能要求根據上面的條件來開始計算小費
我們新增一個<Button
在Switch的下方,將android:layout_width
設為 “0dp”
,android:layout_height
設為"wrap_content"
接著依序新增下列屬性:
1. id 設定為 "@+id/calculate_button"
2. text 設定為 "Calculate"
3. layout_constraintEnd_toEndOf
設為"parent"
,因為我們需要Button 元件的右邊貼在父元件的右邊緣
4. layout_constraintStart_toStartOf
設為"parent"
,因為我們需要Button 元件的左邊貼在父元件的左邊緣
5. layout_constraintTop_toBottomOf
設為"@id/round_up_switch"
使Switch上邊緣貼齊Switch的下邊緣
6. 加上結束標籤 "/>"
<Button
android:id="@+id/calculate_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Calculate"
app:layout_constraintTop_toBottomOf="@id/round_up_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
[TextView]
我們新增一個<TextView
在Button的下方,將android:layout_width
設為 “wrap_content”
,android:layout_height
設為"wrap_content"
接著依序新增下列屬性:
1. id 設定為 "@+id/tip_result"
2. text 設定為 "Tip Amount"
3. layout_constraintEnd_toEndOf
設為"parent"
,因為我們需要TextView 元件的右邊貼在父元件的右邊緣
4. layout_constraintTop_toBottomOf
設為"@id/calculate_button"
使TextView上邊緣貼齊Button的下邊緣
5. 加上結束標籤 "/>"
<TextView
android:id="@+id/tip_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/calculate_button"
android:text="Tip Amount" />
現在我們已經完成了這個專案所有的介面製作,下一篇文章會來說明透過ViewBinding在Kotlin 與UI做互動,完成計算小費的功能吧!!
[QA]
● xml 檔案有哪四個元素組成?
● Code, Design, Split 在 Android 介面設計分別是怎麼呈現,你認為哪一種比較方便來做介面設計?
●android:id
有什麼用途?
● 如果要在 RadioGroup 內預設一個選項,要設定什麼屬性?
●app:layout_constraintEnd_toEndOf
是指對齊某元件的右邊還是下方?
●app:layout_constraintStart_toStartOf
是指對齊某元件的左邊還是上方?
喜歡我的文章的人也記得幫我按個拍手、分享,覺得很不錯的可以幫我拍個50下!
也要快點追蹤我的 FB粉絲專頁 — 飛比尋常的程式設計世界 ,不會太頻繁出現在你的塗鴉牆騷擾你,好文章生產需要一點時間,有錯誤或想討論的都歡迎留言給我唷!那就下次見拉!