HTML의
<select>태그처럼 목록에서 원하는 값을 선택할 수 있는 기능을 제공하기 위해 구현된 클래스입니다.


주요 요구 사항:
디자인 구조는 이전의 CustomInput과 유사하므로, 이번에는 옵션 목록 노출 구현을 중심으로 설명하겠습니다.
선택 가능한 옵션 목록을 처리하기 위해 HTML의
<option>태그처럼SelectItem데이터 클래스를 만들어 관리합니다.
data class SelectItem(
val label: String,
val value: String,
var checked: Boolean = false
)
CustomSelect는 AppCompatEditText를 상속받아, 옵션 선택 기능을 제공하고, 이를 관리하기 위해 options 배열을 사용합니다.
class CustomSelect @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatEditText(context, attrs, defStyleAttr) {
private var options: Array<SelectItem> = arrayOf()
private var value: String = ""
// 옵션을 설정하는 메서드
fun setOptions(options: Array<SelectItem>) {
this.options = options
options.find { it.checked }?.label.let {
setText(it)
}
}
// 선택된 옵션의 값에 접근하기 위한 메서드
fun getValue(): String? {
return value
}
}
옵션 목록을 설정하고, 기본 선택된 값을 화면에 표시하기 위해
setOptions()메서드를 사용합니다.
binding.selectPetSize.setOptions(
arrayOf(
SelectItem("소형", "Small"),
SelectItem("중형", "Medium"),
SelectItem("대형", "Large"),
)
)
이 메서드를 통해 선택 가능한 옵션을 CustomSelect에 전달하고, 선택된 옵션을 화면에 노출시킵니다.
선택된 옵션을 사용해야 하는 상황을 처리하기 위해 getValue() 메서드를 추가하여 선택된 옵션의 값에 접근할 수 있도록 합니다.
value 변수에는 사용자가 선택한 옵션의 실제 값이 저장되며, 이 값을 필요할 때 가져올 수 있어야 합니다. getValue() 메서드는 이를 간편하게 접근할 수 있도록 도와줍니다.
// 선택된 옵션의 값에 접근하기 위한 메서드
fun getValue(): String? {
return value
}
이 메서드를 통해 선택된 옵션의 값을 아래와 같이 다른 작업이나 프로세스에서 활용할 수 있습니다.
val value = binding.select.getValue()
옵션을 선택할 수 있는 UI를 제공하기 위해,
showOptions()메서드를 사용해 Bottom Sheet 다이얼로그를 띄웁니다.
var type: String = "list"
fun showOptions() {
val bottomSheetView =
LayoutInflater.from(context).inflate(R.layout.custom_bottom_sheet_container, null)
val contentFrame: FrameLayout = bottomSheetView.findViewById(R.id.content_frame)
// type에 따라 다른 UI 구성
when (type) {
"list" -> inflateListOptionsView(contentFrame)
"gender" -> inflateGenderOptionsView(contentFrame)
else -> {
text = null
value = ""
}
}
setupBottomSheetDialog(bottomSheetView)
}
type 값에 따라 다른 옵션 UI를 띄우며, 이번에는 옵션 리스트를 보여주는 inflateListOptionsView()를 중점적으로 설명하겠습니다.
옵션 리스트를 화면에 표시하기 위해,
inflateListOptionsView()를 통해 리스트 UI를 생성합니다.
private fun inflateListOptionsView(contentFrame: FrameLayout) {
// 1. 리스트를 담을 커스텀 뷰를 인플레이트하고 추가
val customView = LayoutInflater.from(context).inflate(R.layout.custom_select_list, null)
contentFrame.addView(customView)
// 2. RecyclerView 설정
val recyclerView: RecyclerView = customView.findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(context)
// 3. 어댑터 설정
recyclerView.adapter = OptionAdapter(options) { selectedOption ->
// 선택된 옵션에 따라 checked 상태와 텍스트, 값 업데이트
options.forEach { it.checked = it == selectedOption }
setText(selectedOption.label)
value = selectedOption.value
hidePopup()
}
}
FrameLayout에 추가합니다.옵션 목록을 리스트 형태로 표시하고, 선택된 옵션을 처리하는 어댑터입니다.
private inner class OptionAdapter(
private val options: Array<SelectItem>,
private val itemClickListener: (SelectItem) -> Unit
) : RecyclerView.Adapter<OptionAdapter.OptionViewHolder>()
OptionAdapter는 options 배열과 선택 리스너를 받아, 옵션을 선택하면 해당 옵션의 상태를 업데이트합니다. 이를 통해 사용자가 선택한 값이 반영되도록 합니다.
Bottom Sheet 다이얼로그를 초기화하고 설정된 UI를 화면에 띄우는 메서드입니다.
private fun setupBottomSheetDialog(bottomSheetView: View) {
bottomSheetDialog = BottomSheetDialog(context).apply {
setContentView(bottomSheetView)
setOnShowListener {
val parentLayout = bottomSheetView.parent as ViewGroup
parentLayout.setBackgroundResource(android.R.color.transparent)
}
setOnCancelListener {
setUnderlineColor(R.color.disable)
}
}
setupBottomSheetBehavior(bottomSheetView)
bottomSheetDialog.show()
}
showOptions() 메서드에서 인플레이트한 bottomSheetView를 다이얼로그의 콘텐츠로 설정하고, 화면에 노출합니다. 이를 통해 사용자가 옵션을 선택할 수 있는 인터페이스가 제공됩니다.
SelectItem 데이터 클래스와 CustomSelect로 옵션 목록과 선택 상태를 관리합니다.CustomSelect에 반영됩니다.