일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 안드로이드 유닛 테스트 예시
- 안드로이드 라이선스
- 서비스 쓰레드 차이
- 서비스 vs 쓰레드
- android ar 개발
- 안드로이드 레트로핏 사용법
- 객체
- 클래스
- 안드로이드 라이선스 종류
- Rxjava Observable
- jvm이란
- 스택 큐 차이
- 2022 플러터 설치
- 안드로이드 유닛테스트란
- 안드로이드 레트로핏 crud
- 안드로이드 os 구조
- jvm 작동 원리
- 큐 자바 코드
- ar vr 차이
- 2022 플러터 안드로이드 스튜디오
- rxjava disposable
- rxjava cold observable
- 플러터 설치 2022
- 자바 다형성
- ANR이란
- android retrofit login
- rxjava hot observable
- 멤버변수
- 안드로이드 유닛 테스트
- 스택 자바 코드
- Today
- Total
나만을 위한 블로그
[Android] 스피너 커스텀하는 방법 본문
한 화면에서 여러 데이터 중 하나를 선택할 수 있게 하는 방법 중 하나가 스피너다.
이 스피너를 내가 원하는 모양대로 만들고 싶을 때가 있는데 이 방법이 조금 귀찮아서 기록해두려고 한다.
먼저 xml 2개를 만든다. 화면에서 보이는 스피너 안의 텍스트뷰, 스피너를 눌렀을 때 안의 데이터들을 보여줄 텍스트뷰 2개다. 각 파일명은 소스코드 최상단에 써놨다.
이미지뷰 안의 arrow_down은 대충 이미지 주워와서 끼워 넣거나 mipmap 안의 런처 아이콘으로 해도 상관없다.
<!-- spinner_outer_view.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1"
android:orientation="horizontal">
<!-- 액티비티에서 사용자에게 보여지는 스피너 안의 텍스트뷰. 선택된 상태로 취급받음 -->
<TextView
android:id="@+id/spinner_inner_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".85"
android:gravity="center"
android:paddingStart="13dp"
android:textColor="#000000"
android:textSize="15sp" />
<ImageView
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_weight=".15"
android:layout_marginStart="5dp"
android:src="@drawable/arrow_down" />
</LinearLayout>
<!-- spinner_inner_view.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<!-- 클릭 시 나타나는 스피너 안의 텍스트뷰들 -->
<TextView
android:id="@+id/spinner_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp" />
</LinearLayout>
이제 어댑터를 만든다. CustomSpinnerAdapter라는 클래스를 만들고 아래 소스코드를 복붙한다.
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
public class CustomSpinnerAdapter extends BaseAdapter {
private final List<String> list;
private final LayoutInflater inflater;
private String text;
public CustomSpinnerAdapter(Context context, List<String> list) {
this.list = list;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
if (list != null)
return list.size();
else
return 0;
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
// 화면에 들어왔을 때 보여지는 텍스트뷰 설정
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = inflater.inflate(R.layout.spinner_outer_view, parent, false);
if (list != null) {
text = list.get(position);
((TextView) convertView.findViewById(R.id.spinner_inner_text)).setText(text);
}
return convertView;
}
// 클릭 후 나타나는 텍스트뷰 설정
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = inflater.inflate(R.layout.spinner_inner_view, parent, false);
if (list != null) {
text = list.get(position);
((TextView) convertView.findViewById(R.id.spinner_text)).setText(text);
}
return convertView;
}
// 스피너에서 선택된 아이템을 액티비티에서 꺼내오는 메서드
public String getItem() {
return text;
}
}
맨 마지막의 getItem()은 무조건 구현할 필요 없는 선택사항이다. 저 방법이 아니라도 스피너에서 선택된 아이템을 가져올 방법은 있다.
마지막으로 메인 액티비티의 XML과 자바 파일이다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".TestActivity">
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class TestActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
private List<String> list = new ArrayList<>();
private Spinner spinner;
private CustomSpinnerAdapter adapter;
private String selectedItem;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
spinner = findViewById(R.id.spinner);
// 스피너 안에 넣을 데이터 임의 생성
for (int i = 1; i < 20; i++) {
list.add("아이템" + i);
}
// 스피너에 붙일 어댑터 초기화
adapter = new CustomSpinnerAdapter(this, list);
spinner.setAdapter(adapter);
// 스피너 클릭 리스너
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// 어댑터에서 정의한 메서드를 통해 스피너에서 선택한 아이템의 이름을 받아온다
selectedItem = adapter.getItem();
Toast.makeText(TestActivity.this, "선택한 아이템 : " + selectedItem, Toast.LENGTH_SHORT).show();
// 어댑터에서 정의하는 게 귀찮다면 아래처럼 구할 수도 있다
// getItemAtPosition()의 리턴형은 Object이므로 String 캐스팅이 필요하다
String otherItem = (String) spinner.getItemAtPosition(position);
Log.e(TAG, "getItemAtPosition() - 선택한 아이템 : " + otherItem);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
//
}
});
}
}
이러고 실행하면 아래처럼 동작한다. 로그도 출력되니 에뮬레이터와 로그캣을 같이 보면 된다.
잘 동작한다. 스피너 오른쪽에 화살표가 2개인 것만 빼고 말이다.
이제 저 기본 화살표를 없애고 내가 넣은 아이콘을 스피너 우측에 보여줄 것이다. 그러기 위해 res/drawable 폴더에 XML 하나를 만들어준다.
<!-- spinner_border_layout.xml -->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
<stroke
android:width="1dp"
android:color="#000"/>
</shape>
그리고 메인 액티비티의 XML에서 스피너의 속성으로 background 값을 추가한다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".TestActivity">
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/spinner_border_layout"/>
</LinearLayout>
이대로 빌드하면 끝인가? 아니다. 이 상태로 빌드하면 화면에서 스피너가 안 이쁜 모습으로 보인다.
글자 위아래로 패딩이 좀 있으면 볼 만해질 것 같다. 그럼 어떤 파일의 어디를 고치면 좋을까?
스피너를 클릭하지 않은 상태에서 보이는 뷰를 고쳐야 하니까 spinner_outer_view 파일을 고치면 될 것이다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1"
android:padding="16dp"
android:orientation="horizontal">
<!-- 액티비티에서 사용자에게 보여지는 스피너 안의 텍스트뷰. 선택된 상태로 취급받음 -->
<TextView
android:id="@+id/spinner_inner_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".85"
android:gravity="center"
android:paddingStart="13dp"
android:textColor="#000000"
android:textSize="15sp" />
<ImageView
android:layout_width="0dp"
android:layout_height="25dp"
android:layout_weight=".15"
android:layout_marginStart="5dp"
android:src="@drawable/arrow_down" />
</LinearLayout>
부모 레이아웃에서 전체 패딩을 16dp만큼 먹였다. 이렇게 하면 아래처럼 보인다.
이제 이 예제를 바탕으로 본인의 입맛에 맞게 모양, 안에 들어갈 데이터를 바꿔서 적절하게 사용하면 된다.
주의할 것은 내가 임의로 커스텀한 드로어블을 스피너에 적용한 것이기 때문에 스피너를 클릭해도 화살표 주변에서 리플 효과(클릭 시 회색 물결 같은 애니메이션이 발생하는 것)가 발생하지 않는다. 이 현상의 수정 방법까지는 찾아보지 않아서 모른다.
참고한 사이트)
https://onedaycodeing.tistory.com/66
'Android' 카테고리의 다른 글
[Android] Material CalendarView 커스텀 사용법 정리 (0) | 2022.01.23 |
---|---|
[Android] TextWatcher란? (0) | 2022.01.20 |
[Android] 화면 터치 여부를 확인하는 법 (dispatchTouchEvent) (0) | 2021.12.09 |
[Android] 밑에서 조금 떠 있는 BottomNavigationView 만들기 (0) | 2021.12.05 |
[Android] MVVM + Rxjava + Retrofit + RecyclerView 같이 사용하기 (0) | 2021.11.29 |