0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Android] Spinnerの実装あれこれ

Last updated at Posted at 2024-09-23

概要

こんな感じのSpinnerのデザインを実装したい。
Screenshot_20240923_221321.png

0. (参考)標準Spinnerのデザイン実装方法

発展例を紹介する前に標準的なUIについて確認していきます。
標準SpinnerのUIは以下のような感じになっています。
Screenshot_20240923_220127.png

①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を実装する。

ボタンに▼マークが内包されるようなUIを実装していきます。
Screenshot_20240923_221321_2.png

①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を実装する。
Screenshot_20240923_221321_3.png

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に反映させる
(以下の例だと、黄色を選択した際に文字色と▼マークを黒色にして、背景色を黄色にする。)
Screenshot_20240923_230333.png

//データ設定
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が見つかったので、作ってみました。
こういうやり方すればもっと楽に実装できますよとかあれば、コメントいただけると非常にためになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?