본문 바로가기
Android Studio

[Android, Kotlin] Adapter에서 UI 및 데이터 변경하기(CallBack)

by 열정적인 이찬형 2022. 9. 30.

Adapter에 요소들을 클릭하였을 때 Intent를 진행해야 하거나 MVVM패턴에 ViewModel에 대한 데이터를 수정하고 싶어서 여러가지 정보를 찾아보고 제가 해결한 내용을 정리한 것입니다.


CallBack

안드로이드 개발을 진행할 때 onClickListener()를 자주 사용하게 될 것이라고 생각하는데 이 인터페이스가 CallBack Pattern을 사용하고 있습니다.

 

Android Develops에서 onClickListener를 살펴보면

 

View.OnClickListener  |  Android Developers

android.net.wifi.hotspot2.omadm

developer.android.com

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 함수를 실행하도록 하였습니다.

 

 

더 좋은 방법이 존재한다면 댓글로 남겨주시면 감사하겠습니다.

댓글