AndroidのUIのひとつにSpinnerがあります。
いくつかの候補から対象を選ぶときに最適なドロップダウン(プルダウン)リストが主な使い道です。見た目は違いますが、iOSならUIPickerViewがそれに相当します。
こんなの。
ところがこのSpinner、表示内容に対してwidthが十分取れないと残念な見た目になってしまいます。
ドロップダウンのリスト部分は画面に被って表示されますから、画面幅を超えない限り長くても大丈夫ですが、選択されたアイテムが表示されるSpinner自体はレイアウトに左右されます。
そこでどうしたいのかというと、ドロップダウンのリスト部分と選択されたアイテムの短縮形を表示したいのです。「ジミ・ヘンドリックス」を選ぶと「ジミヘン」になるようにね。
♪♪♪
標準的なSpinnerがセットアップされている前提です。
♪♪♪
まずは、ドロップダウンのリスト部分と選択されたアイテムのペアを格納するデータクラスを作成します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bar.foo.spinnersample | |
data class CustomSpinnerHolder(val name: String, val shortName: String) { | |
} |
Spinnerとリスト部分のレイアウトは基本的に変わりませんが、それぞれ名前(id)をつけます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<TextView | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
android:id="@+id/custom_spinner_short_name" | |
style="?attr/spinnerDropDownItemStyle" | |
android:singleLine="true" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:ellipsize="marquee" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<TextView | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
android:id="@+id/custom_spinner_name" | |
style="?attr/spinnerDropDownItemStyle" | |
android:singleLine="true" | |
android:layout_width="match_parent" | |
android:layout_height="?attr/dropdownListPreferredItemHeight" | |
android:ellipsize="marquee" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bar.foo.spinnersample | |
import android.content.Context | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.widget.BaseAdapter | |
import android.widget.TextView | |
class CustomSpinnerAdapter(val context: Context, var dataSource: List<CustomSpinnerHolder>) : BaseAdapter() { | |
private val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater | |
//region SpinnerAdapter | |
/** | |
* Gets a View that displays in the drop down popup the data at the specified position in the data set. | |
*/ | |
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View { | |
val view: View | |
val vh: ItemHolder | |
if (convertView == null) { | |
view = inflater.inflate(R.layout.spinner_artist_dropdown, parent, false) | |
vh = ItemHolder(view) | |
view?.tag = vh | |
} else { | |
view = convertView | |
vh = view.tag as ItemHolder | |
} | |
vh.label.text = dataSource.get(position).name | |
return view | |
} | |
//endregion | |
//region Adapter | |
/** | |
* How many items are in the data set represented by this Adapter. | |
*/ | |
override fun getCount(): Int { | |
return dataSource.size; | |
} | |
/** | |
* Get the data item associated with the specified position in the data set. | |
*/ | |
override fun getItem(position: Int): Any? { | |
return dataSource[position]; | |
} | |
/** | |
* Get the row id associated with the specified position in the list. | |
*/ | |
override fun getItemId(position: Int): Long { | |
return position.toLong(); | |
} | |
/** | |
* Get a View that displays the data at the specified position in the data set. | |
*/ | |
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { | |
val view: View | |
val vh: SelectedItemHolder | |
if (convertView == null) { | |
view = inflater.inflate(R.layout.spinner_artist, parent, false) | |
vh = SelectedItemHolder(view) | |
view?.tag = vh | |
} else { | |
view = convertView | |
vh = view.tag as SelectedItemHolder | |
} | |
vh.label.text = dataSource.get(position).shortName | |
return view | |
} | |
//endregion | |
//region PRIVATE METHODS | |
private class SelectedItemHolder(row: View?) { | |
val label: TextView | |
init { | |
label = row?.findViewById(R.id.custom_spinner_short_name) as TextView | |
} | |
} | |
private class ItemHolder(row: View?) { | |
val label: TextView | |
init { | |
label = row?.findViewById(R.id.custom_spinner_name) as TextView | |
} | |
} | |
//endregion | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bar.foo.spinnersample | |
import androidx.appcompat.app.AppCompatActivity | |
import android.os.Bundle | |
import android.view.View | |
import android.widget.AdapterView | |
import android.widget.Spinner | |
import android.widget.Toast | |
class MainActivity : AppCompatActivity() { | |
//region ITEMS ON SCREEN | |
private lateinit var spArtist: Spinner | |
//endregion | |
//region ITEMS ON SPINNER | |
val spinnerItems = listOf<CustomSpinnerHolder>( | |
CustomSpinnerHolder("ジミ・ヘンドリックス", "ジミヘン"), | |
CustomSpinnerHolder("エリック・クラプトン", "クラプトン"), | |
CustomSpinnerHolder("スティーヴィー・レイ・ヴォーン", "SRV"), | |
CustomSpinnerHolder("フランク・ザッパ", "ザッパ"), | |
CustomSpinnerHolder("ピート・タウンゼント", "ピート"), | |
CustomSpinnerHolder("スティーヴ・クロッパー", "クロッパー"), | |
) | |
//endregion | |
//region LIFECYCLE METHODS OF FRAGMENT | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
spArtist = findViewById<Spinner>(R.id.spnner_artist) | |
val adapter = CustomSpinnerAdapter(this, spinnerItems) | |
spArtist.adapter = adapter | |
spArtist.onItemSelectedListener = spinnerItemSelectedListener() | |
} | |
//endregion | |
//region ACTION EVENT | |
/** | |
* Spinnerが操作されたとき | |
*/ | |
private inner class spinnerItemSelectedListener : AdapterView.OnItemSelectedListener { | |
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { | |
when(parent?.id) { | |
R.id.spnner_artist -> { | |
Toast.makeText(this@MainActivity, spinnerItems[position].name, Toast.LENGTH_SHORT).show() | |
} | |
} | |
} | |
override fun onNothingSelected(parent: AdapterView<*>?) { | |
} | |
} | |
//region ACTION EVENT | |
} |
GitHubにこのサンプルを置いておきました。
やっぱりこれだよ、これ。