안녕하세요.
이번 포스팅에서는 리사이클러뷰의 아이템의 클릭 이벤트를 처리하는 기능을 구현해 보겠습니다.
언어: 코틀린
sdk vsersion
- compile: 33
- min: 21
- target: 33
가장 먼저 예제에 쓰일 아이템을 만들겠습니다.
RecyclerViewItemProvider.kt
object RecyclerViewItemProvider {
fun getItems(): List<Animal> {
return listOf(
Animal("포유류", "고양이"),
Animal("포유류", "강아지"),
Animal("포유류", "토끼"),
Animal("포유류", "미어캣"),
Animal("조류", "팽귄"),
Animal("조류", "참새"),
Animal("어류", "잉어"),
Animal("파충류", "악어"),
Animal("파충류", "도마뱀"),
Animal("어류", "금붕어"),
)
}
}
data class Animal(
val species: String,
val name: String
)
recycler view의 item view입니다.
view_animal_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="animal"
type="com.contents.laboratory.Animal" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="5dp">
<TextView
android:id="@+id/text_species"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{animal.species}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{animal.name}"
app:layout_constraintTop_toBottomOf="@id/text_species"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
데이터 바인딩을 사용해 text view를 표시하도록 합니다.
프래그먼트와 뷰모델을 생성합니다.
RecyclerViewClickViewModel.kt
@HiltViewModel
class RecyclerViewClickViewModel @Inject constructor() : ViewModel() {
private val _clickedItem = MutableLiveData<Animal>()
val clickedItem: LiveData<Animal> get() = _clickedItem
fun onItemClicked(item: Animal) {
_clickedItem.postValue(item)
}
}
RecyclerViewClickFragment.kt
@AndroidEntryPoint
class RecyclerViewClickFragment : Fragment() {
private var _binding: FragmentRecyclerViewClickBinding? = null
private val binding get() = _binding!!
private val viewModel: RecyclerViewClickViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentRecyclerViewClickBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
fragment_recycler_view_click.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.contents.laboratory.RecyclerViewClickViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RecyclerViewClickFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/animal_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="@layout/view_animal_item" />
<TextView
android:id="@+id/text_species"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.clickedItem.species}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.clickedItem.name}"
app:layout_constraintTop_toBottomOf="@id/text_species"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
데이터 바인딩을 사용해 클릭한 아이템이 ui에 표시되도록 작성했습니다.
어답터를 작성하겠습니다.
AnimalAdapter.kt
class AnimalAdapter : ListAdapter<Animal, AnimalAdapter.AnimalViewHolder?>(
AnimalDataCallback
) {
class AnimalViewHolder(
private val binding: ViewAnimalItemBinding,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Animal) {
binding.animal = item
}
}
companion object {
private val AnimalDataCallback = object : DiffUtil.ItemCallback<Animal>() {
override fun areItemsTheSame(
oldItem: Animal,
newItem: Animal
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
override fun areContentsTheSame(
oldItem: Animal,
newItem: Animal
): Boolean {
return oldItem == newItem
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimalViewHolder {
val binding =
ViewAnimalItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return AnimalViewHolder(binding)
}
override fun onBindViewHolder(holder: AnimalViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
여기에 click listener를 추가하겠습니다.
Adapter 파일 내에 ClickEventListener class를 추가하며, click event 발생 시 animal을 인자로 넘기도록 합니다.
AnimalAdapter.kt
class AnimalClickListener(val clickListener: (animal: Animal) -> Unit) {
fun onClick(animal: Animal) = clickListener(animal)
}
adapater 전체 소스입니다.
AnimalAdapter.kt
class AnimalAdapter(
private val animalClickListener: AnimalClickListener
) : ListAdapter<Animal, AnimalAdapter.AnimalViewHolder?>(
AnimalDataCallback
) {
class AnimalViewHolder(
private val binding: ViewAnimalItemBinding,
private val animalClickListener: AnimalClickListener
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Animal) {
binding.animal = item
binding.clickListener = animalClickListener
}
}
companion object {
private val AnimalDataCallback = object : DiffUtil.ItemCallback<Animal>() {
override fun areItemsTheSame(
oldItem: Animal,
newItem: Animal
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
override fun areContentsTheSame(
oldItem: Animal,
newItem: Animal
): Boolean {
return oldItem == newItem
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimalViewHolder {
val binding =
ViewAnimalItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return AnimalViewHolder(binding, animalClickListener)
}
override fun onBindViewHolder(holder: AnimalViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class AnimalClickListener(val clickListener: (animal: Animal) -> Unit) {
fun onClick(animal: Animal) = clickListener(animal)
}
에러가 발생할 아래의 코드는 이후에 item xml을 작업해 주면 해결됩니다.
binding.clickListener = animalClickListener
item view에 clickListener를 추가하고, onClick을 설정해 줍니다.
view_animal_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="animal"
type="com.contents.laboratory.Animal" />
<variable
name="clickListener"
type="com.contents.laboratory.AnimalClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="5dp"
android:onClick="@{() -> clickListener.onClick(animal)}">
<TextView
android:id="@+id/text_species"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{animal.species}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{animal.name}"
app:layout_constraintTop_toBottomOf="@id/text_species"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
클릭 이벤트를 발생시킬 준비가 끝났습니다.
이제 프래그먼트에 어답터를 추가하겠습니다.
RecyclerViewClickFragment.kt
@AndroidEntryPoint
class RecyclerViewClickFragment : Fragment() {
private var _binding: FragmentRecyclerViewClickBinding? = null
private val binding get() = _binding!!
private val viewModel: RecyclerViewClickViewModel by viewModels()
private val animalAdapter by lazy {
AnimalAdapter(AnimalClickListener {
viewModel.onItemClicked(it)
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentRecyclerViewClickBinding.inflate(layoutInflater, container, false).apply {
viewModel = this@RecyclerViewClickFragment.viewModel
lifecycleOwner = viewLifecycleOwner
}
binding.animalRecyclerView.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
adapter = animalAdapter
}
animalAdapter.submitList(RecyclerViewItemProvider.getItems().toMutableList())
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
아이템 클릭 시 뷰모델의 함수를 호출하며 아이템을 인자로 넘기고, 뷰모델에서 해당 아이템을 post합니다.
에뮬레이터에서 실행해 보겠습니다.
정상적으로 동작합니다.
이상 포스팅을 마치겠습니다.
감사합니다.
'Android Application > 응용 구현' 카테고리의 다른 글
안드로이드의 다양한 데이터 바인딩(editText, Progress bar...) (0) | 2023.04.02 |
---|---|
안드로이드 회차별 로또 정보 받아오기(retrofit2 + MVVM) (0) | 2023.04.02 |
Android Studio 연습용 어플 구현2 (사용자 입력2) (0) | 2020.05.26 |
Android Studio 연습용 어플 구현1 (사용자 입력) (0) | 2020.05.25 |
Android Studio 연습용 어플 구현 (Activity 전환) (0) | 2020.05.22 |
댓글