HOME > android > basic

안드로이드 - Spinner(스피너) 구현 방법 (AndroidX, Kotlin)

By JS | 02 Dec 2018

안드로이드 스피너(Android Spinner)는 드롭다운 메뉴입니다. 스피너를 클릭하면 선택가능한 메뉴가 보이고, 이 메뉴 중 하나를 눌렀을 때 출력 값을 다르게 할 수 있습니다.

AndroidX RecyclerView 구현(kotlin) 글에서 구현한 것은 Android Dev Submmit의 Youtube 정보를 RecyclerView로 보여주는 앱이었습니다. 이 프로젝트에 스피너를 추가하여, Android Dev SubmmitGoogle I/O 두개의 정보를 선택적으로 보여주도록 앱을 구현해보겠습니다.

프로젝트 생성

AndroidX RecyclerView 구현(kotlin)에서 만든 프로젝트에서 Spinner를 추가해보겠습니다.

이 프로젝트는 GitHub에서 다운받을 수 있습니다.

리소스 추가

새로운 이미지 3개를 다운받아야하는데요. 완성된 프로젝트/res/drawable/에 보면 googleio로 시작하는 JPG 파일 3개가 있습니다. 이 이미지를 다운로드하여 자신의 프로젝트, 동일 위치에 파일들을 추가해주세요.

그리고 프로젝트의 리소스 strings.xml에 아래 3개의 문자열 추가해주세요.

/res/values/strings.xml
<resources>
  ...
<string name="io_title01">Keynote</string>
<string name="io_title02">Android: Open to the future</string>
<string name="io_title03">Wear OS by Google</string>
 ...
</resources>

마지막으로 arrays.xml을 만들고 아래 문자열 배열을 추가해주세요

/res/values/arrays.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="spinner_entries">
        <item >Android Dev Summit</item>
        <item >Android I/O</item>
    </string-array>
</resources>

스피너 정의

스피너는 XML 레이아웃에 다음과 같이 정의할 수 있습니다.

<Spinner
    android:id="@+id/planets_spinner"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />

저희는 entries 속성을 추가하여 activity_main.xml에 스피너를 정의하겠습니다.

/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Spinner
            android:id="@+id/spinner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:entries="@array/spinner_entries"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:listitem="@layout/list_item"/>

</LinearLayout>

여기서 스피너의 entries@array/spinner_entries를 참조하는데요. 스피너의 리스트 목록을 가져옵니다. 이렇게 리소스로 리스트를 관리할 수 있습니다.

앱을 빌드하고 실행해보면 UI에 스피너가 생성되었고 터치해보면 리소스에서 추가한 리스트가 나오는 것을 볼 수 있습니다. androidx spinner

리스너 등록

하지만 리스트를 선택해봐도 화면은 달라지지 않습니다. 리스너를 등록하여 특정 목록을 선택했을 때 화면 구성을 다르게 할 수 있습니다.

Spinner에 리스너는 아래 코드처럼 등록할 수 있습니다. 그럼 onNothingSelectedonItemSelected에 대한 콜백을 받을 수 있습니다.

spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
    override fun onNothingSelected(parent: AdapterView<*>?) {
    }
    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
    }
}

코드를 조금 수정해서, "Android Dev Summit"를 선택하면 Android Dev Summit에 대한 내용을 보여주고 Android I/O을 선택하면 Android I/O에 대한 내용을 보여주도록 수정해보겠습니다.

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var adapter: RecyclerAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val list = ArrayList<YoutubeItem>()
        adapter = RecyclerAdapter(list)
        recyclerView.adapter = adapter
        recyclerView.addItemDecoration(
            DividerItemDecoration(this, DividerItemDecoration.VERTICAL))

        spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
            override fun onNothingSelected(parent: AdapterView<*>?) {
            }

            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                Log.d("MainActivity",
                    "onItemSelected : $position, ${spinner.getItemAtPosition(position)}")
                when (spinner.getItemAtPosition(position)) {
                    "Android Dev Summit" -> {
                        updateAndroidSubmit()
                    }
                    "Android I/O" -> {
                        updateGoogleIO()
                    }
                    else -> {
                        updateAndroidSubmit()
                    }
                }
            }
        }
    }

    fun updateAndroidSubmit() {
        val list = ArrayList<YoutubeItem>()
        list.add(YoutubeItem(getDrawable(R.drawable.image01)!!, getString(R.string.title01)))
        list.add(YoutubeItem(getDrawable(R.drawable.image02)!!, getString(R.string.title02)))
        list.add(YoutubeItem(getDrawable(R.drawable.image03)!!, getString(R.string.title03)))
        list.add(YoutubeItem(getDrawable(R.drawable.image04)!!, getString(R.string.title04)))
        list.add(YoutubeItem(getDrawable(R.drawable.image05)!!, getString(R.string.title05)))
        list.add(YoutubeItem(getDrawable(R.drawable.image06)!!, getString(R.string.title06)))
        list.add(YoutubeItem(getDrawable(R.drawable.image07)!!, getString(R.string.title07)))
        list.add(YoutubeItem(getDrawable(R.drawable.image08)!!, getString(R.string.title08)))
        list.add(YoutubeItem(getDrawable(R.drawable.image09)!!, getString(R.string.title09)))
        list.add(YoutubeItem(getDrawable(R.drawable.image10)!!, getString(R.string.title10)))
        adapter.items = list
        adapter.notifyDataSetChanged()
    }

    fun updateGoogleIO() {
        val list = ArrayList<YoutubeItem>()
        list.add(YoutubeItem(getDrawable(R.drawable.googleio01)!!, getString(R.string.io_title01)))
        list.add(YoutubeItem(getDrawable(R.drawable.googleio02)!!, getString(R.string.io_title02)))
        list.add(YoutubeItem(getDrawable(R.drawable.googleio03)!!, getString(R.string.io_title03)))
        adapter.items = list
        adapter.notifyDataSetChanged()
    }
}

빌드해보면 컴파일 에러가 발생하는데요. RecyclerView의 item을 private val에서 var로 변경하면 에러가 해결됩니다.

RecyclerAdapter.kt
class RecyclerAdapter(var items: ArrayList<YoutubeItem>) :
  ....

빌드 후 실행해보세요. 스피너의 목록을 선택하면 UI가 변경되는 것을 확인할 수 있습니다.

androidx spinner

스피너 어댑터

XML에서 스피너의 엔트리를 설정하지 않고 코틀린 코드에서 설정할 수 있습니다. 이런 경우 직접 스피너의 어댑터를 생성해야 합니다.

val adapter = ArrayAdapter.createFromResource(
    this,
    R.array.spinner_entries, android.R.layout.simple_spinner_item
)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter

정리

간단히 XML로 스피너(Spinner)를 생성하는 방법과 리스너를 등록하여 목록에 따라 다르게 동작하도록 구현하였습니다. 또한 어댑터를 생성하여 스피너를 구현하는 방법을 알아보았습니다.

참고