정상에서 IT를 외치다

[Android, Spinner] 스피너 기본 사용법부터 커스텀 까지! 본문

안드로이드

[Android, Spinner] 스피너 기본 사용법부터 커스텀 까지!

Black-Jin 2019. 7. 15. 10:30
반응형

안녕하세요. 블랙진입니다.

오늘은 안드로이드 위젯 중 기본이 되는 스피너 사용법에 대해 살펴보겠습니다.



1. 스피너란?


여러개의 값 중 1개를 선택하기 위해 사용하는 안드로이드 에서 제공해주는 기본 위젯 입니다.





2. 구현해보자


1) xml 그리기

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

<Spinner
android:id="@+id/spinner"
android:layout_marginTop="100dp"
android:layout_centerHorizontal="true"
android:layout_width="300dp"
android:layout_height="wrap_content"/>

</RelativeLayout>


가로 300dp 크기의 Spinner을 생성해 주었습니다.


2) 배열을 선언하자


배열을 선언하여 가져오는 두가지 방법이 있습니다.


2-1) 코드로 아이템0 ~ 아이템4 까지 5개의 배열을 선언해줍니다.

val items = arrayOf("아이템0","아이템1","아이템2","아이템3","아이템4")


2-2) xml 을 이용한 방식


다음 방법은 res 폴더에서 배열을 생성하는 방법입니다. 저는 array.xml 이라는 폴더를 생성해 주었습니다



array.xml 안에 my_array 이름으로 배열을 만들었습니다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="my_array">
<item>아이템0</item>
<item>아이템1</item>
<item>아이템2</item>
<item>아이템3</item>
<item>아이템4</item>
</string-array>
</resources>


Activity에서는 resources를 불러와 아래와 같이 사용해 주면 됩니다.

val items = resources.getStringArray(R.array.my_array)


스피너는 고정된 리스트를 보여주는 것이기 때문에 xml 로 따로 관리해주는 것이 더욱 좋습니다.



3) 어댑터 연결 및 클릭 리스너 구현

//어댑터의 아이템은 안드로이드 스튜디오에서 제공해 주는 기본인
//android.R.layout.simple_spinner_dropdown_item 을 사용했습니다.
val myAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, items)

spinner.adapter = myAdapter

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

//아이템이 클릭 되면 맨 위부터 position 0번부터 순서대로 동작하게 됩니다.
when(position) {
0 -> {

}
1 -> {

}
//...
else -> {

}
}
}

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

}
}


이게 전부 입니다! 참 쉽죠? 




근데 앱을 실행해보면 초기화 될 때 "아이템0"이 찍히는 걸 볼 수 있습니다. 최초 초기화 시 0번째 배열에 있는 아이템이 위치하게 되는데요. 초기 값을 다르게 하고 싶다면 setSelection()을 호출하세요


spinner.setSelection(2) // 2번째 포지션으로 이동합니다.



3. 커스텀 마이징 해보자


실제 앱애서는 위젯을 그대로 쓰는 개발자는 없잖아요? 이쁘게 커스텀 마이징 해보겠습니다.


1) 배경을 만들어 보자


spinner_background.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<!-- #f5f5f5" 색으로 채움 -->
<solid
android:color="#f5f5f5"/>

<!-- 1dp 크기의 #000000 색 테두리 -->
<stroke
android:width="1dp"
android:color="#000000"/>

</shape>


스피너의 Background에 추가해 줍니다.

<Spinner
android:id="@+id/spinner"
android:background="@drawable/spinner_background"
android:layout_marginTop="100dp"
android:layout_centerHorizontal="true"
android:layout_width="300dp"
android:layout_height="wrap_content"/>




뭔가 느낌있게 변화했죠?



2) divider 를 넣어보자


divider 는 두가지 방법으로 넣을 수 있습니다.


2-1) 첫번째 방법으로는 styles 를 지정해 줍니다.


values - styles.xml

<style name="SpinnerDivideStyle" parent="android:style/Widget.ListView.DropDown">
<item name="android:divider">#000000</item> <!-- divider 색 -->
<item name="android:dividerHeight">2dp</item> <!-- divider 높이 -->
</style>

<style name="SpinnerTheme" parent="AppTheme">
<item name="android:dropDownListViewStyle">@style/SpinnerDivideStyle</item>
</style>


spinner에 theme를 설정해 줍니다.

<Spinner
android:id="@+id/spinner"
android:background="@drawable/spinner_background"
android:layout_marginTop="100dp"
android:layout_centerHorizontal="true"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:theme="@style/SpinnerTheme"/>


결과




2-2) 두번째 방법으로는 item xml 을 직접 만들어서 사용합니다.


item_spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tvItemSpinner"
android:layout_width="match_parent"
android:layout_height="45dp"
android:paddingStart="15dp"
android:textColor="@android:color/black"
android:textSize="15sp" />


activity

val myAapter = ArrayAdapter(this, R.layout.item_spinner, items)


ArrayAdapter을 초기화 해줄 때 직접 만든 xml을 추가해 줍니다. 이때 Divider가 있는 배경을 TextView에 넣어주면 되겠죠?


주의) xml을 직접 만들 때는 TextView 1개만 있는 xml 이어야 합니다.




4. 스피너에 제목(힌트)을 넣어 보자


스피너는 2가지 모드를 가지고 있습니다. 각 모드 마다 구현하는 방식이 다릅니다.



1) dropdown 모드


위에서 진행한 1~3 예제는 dropdown 모드이며 default 값입니다.


2) dialog 모드


이미지를 먼저 볼까요?




보시는 바와 같이 Dialog 를 통해 스피너를 선택합니다. 코드는 xml에서 spinnerMode 를 dialog 로 추가해 주면 됩니다.


<Spinner
android:id="@+id/spinner"
android:background="@drawable/spinner_background"
android:layout_marginTop="100dp"
android:spinnerMode="dialog"
android:layout_centerHorizontal="true"
android:layout_width="300dp"
android:layout_height="wrap_content"/>


다이어로그  모드 일때는 아래 코드를 통해 제목을 쉽게 추가할 수 있습니다.

spinner.prompt = "제목입니다."


아래 이미지와 같이 적용되며 dropdown 모드일 때는 적용되지 않습니다.





dropdown 모드일 때 제목을 추가해 보겠습니다.


1) Spinner 의 높이를 지정해줍니다. 


Spinner의 높이를 지정 않해주니 Background 적용이 안됩니다....

<Spinner
android:id="@+id/spinner"
android:background="@drawable/spinner_background"
android:layout_centerHorizontal="true"
android:layout_marginTop="100dp"
android:spinnerMode="dropdown"
android:layout_width="300dp"
android:layout_height="45dp"/>

height 값을 아이템의 크기에 맞춰 45dp 주었습니다.


2) Spinner 코드를 작성해 줍니다. 설명은 주석으로 대체하겠습니다.

val items = resources.getStringArray(R.array.my_array)

val myAapter = object : ArrayAdapter<String>(this, R.layout.item_spinner) {

override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {

val v = super.getView(position, convertView, parent)

if (position == count) {
//마지막 포지션의 textView 를 힌트 용으로 사용합니다.
(v.findViewById<View>(R.id.tvItemSpinner) as TextView).text = ""
//아이템의 마지막 값을 불러와 hint로 추가해 줍니다.
(v.findViewById<View>(R.id.tvItemSpinner) as TextView).hint = getItem(count)
}

return v
}

override fun getCount(): Int {
//마지막 아이템은 힌트용으로만 사용하기 때문에 getCount에 1을 빼줍니다.
return super.getCount() - 1
}

}


//아이템을 추가해 줍니다.
myAapter.addAll(items.toMutableList())

//힌트로 사용할 문구를 마지막 아이템에 추가해 줍니다.
myAapter.add("제목입니다.")

//어댑터를 연결해줍니다.
spinner.adapter = myAapter

//스피너 초기값을 마지막 아이템으로 설정해 줍니다. (마지막 아이템이 힌트 이기 때문이죠)
spinner.setSelection(myAapter.count)

//droplist를 spinner와 간격을 두고 나오게 해줍니다.)
//아이템 크기가 45dp 이므로 45dp 간격을 주었습니다.
//이때 dp 를 px 로 변환해 주는 작업이 필요합니다.
spinner.dropDownVerticalOffset = dipToPixels(45f).toInt()

//스피너 선택시 나오는 화면 입니다.
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {

//아이템이 클릭 되면 맨 위부터 position 0번부터 순서대로 동작하게 됩니다.
when (position) {
0 -> {

}
1 -> {

}
//...
else -> {

}
}
}

override fun onNothingSelected(parent: AdapterView<*>) {
Log.d("MyTag", "onNothingSelected")
}
}


추가) dp 값을 px 값으로 변환해 주는 함수

private fun dipToPixels(dipValue: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dipValue,
resources.displayMetrics
)
}


초기 스피너 화면 : 제목(힌트)가 노출됩니다.




스피너를 누르면 나오는 화면입니다.




<참고자료>

스피너 구글 문서

스피너 힌트

반응형
Comments