Androidのドロップダウンリストって名称しか持てないの?
Androidのドロップダウンリスト(spinner)は普通に書くと名称しかドロップダウンリストに持てません。
ドロップダウンリストから選択すると上の例だと「ひとつめ」、「ふたつめ」、「みっつめ」、「よっつめ」のいずれかがリスナAdapterView.OnItemSelectedListener#onItemSelectedに渡されます。
ここでやりたいのはHTMLの<SELECT>...<OPTION>と同じように、
<select name="choice">
<option value="1">ひとつめ</option>
<option value="2">ふたつめ</option>
<option value="3">みっつめ</option>
<option value="4">よっつめ</option>
</select>
といったように
「ひとつめ」を選択 → 1 がonItemSelectedに渡されるようにする
「ふたつめ」を選択 → 2 がonItemSelectedに渡されるようにする
「みっつめ」を選択 → 3 がonItemSelectedに渡されるようにする
「よっつめ」を選択 → 4 がonItemSelectedに渡されるようにする
にしたいです。
名称と値のペアを保持するデータクラスを作る
名称と値のペアを保持するデータクラスを作ります。kotlinだと、たったこんだけです。
data class MyItem(var code: String, var name: String)
ArrayAdapterを継承して独自のAdapterを作る
通常名称しかない場合はArrayAdapterを使いますが、ArrayAdapterを継承して独自のAdapterを作ります。
class MyAdapter(context: Context, list: ArrayList<MyItem>) :
ArrayAdapter<MyItem>(context, android.R.layout.simple_spinner_item, list) {
init {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
・・・
}
MyAdapterのメソッドのoverride
このMyAdapterのコンストラクタの2番めの引数がドロップダウンリストに表示したい名称と値のペア、そのリストです。この、MyAdapterのメソッドをふたつoverrideしてやる必要があります。overrideしたままのメソッドの中身だと。
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
return super.getView(position, convertView, parent)
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
return super.getDropDownView(position, convertView, parent)
}
ですが、このまだだと、MyItem#toString()の結果がドロップダウンリストにでてしまいます。出てほしいのはMyItem.nameだけなのでこれらのメソッドを細工する必要があります。
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val textView = super.getView(position, convertView, parent) as TextView
textView.text = getItem(position)?.name ?: ""
return textView
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val textView = super.getDropDownView(position, convertView, parent) as TextView
textView.text = getItem(position)?.name ?: ""
return textView
}
ここのミソは、overrideしただけの処理はいきなりTextViewを返していますが、一旦TextViewで受け取り、
textView.text = getItem(position)?.name ?: ""
getItem(position)がMyItemを返すので、そこからnameだけを取り出しTextView.textに設定してやります。
このふたつのメソッドの違いですが、わかりずらくて・・・
getView は選択中の項目の表示レイアウトを生成します。なのでドロップダウンリストにフォーカスが当たってない時の表示。
getDropDownView はリストのレイアウトを生成まます。ドロップダウンリストにフォーカスが当たった時の表示。
ドロップダウンリストにリストを設定する側
ドロップダウンリストにリストを設定する側は
val spinner = binding.spinner
val myItemList = ArrayList<MyItem>()
myItemList.add(MyItem("1", "名称1"))
myItemList.add(MyItem("2", "名称2"))
myItemList.add(MyItem("3", "名称3"))
myItemList.add(MyItem("4", "名称4"))
myItemList.add(MyItem("5", "名称5"))
val myAdapter = MyAdapter(this, myItemList)
spinner.adapter = myAdapter
のようになります。ドロップダウンリストが選択された時にのリスナ(onItemSelected)もMyItem.codeだけ欲しいので、
spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?,view: View?, position: Int, id: Long) {
val spinnerParent = parent as Spinner
val myItem = spinnerParent.selectedItem as MyItem
Log.d("MyAdapter", "${myItem.name} = ${myItem.code}が選択されました")
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
の様に、
spinnerParent.selectedItem
がMyItemを返すのでそこからcode、nameを別々に取ってやる必要があります。
完成形はgitHubに置きました