Pa K'ode

[안드로이드] Spinner 기본 사용 방법과 Custom 방법에대해 알아보자 본문

안드로이드

[안드로이드] Spinner 기본 사용 방법과 Custom 방법에대해 알아보자

Paku 2022. 9. 1. 19:35

안녕하세요, 안드로이드 개발자 파쿠입니다 :)

오늘은 View의 한 종류인 Spinner에 대해 알아보려고 합니다.

보통 드랍다운 레이아웃이 필요한 경우에 많이 사용하게 되는데요, 

여기서 드랍다운 메뉴란 ↓ 이렇게 생긴 레이아웃 종류입니다!

 

아래 나와있는 분류대로 순차적으로 알아보도록 하겠습니다!

  • 스피너 커스텀 하기
  • 스피너에 데이터 추가하기
  • 스피너에 가이드 문구 추가하기
1) 스피너 커스텀 하기

 

우선,스피너의 배경이 될 drawable 파일을 하나 생성해줍니다.

 

bg_spinner.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true">
        <layer-list>
            <item>
                <shape>
                    <solid android:color="@color/white"/>
                    <stroke android:width="1dp" android:color="@color/light_blue_grey" />
                    <corners android:radius="15dp" />
                    <padding android:bottom="15dp" android:left="10dp" android:right="10dp" android:top="15dp" />
                </shape>
            </item>
            <item android:right="5dp">
                <bitmap android:gravity="center_vertical|right" android:src="@drawable/ic_arrow_down_yellow" />
            </item>
        </layer-list>
    </item>

    <item android:state_enabled="false">
        <layer-list>
            <item>
                <shape>
                    <solid android:color="@color/pale_grey"/>
                    <stroke android:width="1dp" android:color="@color/light_blue_grey" />
                    <corners android:radius="15dp" />
                    <padding android:bottom="15dp" android:left="10dp" android:right="10dp" android:top="15dp" />
                </shape>
            </item>
            <item android:right="5dp">
                <bitmap android:gravity="center_vertical|right" android:src="@drawable/ic_arrow_down" />
            </item>
        </layer-list>
    </item>
</selector>
예시로 사용된 코드는 상단에 기제된 gif내 스피너의 배경입니다. 참고하셔서, 상황에 맞게 사용하시면 됩니다.

 

이렇게 배경이될 drawable 파일을 생성하셨다면, 다음으로 메뉴에 추가될 아이템을 만들어줍니다.

 

item_spinner.xml

<TextView
	id="@+id/tv_title"
    style="@style/Pretend_Medium"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:lineSpacingExtra="6sp"
    android:paddingHorizontal="20dp"
    android:paddingVertical="16dp"
    android:textColor="@color/dark_grey"
    android:textSize="15sp"
    android:textStyle="normal" />
스피너에서 선택된 아이템의 뷰 입니다.

 

item_spinner_dropdown.xml

<TextView
	id="@+id/tv_title"
    style="@style/Pretend_Medium"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:lineSpacingExtra="6sp"
    android:paddingHorizontal="20dp"
    android:paddingVertical="16dp"
    android:textColor="@color/dark_grey"
    android:textSize="15sp"
    android:textStyle="normal" />
스피너에서 펼쳐진 아이템의 뷰 입니다.

 

스피너 배경과 메뉴 아이템까지 작성하셨다면, 범용으로 사용할수 있게끔 테마를 추가해줍니다.

 

themes.xml

<style name="Widget.My.Spinner" parent="Widget.AppCompat.Spinner">
    <item name="android:overlapAnchor">false</item>
    <item name="overlapAnchor"> false</item>
    <item name="android:spinnerMode">dropdown</item>
    <item name="android:dropDownWidth">match_parent</item>
    <item name="android:background">@drawable/bg_spinner</item>
</style>
  • overlapAnchor
    • true -> 드랍다운 메뉴의 표시위치가 스피너와 겹쳐질수 있습니다.
    • false -> 드랍다운 메뉴가 스피너 아래로 고정됩니다.
  • spinnerMode 
    • dropdown -> 메뉴가 드랍다운 형식으로 표시됩니다.
    • dialog -> 메뉴가 팝업형태로 표시됩니다.
  • dropDownWidth 
    • 드랍다운 메뉴의 가로길이를 설정합니다.

 

스타일까지 작성되었다면, 스피너에 적용해줍니다.

fragment.xml

<Spinner
    style="@style/Widget.My.Spinner"
    list="@{output.categoryList}"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:padding="0dp" />

저는 databinding을 이용해 리스트를 바로 추가해 주었습니다.

 

주의할점으론, 예시의 bg_spinner 내부에 padding 값이 포함되었기때문에 spinner에 padding="0dp" 값을 추가해줍니다.

 

2) 스피너에 데이터 추가하기

스피너에 제공하는 선택 항목은 어떠한 소스에서든 가져올 수 있지만, SpinnerAdapter를 통해 제공되어야 합니다. 예를 들어 선택 항목을 배열에서 사용할 수 있는 경우에는 ArrayAdapter, 선택 항목을 데이터베이스 쿼리에서 사용할 수 있는 경우에는 CursorAdapter를 통해 제공합니다.

출처: https://developer.android.com/guide/topics/ui/controls/spinner?hl=ko 

 

스피너  |  Android 개발자  |  Android Developers

스피너 스피너는 값 집합에서 하나의 값을 선택할 수 있는 빠른 방법을 제공합니다. 기본 상태의 스피너는 현재 선택된 값을 표시합니다. 스피너를 터치하면 기타 모든 사용 가능한 값을 포함하

developer.android.com

 

보통의 상황에서는 ArrayAdapter를 통해 스피너에 들어갈 데이터를 전달해 줍니다.

ArrayAdapter 앞 <> 태그로 들어갈 데이터의 변수형을 명명해주는것이 좋습니다.

 

val adapter =  ArrayAdapter<*>(requireContext(), R.layout.item_spinner, R.id.tv_title , listOf<*>())
adapter.setDropDownViewResource(R.layout.item_spinner_dropdown)
(Spinner).adapter = adapter

앞에서 만들어준 item_spinner와 item_spinner_dropdown resource를 연동해 줍니다.

주의하실 점으로는 item_spinner의 textview에 지정해준 id값도 옆에 추가해 줘야합니다

 

결과 값을 받아서 처리해야 한다면 onItemSelectedListener를 사용하여 callback을 받을수 있습니다

 

(Spinner).onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
	override fun onNothingSelected(parent: AdapterView<*>?) {}
    	override fun onItemSelected(
    	parent: AdapterView<*>?,
        	view: View?,
        	position: Int,
        	id: Long
    	) {
      	// 결과 값의 처리
    	}
}

 

3) 스피너에 가이드 문구 추가하기

기본적으로 스피너에는 예시에서 본것처럼의 가이드 문구에 대한 처리를 제공하지 않습니다.

그래서 일부 코드를 추가하여 사용해야 하는데, 제가 알고있는 방식으로는 두가지가 있습니다.

 

  1. 데이터 바인딩을 사용,  특정 포지션의 콜백을 비활성화 하여 바인딩 연산자를 이용하는 방법
  2. CustomArrayAdapter에 ArrayAdapter를 상속받아, 해당 포지션의 뷰를 수정하는 방법.
    -> 1번 방식보다 간편하고, DropdownView에 View를 제거해 깔끔한 UI를 보여줄수 있습니다. 
    해당 포스트 에선 2번 방식에 대해 알아보겠습니다.

BaseArrayAdapter.class

class BaseArrayAdapter<T>(
    context: Context
) : ArrayAdapter<T>(context, R.layout.item_spinner, R.id.tv_title, arrayListOf()) {

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view = super.getDropDownView(position, convertView, parent)
         if (position == 0) {
         	(view.findViewById<TextView>(R.id.tv_title)).apply { 
                width = 0
                height = 0
            }
         }
        return view
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view = super.getView(position, convertView, parent)
        if(position == 0) {
            view.findViewById<TextView>(R.id.tv_title).setTextColor(context.getColor(R.color.key_color))
        }
        return view
    }
    
    fun submitList(list: List<T>?) {
        this.clear()
        this.addAll(list)
        this.notifyDataSetChanged()
    }
}

 

DropdownView -> 아이템을 리턴할떄, 특정 뷰의 포지션을 찾아 보이지 않게 처리하였습니다.

View  -> 특정 뷰의 포지션을 찾아 글자색을 변경해줌으로써 hint 효과를 주었습니다.

 

기본적으로 Spinner의 경우 항상 0번쨰 포지션의 View를 보여주기떄문에, position == 0 의 조건을 걸었습니다.

 

Fragment.class

        val adapter = BaseArrayAdapter(requireContext())
        adapter.setDropDownViewResource(R.layout.item_spinner_dropdown)
        (Spinner).adapter = adapter
        adapter.submitList(listOf("가이드 문구가 0번쨰"))
        (Spinner).onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                if (position == 0) return 
                // 선택되었을떄의 처리
            }

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

 

2번에서 연결한 ArrayAdapter 코드처럼 사용해주시면 됩니다.

주의하실 점으로는 가이드가 될 text는 항상 리스트에 첫번쨰에 위치해야 합니다.

 

 

 

Comments