관리 메뉴

나만을 위한 블로그

[Android] 데이터 바인딩, 콜백을 사용해 BottomSheetDialog에서 액티비티로 값 전달하는 방법 본문

Android

[Android] 데이터 바인딩, 콜백을 사용해 BottomSheetDialog에서 액티비티로 값 전달하는 방법

참깨빵위에참깨빵 2022. 7. 18. 23:19
728x90
반응형

사용자가 BottomSheetDialog에서 어떤 값을 선택 or 입력하면 그 값을 액티비티 또는 프래그먼트로 받아와야 할 때가 있다. 값을 받은 다음의 후속 처리는 나중 일이고, BottomSheetDialog에서 어떻게 액티비티 or 프래그먼트로 값을 보낼 수 있을까?

해결법이야 다양하겠지만 내 기준으로 가장 간단한 방법은 콜백이다. 어떻게 만드는지 확인해보자.

 

먼저 BottomSheetDialog의 XML을 대충 만든다. 이름은 test_bottom_sheet.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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white">

        <TextView
            android:id="@+id/tvBottomSheetTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="BottomSheetDialog"
            android:textColor="@color/black"
            android:textSize="30sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tvBottomSheetSubTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:text="제목 밑 글자"
            android:textColor="@color/black"
            android:textSize="24sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tvBottomSheetTitle" />

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btnBottomSheetCancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_marginStart="40dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tvBottomSheetSubTitle"
            android:text="취소"/>

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btnBottomSheetOk"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_marginEnd="40dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tvBottomSheetSubTitle"
            android:text="확인"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

그리고 BottomSheetDialog 파일도 만들어준다.

 

import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.Window
import com.example.kotlinprac.databinding.TestBottomSheetBinding

class TestBottomSheet(context: Context, layoutId: Int): Dialog(context, layoutId) {

    private lateinit var binding: TestBottomSheetBinding
    private lateinit var dialog: Dialog
    private var listener: OnSendFromBottomSheetDialog? = null

    fun showDialog() {
        binding = TestBottomSheetBinding.inflate(LayoutInflater.from(context))
        dialog = setDialogOptions()

        binding.run {
            btnBottomSheetCancel.setOnClickListener {
                if (listener == null) return@setOnClickListener
                listener?.sendValue("BottomSheetDialog에서 취소 버튼 클릭함!")
                dialog.dismiss()
            }

            btnBottomSheetOk.setOnClickListener {
                if (listener == null) return@setOnClickListener
                listener?.sendValue("BottomSheetDialog에서 확인 버튼 클릭함!")
                dialog.dismiss()
            }
        }
    }

    private fun setDialogOptions(): Dialog = Dialog(context).apply {
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(binding.root)
        setCanceledOnTouchOutside(true)
        window?.run {
            setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
            setGravity(Gravity.BOTTOM)
        }
        show()
    }

    interface OnSendFromBottomSheetDialog {
        fun sendValue(value: String)
    }

    fun setCallback(listener: OnSendFromBottomSheetDialog) {
        this.listener = listener
    }

}

 

자잘한 건 다 치우고 인터페이스와 setCallback(), 각 버튼의 클릭 리스너 안을 잘 봐야 한다.

먼저 인터페이스를 만들고 전역변수로 이 인터페이스의 참조변수를 만든 다음, setCallback()으로 어딘가에서 이 인터페이스의 구현을 보내면 BottomSheetDialog 안의 인터페이스를 이것으로 초기화한다.

그리고 버튼을 누르면 내가 정의해둔 임의의 문자열을 추상 함수를 통해 호출된 곳으로 쏜다. 여기서 호출된 곳은 액티비티 or 프래그먼트가 될 것이다.

 

이제 액티비티도 만들어준다. XML은 데이터바인딩 처리하고 버튼 하나 대충 만들어두면 된다.

 

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityCallbackSecondBinding

class CallbackSecondActivity : AppCompatActivity() {

    private val TAG = this.javaClass.simpleName
    private val binding by lazy {
        ActivityCallbackSecondBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        binding.run {
            btnCallBottomSheet.setOnClickListener {
                val dialog = TestBottomSheet(this@CallbackSecondActivity, R.layout.test_bottom_sheet)
                dialog.apply {
                    showDialog()
                    setCallback(object : TestBottomSheet.OnSendFromBottomSheetDialog {
                        override fun sendValue(value: String) {
                            Log.e(TAG, "BottomSheetDialog -> 액티비티로 전달된 값 : $value")
                        }
                    })
                }
            }
        }
    }
}

 

화면의 버튼을 클릭하면 showDialog()의 영향으로 BottomSheetDialog가 생길 것이다. 그 후 이어서 setCallback()으로 TestBottomSheet 안에 있는 인터페이스를 구현한다. 그럼 OnSendFromBottomSheetDialog 인터페이스를 구현한 이 액티비티가 인터페이스 안에 있는 함수의 호출자가 되는 것이므로, BottomSheetDialog 안의 취소 또는 확인 버튼을 누르면 그 값이 이 액티비티로 전달된다. showDialog()와 setCallback()의 호출 순서는 상관없다.

확인을 위해 로그로 BottomSheetDialog에서 날아온 값을 출력하는데 로그캣을 보면 아래와 같이 나온다.

 

 

프래그먼트를 사용한다면 매개변수인 context와 this@CallbackSecondActivity 대신 requireActivity()를 넣으면 액티비티에서처럼 똑같이 작동할 것이다.

기능 구현을 우선으로 해서 리팩토링할 부분이 많다. 각자 프로젝트에 맞게 구현한 다음 리팩토링하면 될 것이다.

또한 이 포스팅에선 버튼을 사용했지만 라디오그룹+라디오버튼, 체크박스, 리사이클러뷰 등 어떤 뷰를 사용하더라도 로직 흐름만 파악된다면 곧바로 응용 가능할 것이다.

반응형
Comments