Android

Android에서 CustomDialog 만들기

깨비도 2024. 8. 27. 11:22

0️⃣ CustomDialog!

사실 Dialog를 띄워 화면에 알림을 주는 일은 이제는 도저히 뺄 수 없는 필수 기능이다. 할 줄 안다고 생각했는데도 막상 다시 하니 또 버벅거리며 한참을 헤매서 세세하게 기록해보려고 한다.

1️⃣ dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    android:background="@color/black_a50"
    android:gravity="center"
    android:paddingHorizontal="20dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="192dp"
        android:background="@drawable/shape_dialog"
        android:paddingHorizontal="20dp"
        android:paddingVertical="14dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/tv_dialog_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@string/dialog_default"
            android:textColor="@color/white"
            android:textSize="16sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toTopOf="@+id/wrapper_buttons"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <LinearLayout
            android:id="@+id/wrapper_buttons"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent">

            <androidx.appcompat.widget.AppCompatButton
                android:id="@+id/btn_dialog_cancel"
                style="@style/detail_btn"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginEnd="4dp"
                android:layout_weight="1"
                android:backgroundTint="@color/transparent"
                android:gravity="center"
                android:text="@string/dialog_cancel"
                android:textColor="@color/blue_grey"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent" />

            <androidx.appcompat.widget.AppCompatButton
                android:id="@+id/btn_dialog_confirm"
                style="@style/detail_btn"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="4dp"
                android:layout_weight="1"
                android:gravity="center"
                android:text="@string/dialog_confirm"
                android:textAllCaps="false"
                app:layout_constraintBottom_toBottomOf="@+id/btn_dialog_cancel"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/btn_dialog_cancel"
                app:layout_constraintTop_toTopOf="@+id/btn_dialog_cancel" />

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

핵심 포인트는 가장 최상단 부모 태그의 width, height값을 match_parent로 주는 것이다.

2️⃣ DialogFragment

class TwoButtonDialogFragment(
    private val title: String,
    private val onClickConfirm: () -> Unit
) : DialogFragment() {
    private var _binding: DialogTwobuttonBinding? = null
    val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = DialogTwobuttonBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView()
    }

    private fun initView() = with(binding) {
        tvDialogTitle.text = title

        btnDialogCancel.setOnClickListener {
            dismiss()
        }

        btnDialogConfirm.setOnClickListener {
            onClickConfirm()
            dismiss()
        }
    }


    override fun onResume() {
        super.onResume()
        // full Screen code
        dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
        dialog?.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
    }
}

해당 Fragment를 재사용하기 위해 생성자로 title과 setOnClickListner를 받아와서 사용했다.

또한 onResume() 코드를 전체 복사해서 붙여넣자! 그렇지 않으면 아래 스크린샷처럼 옹졸한 Dialog가 나온다... (옹졸 그자체)

3️⃣ Dialog 띄우는 함수

btn.setOnClickListener {
    val dialog = TwoButtonDialogFragment(
        getString(R.string.timer_dialog_finish)
    ) {
        // timer 종료하기
    }
    // 알림창이 띄워져있는 동안 배경 클릭 막기
    dialog.isCancelable = false
    dialog.show(getParentFragmentManager(), "ConfirmDialog")
}

이렇게 setOnClickListener에서 dialog를 선언해주고, 생성자로 받기로 한 props들을 모두 보내준 뒤, show해주면 된다.

그럼 이렇게 완벽한 Dialog가 등장한다.

 


막상 정리해놓고 보니 별 거 없는데 또 저 옹졸하게 나오는 dialog 때문에 40분 넘게 삽질했다...!

더 열심히 공부해보는 것으로...