Vintage appMaker의 Tech Blog

[예제] Kotlin에서 Spinner 커스텀 본문

Source code or Tip/Android(Java, Kotlin)

[예제] Kotlin에서 Spinner 커스텀

VintageappMaker 2021. 5. 12. 22:55

Android에서 앱을 만들다보면 

 

  • Button
  • TextEdit
  • TextView

다음으로 Spinner를 커스텀해야 할 때가 많다. 그런데 위의 3개와는 달리 Spinner를 커스텀 할 때에는 해야할 일들이 많다. 일반적으로 

 

1. Spinner 배경설정

2. Adapter 만들기 

3. Adapter의 getView에서 처리 

4. Spinner에 adapter 설정하기 

 

순서로 처리한다. 

 

1. Spinner 배경설정

Spiner의 backgroud에 drawable한 XML을 설정한다. 

 

■ bg_spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#333333"/>
            <corners android:radius="3dp" />
        </shape>
    </item>
    <item android:gravity="center_vertical|right" android:right="8dp">
        <layer-list>
            <item android:width="12dp" android:height="12dp"  android:gravity="center" android:bottom="10dp">
                <rotate
                    android:fromDegrees="45"
                    android:toDegrees="45">
                    <shape android:shape="rectangle">
                        <solid android:color="#eeeeee" />
                        <stroke android:color="#eeeeee" android:width="1dp"/>
                    </shape>
                </rotate>
            </item>
            <item android:width="20dp" android:height="11dp" android:bottom="21dp" android:gravity="center">
                <shape android:shape="rectangle">
                    <solid android:color="#333333"/>
                </shape>
            </item>
        </layer-list>
    </item>
</layer-list>

 

■ custom_spinner_item1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/text_first"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#333333"
        android:ellipsize="marquee"
        android:gravity="left|center_vertical"
        android:minWidth="130dp"
        android:paddingStart="20dp"
        android:singleLine="true"
        android:textColor="#eeeeee"
        android:textSize="12dp" />

</LinearLayout>

 

2. Adapter 만들기 

Spinner에서 사용할 Custom Adapter를 만들어 설정해야 한다. Kotlin의 경우, 확장함수를 지원하므로 이를 이용하면

범용적으로 편리하게 사용할 수 있다. 

fun View.dpToPx(dp: Float): Int = context.dpToPx(dp)
fun Context.dpToPx(dp: Float): Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics).toInt()
fun View.setHeight(context : Context, value: Int) {
    val lp = layoutParams
    lp?.let {
        lp.height = dpToPx(value.toFloat())
        layoutParams = lp
    }
}

fun Spinner.setCustomAdapter(context : Context, lst : MutableList<String>, unselectedTitle : String = "" ){
    class CustomSpnAdapter : BaseAdapter {
        var lst : MutableList<String> = mutableListOf<String>()
        var context : Context
        var unselectedTitle : String

        constructor (context: Context, lst: MutableList<String>, unselectedTitle: String){
            this.context = context
            this.lst     = lst
            this.unselectedTitle = unselectedTitle
        }

        override fun getCount(): Int {
            return lst.size
        }

        override fun getItemId(p0: Int): Long {
            return 0
        }

        override fun getItem(n: Int): String {
            return lst[n]
        }

        override fun getView(n: Int, p1: View?, p2: ViewGroup?): View {
            val v = LayoutInflater.from(context).inflate(R.layout.custom_spinner_item1, null)
            v.findViewById<TextView>(R.id.text_first)?.apply {
                text = lst[n]

                
                // ** 이 부분을 처리하지 않으면
                // ** spinner background가 커스텀 item으로 치환된다.
                // ** XML에서도 background를 같게 지정해주어야 한다.
                if(p2 is Spinner){
                    
                    background = context.getDrawable(R.drawable.bg_spinner)
                    if(p2.selectedItemPosition < 0 ){
                        p2.setHeight(context, 40)
                        setTextColor(Color.parseColor("#626466"))
                        text = unselectedTitle
                    }
                }
            }
            return v
        }
    }

    adapter = CustomSpnAdapter(context, lst, unselectedTitle)
    this.setSelection(-1)
}

3. getView 처리하기

인덱스에 따라 보여줄 View를 만들어 반환시킨다. 여기에서 ViewGroup형 변수는 팝업으로 보여줄 것인지, Spinner에서 직접보여줄 것인지를 판단할 수 있다. 그러므로 선택 후, Spinner 위젯을 바꾸고 싶다면 ViewGroup형 변수가 Spinner형인지 비교하여 처리하면 된다. 

 

4. getView 처리하기

Kotlin의 확장함수를 사용했다면 다음과 같이 간단하게 사용할 수 있다. 

...

val spn = findViewById<Spinner>(R.id.spnCustom)
spn.setCustomAdapter(this, mutableListOf("1", "2", "3", "4"))

...

 

그러면 다음과 같이 사용할 수 있다. 

 

Comments