情境
如果想要做一個單選的按鈕選單,可以使用 RadioButton 這個元件。
完整程式碼
如果你想要參考完整程式碼,可以參考我在 GitHub 上所寫的範例。
程式碼說明
如果要加入兩個單選鈕,讓使用者能夠單一選擇其中一個選項。
方法一
如果要使用一個 RadioButton 你可以直接在程式內宣告,
先將xml改成LinearLayout且設定android:orientation="vertical"
為垂直的
這樣就可以把兩個RadioButton加進去且不會重疊。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.givemepass.radiobuttondemo.MainActivity">
</LinearLayout>
接著就把MainActivity加入兩個RadioButton
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val programmer = RadioButton(this)
programmer.text = "程式設計師"
val designer = RadioButton(this)
designer.text = "UI設計師"
container.addView(programmer)
container.addView(designer)
}
}
另外再將 xml 的 container 改成 LinearLayout,這樣一來就可以看到有兩個單選鈕跑出來了。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</LinearLayout>

這時候你會發現一個問題, 就是同時兩個按下去都會變Enabled。

原來 RadioButton
外面還要再包一層 RadioGroup
才能達到這樣的效果。
- 注意 : RadioGroup 是 LinearLayout 的子類別,排版方式預設是垂直的。
val programmer = RadioButton(this)
programmer.text = "程式設計師"
val designer = RadioButton(this)
designer.text = "UI設計師"
val group = RadioGroup(this)
group.addView(programmer)
group.addView(designer)
container.addView(group)
變正常了。


不過隨著開發時間越來越久, 加入的元件就越來越多, 我們的RadioButton也越加越多, 這樣實在不是一個很好的管理方式。
###方法二
我們將元件宣告部分寫進XML, 會讓我們比較好管理,
布局也比較方便。
所以我們只要在XML寫入
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.givemepass.radiobuttondemo.MainActivity">
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:text="程式設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:text="UI設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RadioGroup>
</LinearLayout>
你會發現這樣的宣告方法比較乾淨且方便。

處理事件
如果要偵測事件可以有幾種方式來完成。
方法一
直接從 XML 設定,先把兩個 RadioButton 設定好 ID,並且讓它們指派 onClick 事件為 onSelect 方法。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.givemepass.radiobuttondemo.MainActivity">
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:onClick="onSelect"
android:id="@+id/program"
android:text="程式設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:onClick="onSelect"
android:id="@+id/ui"
android:text="UI設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RadioGroup>
</LinearLayout>
接著在MainActivity宣告這個方法,再指派這個方法有三個重點。
- 方法一定為
public
- 回傳值一定要為
void
- 傳入的參數一定是
View
fun onSelect(view: View) {
when (view.id) {
R.id.program -> Toast.makeText(this@MainActivity, "安安, 程式設計師!", Toast.LENGTH_SHORT).show()
R.id.ui -> Toast.makeText(this@MainActivity, "安安, UI設計師!", Toast.LENGTH_SHORT).show()
}
}
如下圖就會看到事件跟View進行綁定了。


可是這樣會有一個問題就是會不好追蹤方法, 專案一大其實會找不太到方法被塞到哪邊去,因此,比較好的做法就是把事件寫到程式碼內。
方法二
我們先將 XML 的 onSelect
方法拿掉。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.givemepass.radiobuttondemo.MainActivity">
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/program"
android:text="程式設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/ui"
android:text="UI設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RadioGroup>
</LinearLayout>
接著到 MainActivity 內加入事件的宣告,並且把它指定給 RadioButton,最後,再根據傳入的 ID 來辨識是哪個 RadioButton 被按下。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
program.setOnCheckedChangeListener(mOnCheckedChangeListener)
ui.setOnCheckedChangeListener(mOnCheckedChangeListener)
}
private val mOnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { buttonView, _ ->
when(buttonView.id){
R.id.program -> Toast.makeText(this@MainActivity, "安安, 程式設計師!", Toast.LENGTH_SHORT).show()
R.id.ui->Toast.makeText(this@MainActivity, "安安, UI設計師!", Toast.LENGTH_SHORT).show()
}
}
}
看到的結果會跟用 xml 宣告的是一致的,結果如下圖。


設定 Style
既然 RadioButton 是View,那麼它就可以改變它的外觀。
在 Android SDK 內有內建許多 Icon 提供你使用, 路徑如下\AndroidSDK\platforms\android-24\data\res\drawable
,我們就先透過下面這張預設圖來實作。

先在 xml 上面的 RadioButton 加入 background 屬性。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.givemepass.radiobuttondemo.MainActivity">
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:background="@android:drawable/radiobutton_off_background"
android:id="@+id/program"
android:text="程式設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:background="@android:drawable/radiobutton_off_background"
android:id="@+id/ui"
android:text="UI設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RadioGroup>
</LinearLayout>
你就會看到我們將選取的(或自定義)的 drawable 被設定到 RadioButton 的 background。

畫面變得很奇怪, 因為改變樣式是不能透過 `android:background` 屬性的,應該是要用 `android:button` 這個屬性才對,xml 調整一下。
<RadioButton
android:button="@android:drawable/radiobutton_off_background"
android:id="@+id/program"
android:text="程式設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:button="@android:drawable/radiobutton_off_background"
android:id="@+id/ui"
android:text="UI設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

畫面變成我們想要的了,但是很詭異,為什麼按下去沒有效果呢? 原來我們設定 button 的時候,將預設的 selector 效果取代掉了,所以我們要自己來寫一個 selector,先一樣從SDK內取出打勾的效果,如下圖。

接著從 Android Studio 內點選 res 右鍵選擇 Drawable resource file
,建立一個 radio_button_selector.xml
。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" android:drawable="@android:drawable/radiobutton_off_background" />
<item android:state_checked="true" android:drawable="@android:drawable/radiobutton_on_background" />
</selector>
接著指派到原本的 activity_main.xml 內的 RadioButton。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.givemepass.radiobuttondemo.MainActivity">
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:button="@drawable/radio_selector"
android:id="@+id/program"
android:text="程式設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:button="@drawable/radio_selector"
android:id="@+id/ui"
android:text="UI設計師"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RadioGroup>
</LinearLayout>
發現就出現我們想要的style了。


這樣就是一個簡單的 RadioButton 操作了。