在 Android 開發中,原生 Spinner 雖然方便,但外觀與行為常常不容易客製化。這篇將以 QuerySpinner 為例,實作一個自訂 Spinner 元件,說明如何在 Java 中設計自訂元件,並在 XML 佈局中使用,讓你能完全掌控 UI 細節、互動行為和外觀。
自訂元件(Custom View)是指開發者根據需求,繼承 Android 的基礎元件(如 View、Button、Spinner 等),並加以擴充或改寫,設計出獨特外觀、行為或功能的 UI 元件。
常見原因包含:
Spinner 是 Android 常用的下拉選單元件,但預設外觀與行為有限,無法直接修改下拉箭頭、背景、展開位置等細節。因此,開發自訂 Spinner 能讓我們更靈活地設計專案所需的選單效果。
下面是完整的 QuerySpinner 範例,這個自訂 Spinner 支援動態切換背景、初始化選擇行為、選項被重複選擇時仍能觸發事件等功能。
// 自定義的 Spinner 元件
public class QuerySpinner extends AppCompatSpinner {
private boolean isInitialization = false;
private boolean isSettingInitialValue = false;
// 建構函數
public QuerySpinner(Context context) {
super(context);
init();
}
public QuerySpinner(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@SuppressLint("ClickableViewAccessibility")
private void init() {
// 初始化觸摸事件監聽器:點選時設細框背景
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
setBackgroundResource(R.drawable.process_button);
}
return false;
}
});
// 選擇事件處理
setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// 初始化設置選擇項時不觸發粗框
if (!isInitialization) {
isSettingInitialValue = true;
QuerySpinner.this.setSelection(0); //初始化選第一個
isSettingInitialValue = false;
setBackgroundResource(R.drawable.process_button); //細框
isInitialization = true;
} else {
setBackgroundResource(R.drawable.select_button); //一般選擇為粗框
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
setBackgroundResource(R.drawable.process_button); //沒選則細框
}
});
}
// 展開下拉選單時設細框
@Override
public boolean performClick() {
setBackgroundResource(R.drawable.process_button);
return super.performClick();
}
// 處理重複選擇同一選項的事件觸發
@Override
public void setSelection(int position) {
if (isSettingInitialValue) {
super.setSelection(position);
return;
}
boolean sameSelected = position == getSelectedItemPosition();
super.setSelection(position);
if (sameSelected && isInitialization && getOnItemSelectedListener() != null) {
getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
}
}
}
在 layout 檔中直接使用自訂元件,並設定自訂屬性與外觀:
<com.example.aps_true.ui.QuerySpinner
android:id="@+id/query_process_spinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="85dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:spinnerMode="dropdown"
android:background="@drawable/process_button" />
android:background="@drawable/process_button" 設定自訂背景(shape 或 vector)自訂元件可以搭配自訂背景,讓外觀更有辨識度。例如用 VectorDrawable 設計格線效果:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:fillColor="#3DDC84" android:pathData="M0,0h108v108h-108z" />
<!-- 多條格線 ... 詳見原始檔案 -->
</vector>
將此 vector 檔案放在 res/drawable/process_button.xml,並在 Spinner 的 android:background 屬性引用。
你也可以用 shape 或圖片當作自訂背景。
自訂選項 Layout
Spinner 項目預設只能用簡單文字,可自訂 item layout(如 icon+文字):
ArrayAdapter<String> adapter = new ArrayAdapter<>(context, R.layout.custom_spinner_item, dataList);
spinner.setAdapter(adapter);
custom_spinner_item.xml 可設計自己想要的樣式。
支援多種資料型態
可根據需求讓 Spinner 支援不同資料(如物件、圖片),並在 Adapter 轉換顯示。
事件回調
可設定 setOnItemSelectedListener(),處理選項變動:
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// 自訂互動邏輯
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
封裝重用
若常用同一種 Spinner 樣式/行為,可將元件包裝與初始化邏輯都放進 QuerySpinner,方便專案多處引用,維護更省力。
進階互動
可加入動畫、過濾功能、非同步資料載入等,讓 Spinner 更智慧。
自訂元件讓你能完全掌控 UI 細節,無論是外觀、互動或邏輯,皆可依需求擴充。
以 Spinner 為例,藉由繼承、屬性擴充、背景美化與自訂選項,你可以打造出專屬於自己專案的選單元件,並在多個畫面重複使用,大幅提升開發效率與設計一致性。
未來如果有更進階需求,也可以持續擴充元件功能,讓 APP 更貼近你的理想!