일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 안드로이드 유닛테스트란
- 서비스 쓰레드 차이
- 자바 다형성
- rxjava hot observable
- jvm이란
- 서비스 vs 쓰레드
- jvm 작동 원리
- 객체
- 안드로이드 레트로핏 crud
- 안드로이드 레트로핏 사용법
- 안드로이드 os 구조
- 스택 큐 차이
- 안드로이드 라이선스
- rxjava cold observable
- 플러터 설치 2022
- 안드로이드 라이선스 종류
- ar vr 차이
- 스택 자바 코드
- 안드로이드 유닛 테스트
- 2022 플러터 설치
- android ar 개발
- Rxjava Observable
- 멤버변수
- rxjava disposable
- 큐 자바 코드
- 안드로이드 유닛 테스트 예시
- 클래스
- 2022 플러터 안드로이드 스튜디오
- android retrofit login
- ANR이란
- Today
- Total
나만을 위한 블로그
[Android] 코틀린으로 리사이클러뷰 클릭 이벤트 구현하는 방법 정리 본문
리사이클러뷰 클릭 이벤트에 대해선 예전에 자바로 작성한 적이 있다.
https://onlyfor-me-blog.tistory.com/40
그러나 난 지금 자바가 아닌 코틀린으로 안드로이드 앱을 만들고 있고, 위 포스팅에 적힌 방법 외의 다른 방법들을 설명한 미디엄 포스팅을 발견해서 나중에 필요할 때 보려고 포스팅한다.
먼저 아래의 모델 클래스를 만들어둔다.
data class Car(
val brand: String,
val model: Int,
val isDiesel: Boolean
)
그리고 아이템 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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="model"
type="com.example.kotlinprac.recyclerview.clickevent.Car" />
</data>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardCornerRadius="5dp"
app:cardElevation="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{model.brand}"
tools:text="Jane" />
<TextView
android:id="@+id/tvAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{Integer.toString(model.model)}"
tools:text="26" />
<TextView
android:id="@+id/tvSwim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text='@{model.isDiesel ? "Yes" : "No"}'
tools:text="Yes" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
액티비티의 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"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".recyclerview.clickevent.RecyclerViewClickEventActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityRecyclerViewClickEventBinding
class RecyclerViewClickEventActivity : AppCompatActivity() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: ActivityRecyclerViewClickEventBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_click_event)
binding.run {
//
}
}
}
1. 어댑터의 onBindViewHolder()에서 클릭 리스너 설정
가장 기본적인 방법이다. 그냥 어댑터 안의 onBindViewHolder()에서 클릭 리스너를 붙일 뿐이다.
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprac.databinding.RowItemBinding
class CarAdapter(
private val carList: ArrayList<Car>,
): RecyclerView.Adapter<CarAdapter.CarViewHolder>() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: RowItemBinding
inner class CarViewHolder(
binding: RowItemBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: Car) {
binding.model = item
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CarViewHolder {
binding = RowItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CarViewHolder(binding)
}
override fun onBindViewHolder(holder: CarViewHolder, position: Int) {
val item = carList[position]
holder.bind(item)
binding.root.setOnClickListener {
val brand = item.brand
val model = item.model
val isDiesel = item.isDiesel
Log.e(TAG, "어댑터에서 brand : $brand, model : $model, isDiesel : $isDiesel")
}
}
override fun getItemCount(): Int = carList.size
}
액티비티에서 적용해준다.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityRecyclerViewClickEventBinding
class RecyclerViewClickEventActivity : AppCompatActivity() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: ActivityRecyclerViewClickEventBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_click_event)
binding.run {
val carList = arrayListOf(
Car(
brand = "브랜드1",
model = 0,
isDiesel = true
),
Car(
brand = "브랜드2",
model = 1,
isDiesel = false
),
Car(
brand = "브랜드3",
model = 2,
isDiesel = true
),
Car(
brand = "브랜드4",
model = 3,
isDiesel = false
),
Car(
brand = "브랜드5",
model = 4,
isDiesel = true
),
Car(
brand = "브랜드6",
model = 5,
isDiesel = false
),
)
val carAdapter = CarAdapter(carList)
recyclerView.apply {
layoutManager = LinearLayoutManager(this@RecyclerViewClickEventActivity)
setHasFixedSize(true)
adapter = carAdapter
}
}
}
}
이후 실행해서 아이템을 클릭하면 이런 형태로 로그가 찍힐 것이다.
간편하지만 액티비티 / 프래그먼트에서 클릭 이벤트를 구현해서 클릭한 아이템의 position을 알고 싶거나 한 경우에는 이 방식을 쓰면 후속 처리가 귀찮아진다. 어댑터 안에서 클릭 리스너를 처리해도 별 상관이 없는 경우에는 쓸만하지만 뷰모델의 API를 호출한다거나 할 때는 다른 방법을 쓰는 게 낫다.
2. 어댑터 생성자로 커스텀 람다 함수 넘기기
이 방법은 어댑터의 생성자에 리스트와 클릭 리스너를 넘긴다. 이 때 클릭 리스너는 Car 객체를 인자로 받아야 하며 리턴값이 필요없다면 Unit으로 설정한다.
어댑터 코드는 아래와 같다.
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprac.databinding.RowItemBinding
class CarAdapter(
private val carList: ArrayList<Car>,
private val clickListener: (Car) -> Unit
): RecyclerView.Adapter<CarAdapter.CarViewHolder>() {
private lateinit var binding: RowItemBinding
inner class CarViewHolder(
binding: RowItemBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: Car) {
binding.model = item
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CarViewHolder {
binding = RowItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CarViewHolder(binding)
}
override fun onBindViewHolder(holder: CarViewHolder, position: Int) {
val item = carList[position]
holder.bind(item)
binding.root.setOnClickListener {
clickListener(item)
}
}
override fun getItemCount(): Int = carList.size
}
그리고 이를 액티비티에서 적용한다.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityRecyclerViewClickEventBinding
class RecyclerViewClickEventActivity : AppCompatActivity() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: ActivityRecyclerViewClickEventBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_click_event)
binding.run {
val carList = arrayListOf(
Car(
brand = "브랜드1",
model = 0,
isDiesel = true
),
Car(
brand = "브랜드2",
model = 1,
isDiesel = false
),
Car(
brand = "브랜드3",
model = 2,
isDiesel = true
),
Car(
brand = "브랜드4",
model = 3,
isDiesel = false
),
Car(
brand = "브랜드5",
model = 4,
isDiesel = true
),
Car(
brand = "브랜드6",
model = 5,
isDiesel = false
),
)
val carAdapter = CarAdapter(carList) { car ->
val brand = car.brand
val model = car.model
val isDiesel = car.isDiesel
Log.e(TAG, "brand : $brand, model : $model, isDiesel : $isDiesel")
}
recyclerView.apply {
layoutManager = LinearLayoutManager(this@RecyclerViewClickEventActivity)
setHasFixedSize(true)
adapter = carAdapter
}
}
}
}
실행 후 화면에 나타난 아이템을 클릭하면 아래와 같이 로그가 출력될 것이다.
이렇게 어댑터를 구성할 경우 액티비티 / 프래그먼트에서 클릭 이벤트를 처리하고 싶은 경우 매우 간단하게 구현할 수 있는 장점이 있다. 리시버 이름도 it이 아니라 car 등 내가 원하는 이름을 지정할 수 있다.
3. 전역 람다 함수
이 방법은 2번 방법에서 확장된 방법이다. 2번 방법은 모든 어댑터, 아이템 타입에 대해 람다 함수를 작성해야 하지만 이 방법에선 제네릭 매개변수 T를 써서 새 인터페이스를 작성하는 것만 다르다.
먼저 인터페이스를 하나 만든다.
interface ItemClickListener<T> {
fun onItemClick(pos: Int, item: T)
}
그리고 어댑터 파일에서 생성자로 해당 인터페이스의 참조변수를 넘겨받도록 설정하고 onBindViewHolder()에서 클릭 리스너를 설정한다.
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprac.databinding.RowItemBinding
class CarAdapter(
private val carList: ArrayList<Car>,
private val clickListener: ItemClickListener<Car>
): RecyclerView.Adapter<CarAdapter.CarViewHolder>() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: RowItemBinding
inner class CarViewHolder(
binding: RowItemBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: Car) {
binding.model = item
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CarViewHolder {
binding = RowItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CarViewHolder(binding)
}
override fun onBindViewHolder(holder: CarViewHolder, position: Int) {
val item = carList[position]
holder.bind(item)
binding.root.setOnClickListener {
clickListener.onItemClick(position, item)
}
}
override fun getItemCount(): Int = carList.size
}
액티비티에서 구현은 아래처럼 한다. 참고로 인터페이스에서 만든 형식대로 object : ItemClickListener 오른쪽에 타입을 명시하지 않으면 컴파일 에러가 발생하기 때문에 꼭 넣어줘야 한다.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityRecyclerViewClickEventBinding
class RecyclerViewClickEventActivity : AppCompatActivity() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: ActivityRecyclerViewClickEventBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_click_event)
binding.run {
val carList = arrayListOf(
Car(
brand = "브랜드1",
model = 0,
isDiesel = true
),
Car(
brand = "브랜드2",
model = 1,
isDiesel = false
),
Car(
brand = "브랜드3",
model = 2,
isDiesel = true
),
Car(
brand = "브랜드4",
model = 3,
isDiesel = false
),
Car(
brand = "브랜드5",
model = 4,
isDiesel = true
),
Car(
brand = "브랜드6",
model = 5,
isDiesel = false
),
)
val carAdapter = CarAdapter(carList, object : ItemClickListener<Car> {
override fun onItemClick(pos: Int, item: Car) {
val brand = item.brand
val model = item.model
val isDiesel = item.isDiesel
Log.e(TAG, "brand : $brand, model : $model, isDiesel : $isDiesel")
}
})
recyclerView.apply {
layoutManager = LinearLayoutManager(this@RecyclerViewClickEventActivity)
setHasFixedSize(true)
adapter = carAdapter
}
}
}
}
실행하면 역시 잘 동작한다.
이 방법의 경우 동일한 매개변수가 필요한 클릭 이벤트 콜백이 필요할 때 인터페이스를 만들고 그 안에 함수를 정의해서, 만드는 어댑터마다 적용할 수 있어 불필요한 코드를 쓰지 않아도 된다는 장점이 있다. 다른 모델 클래스를 사용하더라도 그 이름만 바꿔서 넣어주면 되기 때문에 확장성도 가져갈 수 있다.
4. 커스텀 인터페이스
이 방법은 예전에 자바로 작성했던 포스트와 비슷한 방법이다. 어댑터 내부에서 인터페이스와 추상 함수를 정의한 다음 onBindViewHolder() 안의 클릭 이벤트에서 호출하고, 액티비티 / 프래그먼트에서 이 인터페이스를 구현하는 방법이다.
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprac.databinding.RowItemBinding
class CarAdapter(
private val carList: ArrayList<Car>,
private val clickListener: OnCarClickListener
): RecyclerView.Adapter<CarAdapter.CarViewHolder>() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: RowItemBinding
inner class CarViewHolder(
binding: RowItemBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: Car) {
binding.model = item
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CarViewHolder {
binding = RowItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CarViewHolder(binding)
}
override fun onBindViewHolder(holder: CarViewHolder, position: Int) {
val item = carList[position]
holder.bind(item)
binding.root.setOnClickListener {
clickListener.onCarClick(position, item)
}
}
override fun getItemCount(): Int = carList.size
interface OnCarClickListener {
fun onCarClick(position: Int, car: Car)
}
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityRecyclerViewClickEventBinding
class RecyclerViewClickEventActivity : AppCompatActivity() {
private val TAG = this.javaClass.simpleName
private lateinit var binding: ActivityRecyclerViewClickEventBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_view_click_event)
binding.run {
val carList = arrayListOf(
Car(
brand = "브랜드1",
model = 0,
isDiesel = true
),
Car(
brand = "브랜드2",
model = 1,
isDiesel = false
),
Car(
brand = "브랜드3",
model = 2,
isDiesel = true
),
Car(
brand = "브랜드4",
model = 3,
isDiesel = false
),
Car(
brand = "브랜드5",
model = 4,
isDiesel = true
),
Car(
brand = "브랜드6",
model = 5,
isDiesel = false
),
)
val carAdapter = CarAdapter(carList, object : CarAdapter.OnCarClickListener {
override fun onCarClick(position: Int, car: Car) {
val brand = car.brand
val model = car.model
val isDiesel = car.isDiesel
Log.e(TAG, "${position}번 아이템 클릭 - brand : $brand, model : $model, isDiesel : $isDiesel")
}
})
recyclerView.apply {
layoutManager = LinearLayoutManager(this@RecyclerViewClickEventActivity)
setHasFixedSize(true)
adapter = carAdapter
}
}
}
}
이 방식으로 만든 리사이클러뷰 클릭 이벤트 또한 아래처럼 작동한다.
다른 방법을 쓰더라도 액티비티 / 프래그먼트에서 클릭한 아이템의 position 값은 구할 수 있다. 또한 각 방식마다 장단점이 있긴 하지만 제일 좋은 건 자신의 상황에 맞고 빠른 속도로 정확하게 작동하는 리사이클러뷰 클릭 이벤트 구현 방법을 적용하는 것이다.
각 방법들을 한 번이라도 자신의 프로젝트에 붙이면서 연습한 다음 자신이 제일 좋다고 판단한 or 적절한 방법을 사용하자.
'Android' 카테고리의 다른 글
[Android] 아이템 하나만 선택 가능한 리사이클러뷰 만드는 법 + 데이터바인딩 적용 (0) | 2022.08.25 |
---|---|
[Android] 유튜브 라이브러리로 영상을 전체화면으로 재생하는 방법 + 리사이클러뷰에서 웹뷰로 여러 영상 재생하기 (0) | 2022.08.08 |
[Android] Coroutine Flow란? MVVM + Flow + Retrofit 예제 (0) | 2022.08.07 |
[Android] targetSdkVersion 33 이후 onBackPressed() deprecated 이슈와 onBackPressedCallback() 사용법 (0) | 2022.08.01 |
[Android] DataStore란? DataStore 예제 (0) | 2022.07.24 |