본문 바로가기

Android/개인 기록

[Android Tip] Spinner: DropDown(다른 글로 수정됨)

Photo by Usplash, Sten Ritterfeld

DropDown Spinner

아래의 이미지 처럼 터치했을 때, 값을 사용자가 선택할 수 있도록 목록 형태로 펼쳐지는 것을 드롭다운(Drowdown) 이라고 한다.

안드로이드에서 드롭다운은 스피너(Spinner) 를 이용해 구현할 수 있다.

이번 포스팅에서는 스피너를 구현하는 방법과 커스텀하는 방법에 대해 알아보겠다.

ViewBinding 환경을 고려하고 작성된 글이다.

 

이번 글의 내용에서 만들어가는 드롭다운의 결과는 아래와 같다.

간단하게 드롭다운에서 아이템을 선택하면 그에 따라 텍스트가 바뀌는 예제다.

 

코드 전문은 아래 깃허브 저장소에서 확인 가능하다.

샘플 프로젝트 코드 링크

 

GitHub - parade621/Basic_Spinner: Tstory Basic Spinner DropDown 코드 전문

Tstory Basic Spinner DropDown 코드 전문. Contribute to parade621/Basic_Spinner development by creating an account on GitHub.

github.com


💻 Code

Layout resource

<Spinner
    android:id="@+id/my_spinner"
    style="@style/Widget.AppCompat.Spinner"
    android:layout_width="100dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:background="@drawable/border_round_5_e5e5e5_arrow"
    android:dropDownWidth="match_parent"
    android:dropDownVerticalOffset="35dp"
    android:popupBackground="@android:color/white" />

 

다른 뷰와 다르게 생소한, dropDownWidthdropDownVertivalOffset에 대해서 알아보겠다.

 

dropDownWidth 

  • 스피너의 드롭다운 목록의 너비를 지정(내려오는 그 박스 가로길이)
  • 값이 match_parent로 설정되면 드롭다운 목록의 너비는 스피너의 너비와 동일하게 된다.

dropDownVerticalOffset

  • 스피너의 드롭다운 목록이 표시되는 수직 위치를 지정하는 오프셋 값
  • 기본적으로 드롭다운 목록은 스피너 바로 아래에 표시된다.
  • 하지만 dropDownVerticalOffset속성을 사용하여 드롭다운 목록의 시작 위치를 조절할 수 있다.
  • 예를 들어, 위 코드처럼 dropDownVerticalOffset을 35dp로 설정하면 드롭다운 목록은 스피너 바로 아래에서부터 35dp 아래로 내려간 위치에서 시작된다. 이로 인해 스피너와 드롭다운 목록 사이에 간격이 생긴다.
  • 추가 팁(?)으로, minWidth나 minHeight로 어느정도의 크기를 보장해 두면 View가 뭉그러지는 것을 일부 방지할 수 있다.

 

테두리 drawable resource

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <corners android:radius="5dp" />
            <solid android:color="@color/white" />
            <stroke
                android:width="1dp"
                android:color="#e5e5e5" />
        </shape>
    </item>
    <item android:right="16dp" android:gravity="center_vertical|end">
        <bitmap android:src="@drawable/btn_arrow_down_black"/>
    </item>
</layer-list>

 

btn_arrow_down_black은 아래 방향을 가르키는 png 이미지 아무거나 넣어줬다.

 

 

대충 이런 모양새를 위한 코드다.

 

Adapter 코드

필자는 onCreate()에 작성했다.

val spinnerAdapter = ArrayAdapter(this, R.layout.simple_spinner_item, genderType)
    spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
    binding.mySpinner.adapter = spinnerAdapter

스피너는 데이터를 효율적으로 관리하고 표시하기 위해 어댑터를 사용한다.

스피너 어댑터의 주요 역할은 다음과 같다.

 

1. 데이터 표시

  • 어댑터는 데이터 셋에서 개별 데이터 항목을 가져와 스피너에 표시한다.
  • 이를 통해 동적인 데이터 변경에 유연하게 대응할 수 있다.

 

2. 다양한 데이터 소스 지원

  • 배열, DB, 구조체 등, 다양한 데이터 소스를 가져와 표시할 수 있다.
  • 위의 예시 코드처럼, ArrayAdapter를 사용하거나 CursorAdapter등을 사용하여 이를 쉽게 구현할 수 있다.

 

리스너 설정

binding.mySpinner.onItemSelectedListener =
      object : AdapterView.OnItemSelectedListener {
          override fun onItemSelected(
              parent: AdapterView<*>?,
              view: View?,
              position: Int,
              id: Long
          ) {
              val selectedItem = parent?.getItemAtPosition(position) as Gender
              binding.textGender.text = selectedItem.sex
          }

          override fun onNothingSelected(parent: AdapterView<*>?) {
              // empty here
          }
      }

 

스피너 위젯의 아이템 선택 리스너를 설정하는 코드다.

하나씩 살펴보겠다.

 

  • onItemSelectedListener
    • 스피너에서 아이템이 선택될 때 호출되는 리스너를 설정한다.

 

  • object : AdapterView.OnItemSelectedListener
    • AdapterView.OnItemSelectedListene 인터페이스를 구현하는 익명 객체를 생성한다.
    • 이 인터페이스는 아이템이 선택되거나 선택이 해제될 때 호출되는 콜백 메서드를 정의한다.

 

  • onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Log)
    • 스피너에서 사용자가 아이템을 선택하면 호출되는 메서드
    • parent: 선택된 아이템을 포함하는 AdapterView로, 여기서는 Spinner다.
    • view: 선택된 아이템의 뷰
    • position: 선택된 아이템의 위치(0부터 시작하는 인덱스)
    • id: 선택된 아이템의 ID(일반적으로 데이터의 행 ID를 의미한다)

 

  • val selectedItem = parent?.getItemAtPosition(position)
    • 선택된 아이템을 가져온다.
    • getItemAtPosition(position) 메서드는 주어진 위치의 아이템을 반환한다.
    • 본인은 as Gender를 통해 본인이 작성한 데이터 클래스인 Gender 타입으로 형 변환을 해줬다.

 

  • onNothingSelected(parent: AdapterView<*>?)
    • 선택이 초기화되었 때 호출되는 메서드
    • 그런데 Spinner에서 사용자가 직접 현재 선택을 초기화하는 UI 옵션이 되지는 않음
    • 때문에 사실상 아무런 동작을 하지 않는 코드이다.
    • 그럼에도 불구하고, onNothingSelected 메서드는AdapterView.OnItemSelectedListener 인터페이스의 일부로 정의되어 있기 때문에, 해당 인터페이스를 구현할 때는 이 메서드를 오버라이드해야 한다.
    • 보통 비워둔다.

 

기회가 된다면 Compose로 Spinner를 만드는 방법에 대한 글도 작성하겠다.

그거 만들어보겠다고 반나절 날렸던게 벌써 5개월 전의 일이라니..

 

최종 수정: 24.05.18