Adapter에 요소들을 클릭하였을 때 Intent를 진행해야 하거나 MVVM패턴에 ViewModel에 대한 데이터를 수정하고 싶어서 여러가지 정보를 찾아보고 제가 해결한 내용을 정리한 것입니다.
CallBack
안드로이드 개발을 진행할 때 onClickListener()를 자주 사용하게 될 것이라고 생각하는데 이 인터페이스가 CallBack Pattern을 사용하고 있습니다.
Android Develops에서 onClickListener를 살펴보면
Interface definition for a callback to be invoked when a view is clicked.
(View를 클릭할 때 호출되는 콜백에 대한 인터페이스 정의입니다.)
View를 매개변수로 가져와서 해당 View 대한 해당 요소를 클릭했을 때 실행되도록 하는 것입니다.
Button에 onClickListenr 과정.
1. 사용자와 상호작용을 진행하게 될 Button이 존재.
2. Button을 클릭했을 때 ClickEvent를 감지하는 Listener로 감지
3. Listener로 Event감지 이후, Activity에 작성한 CallBack 함수가 로직을 처리
사용법
저는 간단한 예제를 보여드리기 위한 환경을 설명해드리겠습니다.
MVVM패턴을 이용하여 ViewModel + DataBinding을 이용한 환경입니다.
간단하게 구현한 화면입니다.
여기서 RecyclerView에 Item들을 Click하면 위에 TextView가 변경되는 형식입니다.
TextView에 대한 Text 값은 ViewModel에서 관리하도록 하였습니다.
RecyclerView7이라는 Item을 Click하면
TextView에 Text가 RecyclerView7로 변경된 것을 확인하실 수 있습니다.
구현되는 과정과 함께 코드를 보여드리겠습니다.
Layout
MainLayout
<?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="viewModel"
type="com.example.kotlintest.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.textData}"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/recyclerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
RecyclerView Item
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item_string"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="30sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Activity, ViewModel...
MainActivity
package com.example.kotlintest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.kotlintest.databinding.ActivityMainBinding
//CallBackListener 인터페이스 가져오기
class MainActivity : AppCompatActivity(), CallBackListener {
private val viewModel : MainViewModel by lazy {
ViewModelProvider(this).get(MainViewModel::class.java)
}
private lateinit var binding : ActivityMainBinding
private lateinit var adapter : Adapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//DataBinding, ViewModel 설정
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.viewModel = viewModel
binding.lifecycleOwner = this
recyclerViewSetting()
}
//RecyclerView 설정
private fun recyclerViewSetting(){
binding.recyclerView.layoutManager = LinearLayoutManager(this)
//this = CallBackListener를 Adapter 매개변수로 보내고 있습니다.
adapter = Adapter(viewModel.textList, this)
binding.recyclerView.adapter = adapter
}
//CallBack 함수 설정!
override fun uiChange(text: String) {
viewModel.textDataChange(text) //ViewModel에 textData 변경하는 함수 실행
}
}
CallBackListener을 가져와서 Listener을 형성하고 uiChange(CallBack 처리 함수)에 대한 로직을 설정합니다.
ViewModel.textDataChange()는 ViewModel에 존재하는 TextView에 대한 데이터를 변화하는 함수입니다.
RecyclerView를 설정하는 방법은 알고 이 사이트에 들어오신 것으로 생각하여 설명은 생략하겠습니다.
ViewModel
package com.example.kotlintest
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() {
val textData = MutableLiveData<String>() //TextView 데이터
//RecyclerView 데이터
val textList = listOf("RecyclerView1","RecyclerView2", "RecyclerView3", "RecyclerView4", "RecyclerView5",
"RecyclerView6", "RecyclerView7", "RecyclerView8", "RecyclerView9")
//TextView Data 변경하는 함수
fun textDataChange(text : String){
textData.value = text
}
}
Adapter
package com.example.kotlintest
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlintest.databinding.ItemBinding
class Adapter(private val data : List<String>, callBack : CallBackListener) : RecyclerView.Adapter<Adapter.ViewHolder>() {
private val mCallBack = callBack //CallBackListener 저장하기
//Adapter에서 사용할 ViewHolder
inner class ViewHolder(private val binding : ItemBinding) : RecyclerView.ViewHolder(binding.root){
//Item Setting function
fun setting(text : String){
binding.itemString.text = text
//RecyclerView Item Click감지 할 때 받아온 CallBack 함수 실행!
binding.itemString.setOnClickListener {
//ClickEvent 감지 -> CallBackListenr 발동 감지 -> Activity에 CallBack 처리 함수 실행!
mCallBack.uiChange(text)
}
}
}
//onCreateViewHolder 설정
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Adapter.ViewHolder {
val binding : ItemBinding = ItemBinding.inflate(LayoutInflater.from(parent.context))
return ViewHolder(binding)
}
//onBindingViewHolder 설정
override fun onBindViewHolder(holder: Adapter.ViewHolder, position: Int) {
holder.setting(data[position])
}
//Item 개수 구하는 Function
override fun getItemCount(): Int {
return data.size
}
}
ViewHolder에서 setting 함수에서 Item들에 대한 것을 정의할 때
Item을 클릭할 때 CallBackListener의 이벤트를 발생하여 CallBack 함수를 실행하도록 하였습니다.
더 좋은 방법이 존재한다면 댓글로 남겨주시면 감사하겠습니다.
댓글