概要
0. (参考)標準Spinnerのデザイン実装方法
発展例を紹介する前に標準的なUIについて確認していきます。
標準SpinnerのUIは以下のような感じになっています。
①xmlファイルにSpinnerを追加する。
<Spinner
android:id="@+id/sp_default"
android:layout_width="200dp"
android:layout_height="50dp"/>
②DropDownリストのUIを設定する。(ImageViewを追加したりカスタマイズ可能)
spinner_dropdown_item.xml
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"★xmlのheightに合わせる方が見栄えが良い
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:gravity="center_vertical"
android:singleLine="true"
android:ellipsize="marquee"/>
③Spinnerの中身を設定する。
val defaultSpinner:Spinner = binding.spDefault
//or val defaultSpinner:Spinner = view.findViewById(R.id.sp_default)
//表示するアイテム
val defaultItems = mutableListOf("赤色","青色","黄色")
val defaultArrayAdapter = ArrayAdapter(requireContext(),
R.layout.spinner_dropdown_item, defaultItems)
defaultSpinner.adapter = defaultArrayAdapter
※参考 SpinnerのBackgroundには以下が設定されているようです。(使い勝手が悪い・・・。)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingMode="stack"
android:paddingStart="0dp"
android:paddingEnd="48dp"★右側に▼のサイズのpaddingを設定
android:paddingLeft="0dp"
android:paddingRight="0dp">
<item★タップエフェクト
android:gravity="end|fill_vertical"
android:width="48dp"
android:drawable="@drawable/control_background_40dp_material" />
<item★「▼」のマーク
android:drawable="@drawable/ic_spinner_caret"
android:gravity="end|center_vertical"
android:width="24dp"
android:height="24dp"
android:end="12dp" />
</layer-list>
1. SpinnerのUIを実装する。
①xmlファイルにSpinnerを追加する。
<Spinner
android:id="@+id/sp_custom"
android:layout_width="200dp"
android:layout_height="50dp"
android:background="@drawable/bg_spinner"
android:foreground="@drawable/fg_spinner"/>
②背景画像を実装する。(ボタン枠)
drawable/bg_spinner.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingMode="stack"
android:paddingStart="24dp"★左側に角丸にした分だけpadding追加(DropDownの座標に影響する)
android:paddingEnd="48dp"★右側に▼のサイズのpaddingを設定
android:paddingLeft="0dp"
android:paddingRight="0dp">
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 背景色 -->
<solid android:color="#ffffffff" />
<!-- 角丸 -->
<corners android:radius="24dp" />
</shape>
</item>
</layer-list>
③重ねる前景画像を実装する。(▼マークとタップエフェクト)
drawable/fg_spinner.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingMode="stack"
android:paddingStart="24dp"
android:paddingEnd="48dp"
android:paddingLeft="0dp"
android:paddingRight="0dp">
<item★タップエフェクト
android:drawable="@drawable/control_background_40dp_material"
android:gravity="end|fill_vertical"
android:width="48dp" />
<item★「▼」のマーク
android:drawable="@drawable/ic_spinner_caret"
android:gravity="end|center_vertical"
android:width="24dp"
android:height="24dp"
android:end="12dp" />
</layer-list>
drawable/control_background_40dp_material.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/control_highlight_material"
android:radius="20dp" />
drawable/control_highlight_material.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
android:state_enabled="true"
android:alpha="@dimen/highlight_alpha_material_colored"
android:color="?attr/colorControlActivated" />
<item android:color="?attr/colorControlHighlight" />
</selector>
drawable/ic_spinner_caret.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:pathData="M7,10l5,5,5-5z"
android:fillColor="@color/white"/>
</vector>
2. Spinnerの内部処理を実装する。
①各DropDownアイテムのUIの情報を構成するDropDownItemクラスを実装する
class DropDownItem {
private var mLabel: String //Spinnerで表示する文字
private var mTextColor = 0 //Spinnerの文字色
private var mBackgroundColor = 0 //Spinnerの背景色
constructor(label: String, textColor: Int, backgroundColor: Int) {
mLabel = label
mTextColor = textColor
mBackgroundColor = backgroundColor
}
fun getLabel(): String {
return mLabel
}
fun getTextColor(): Int {
return mTextColor
}
fun getBackgroundColor(): Int {
return mBackgroundColor
}
}
②DropDownを表示した際に、各アイテムごとにUIを変えるCustomAdapterを実装する。
class CustomAdapter : ArrayAdapter<DropDownItem> {
//spinner_dropdown_itemは好きなレイアウトに変更して可。
constructor(context: Context, list: ArrayList<DropDownItem>) :
super(context, R.layout.spinner_dropdown_item, list)
//SpinnerのUI
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val textView = super.getView(position, convertView, parent) as TextView
textView.text = getItem(position)?.getLabel()
return textView
}
//DropDownアイテムのUI
override fun getDropDownView(
position: Int,
convertView: View?,
parent: ViewGroup
): View {
val textView = super.getDropDownView(position, convertView, parent) as TextView
//Label設定
textView.text = getItem(position)?.getLabel()
//テキスト色
getItem(position)?.getTextColor()?.let { textView.setTextColor(it) }
//背景色
getItem(position)?.getBackgroundColor()?.let { textView.setBackgroundColor(it) }
return textView
}
}
③Spinnerのデータ設定・選択されたアイテムのUIをSpinnerに反映させる
(以下の例だと、黄色を選択した際に文字色と▼マークを黒色にして、背景色を黄色にする。)
//データ設定
val customItems = ArrayList<DropDownItem>()
customItems.add(DropDownItem("赤色", Color.WHITE, Color.RED))
customItems.add(DropDownItem("青色", Color.WHITE, Color.BLUE))
customItems.add(DropDownItem("黄色", Color.BLACK, Color.YELLOW))
val arrayAdapter = CustomAdapter(requireContext(), customItems)
customSpinner.adapter = arrayAdapter
//選択されたアイテムごとにSpinnerのUIを変更する
customSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
(parent.getChildAt(0) as TextView).setTextColor(customItems[pos].getTextColor())
customSpinner.foreground.colorFilter =
BlendModeColorFilter(customItems[pos].getTextColor(), BlendMode.SRC_ATOP)
customSpinner.background.colorFilter =
BlendModeColorFilter(customItems[pos].getBackgroundColor(), BlendMode.SRC_ATOP)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
最後に
ぐぐったらこういった特殊な実装はできないみたいな古いQAが見つかったので、作ってみました。
こういうやり方すればもっと楽に実装できますよとかあれば、コメントいただけると非常にためになります。