본문 바로가기

[Android/Kotlin] 안드로이드 Spinner 커스텀 해보기(3) feat. Drop Down Menu

꿈꾸는블로그왕 2021. 1. 30.

2021/01/30 - [Android/Material Design] - [Android/Kotlin] 안드로이드 Spinner 커스텀 해보기 (1)

2021/01/30 - [Android/Material Design] - [Android/Kotlin] 안드로이드 Spinner 커스텀 해보기(2) feat. Hint 만들기

 

안녕하세요.

이번시간에는 Spinner 위젯 사용시 Drop Down Menu커스텀 하는 방법에 대해서 알아보겠습니다.

 

커스텀 하기 이전에는 보통 텍스값을 많이 사용합니다.

이번에는 이미지와 텍스트값이 같이 나타나도록 해보겠습니다.

 

완성된 모습은 아래와 같습니다.

 

이전 Spinner 구현 코드에서 이어서 해보겠습니다.

필요하신 분들은 이전 블로그 글을 참고부탁드립니다.

 

 

 

 

 

STEP01. Item Layout 만들기

아래와 같이 왼쪽에 이미지 오른쪽에 텍스트를 나타내는 레이아웃을 하나 만들어 줍니다.

[item_spinner.xml]

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
	xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:padding="8dp"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ImageView
        android:id="@+id/img_spinner"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginStart="8dp"
        android:src="@drawable/ic_calendar"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:contentDescription="@string/app_name" />

     <TextView
         android:id="@+id/txt_name"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginHorizontal="16dp"
         android:textSize="18sp"
         android:textStyle="bold"
         app:layout_constraintStart_toEndOf="@+id/img_spinner"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
         tools:text="2021"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

 

 

 

STEP02. Model 클래스 만들기

이미지와 텍스트를 저장할 데이터 클래스를 만들어줍니다.

[SpinnerModel.kt]

data class SpinnerModel(
    val image: Int,
    val name: String,
)

 

 

STEP03. 커스텀을 위한 Adapter 클래스 만들기

Spinner item을 커스텀하기 위해서 ArrayAdapter를 상속받는 Adapter 클래스를 하나 만들어 줍니다.

클래스 생성시 리스트를 넘겨받고 사용합니다. getView() 메소드에서는 눈에 보여지는 Spinner 모습을, 그리고 getDropDownView() 메소드에서는 메뉴가 나타났을 때 모습을 수정해 줍니다.

 

[SpinnerAdapter.kt]

class SpinnerAdapter(
    context: Context,
    @LayoutRes private val resId: Int,
    private val values: MutableList<SpinnerModel>
) : ArrayAdapter<SpinnerModel>(context, resId, values) {

    override fun getCount() = values.size


    override fun getItem(position: Int) = values[position]

    @SuppressLint("ViewHolder")
    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val binding = ItemSpinnerBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        val model = values[position]
        try {
            binding.imgSpinner.setImageResource(model.image)
            binding.imgSpinner.setColorFilter(ContextCompat.getColor(context, R.color.white))
            binding.txtName.text = model.name
            binding.txtName.setTextColor(ContextCompat.getColor(context, R.color.white))
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return binding.root
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
        val binding = ItemSpinnerBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        val model = values[position]
        try {
            binding.imgSpinner.setImageResource(model.image)
            binding.txtName.text = model.name

        } catch (e: Exception) {
            e.printStackTrace()
        }
        return binding.root
    }

}

 

 

STEP03. MainActivity에서 적용하기

listOfYear와 listOfMonth를 만들어 넘겨줄 SpinnerModel 리스트를 만들어 줍니다.

기존 어댑터를 삭제해 주시고, 새롭게 정의한 SpinnerAdapter를 생성하여 setAdapter()에 넣어주시면 됩니다.

[MainActivity.kt]

class MainActivity : AppCompatActivity() {

    private var _binding: ActivityMainBinding? = null
    private val binding get() = _binding!!

    private lateinit var spinnerAdapterYear: SpinnerAdapter
    private lateinit var spinnerAdapterMonth: SpinnerAdapter
    private val listOfYear = ArrayList<SpinnerModel>()
    private val listOfMonth = ArrayList<SpinnerModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setupSpinnerYear()
        setupSpinnerMonth()
        setupSpinnerHandler()
    }

    private fun setupSpinnerYear() {
        val years = resources.getStringArray(R.array.spinner_year)

        for (i in years.indices) {
            val year = SpinnerModel(R.drawable.ic_calendar, years[i])
            listOfYear.add(year)
        }
        spinnerAdapterYear = SpinnerAdapter(this, R.layout.item_spinner, listOfYear)
        binding.spinnerYear.adapter = spinnerAdapterYear
    }

    private fun setupSpinnerMonth() {
        val monthsContainHint = mutableListOf("월")
        val months = DateFormatSymbols().months
        monthsContainHint.addAll(months)

        for (i in monthsContainHint.indices) {
            val month = SpinnerModel(R.drawable.ic_bedtime, monthsContainHint[i])
            listOfMonth.add(month)
        }

        spinnerAdapterMonth = SpinnerAdapter(this, R.layout.item_spinner, listOfMonth)
        binding.spinnerMonth.adapter = spinnerAdapterMonth
    }

    private fun setupSpinnerHandler() {
        binding.spinnerYear.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val year = binding.spinnerYear.getItemAtPosition(position) as SpinnerModel
                if (!year.name.equals("연도")) {
                    binding.txtYear.text = "Selected: ${year.name}"
                }
            }

            override fun onNothingSelected(p0: AdapterView<*>?) {

            }
        }

        binding.spinnerMonth.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val month = binding.spinnerMonth.getItemAtPosition(position) as SpinnerModel
                if (!month.name.equals("월")) {
                    binding.txtMonth.text = "Selected: ${month.name}"
                }
            }

            override fun onNothingSelected(p0: AdapterView<*>?) {

            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

댓글