이번 글에서는 Custom Dialog를 작성하는 방법에 대해 포스팅하려고 한다.
Material Dialog 와 AlertDialog 등 기본적으로 안드로이드에서 제공하는 Dialog의 형태가 있지만 내가 구현 해야했던 것은 다음과 같았기에 커스텀으로 만들기로 하였다.
우선 다이얼로그 클래스와 레이아웃을 선언해주어야한다.
class LessonDeleteDialog(context: Context) {
lateinit var listener: LessonDeleteDialogClickedListener
lateinit var btnDelete: Button
lateinit var btnCancel: Button
interface LessonDeleteDialogClickedListener {
fun onDeleteClicked()
}
private val dlg = Dialog(context)
fun start() {
/*타이틀바 제거*/
dlg.requestWindowFeature(Window.FEATURE_NO_TITLE)
/*커스텀 다이얼로그 radius 적용*/
dlg.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dlg.setContentView(R.layout.dialog_lesson_detail_delete)
btnDelete = dlg.findViewById(R.id.btn_detail_delete)
btnDelete.setOnClickListener {
listener.onDeleteClicked()
dlg.dismiss()
}
btnCancel = dlg.findViewById(R.id.btn_detail_delete_cancel)
btnCancel.setOnClickListener {
dlg.dismiss()
}
dlg.show()
}
}
<?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="312dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:background="@drawable/bg_dialog_rounded">
<ImageView
android:id="@+id/iv_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="27dp"
android:src="@drawable/ic_caution"
android:contentDescription="@string/caution"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/delete_dialog_message"
android:textColor="@color/black"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_detail" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_detail_delete_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginBottom="24dp"
android:background="@drawable/ic_bg_delete_rounded_grey"
android:text="@string/cancel"
android:textColor="@color/gray_500"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_detail_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:background="@drawable/ic_bg_delete_rounded_caution"
android:text="@string/delete"
android:textColor="@color/white"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
ConstraintLayout에 corder radius값을 주기 위해 drawable 폴더에 bg_dialog_rounded 라는 파일을 추가하고 background 속성으로 부여하였다.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white"/>
<corners
android:radius="24dp"/>
</shape>
다만 여기서 이렇게 배경속성을 준다고해서 radius가 적용되지 않아 어떻게 해야 radius가 반영될까 구글링과 온갖 방법들을 시도해봤는데
dlg.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog 초기화 작업중에 해당 코드를 넣어줘야 제대로 radius가 적용된다.
이제 dialog를 뷰를 띄우는 것을 알아봤으니 click event를 적용시켜보자
취소 버튼의 경우 그저 dialog 를 dismiss 하면되기 때문에 패스하도록하고
내 경우엔 삭제 버튼을 누르면 해당 페이지의 정보를 서버에서 삭제하는 작업을 수행하였다.
deleteLesson(accessToken: String, lessonId: String) 이라는 함수가 그 기능을 수행하였는데 해당 함수는 Activity에 존재하기 때문에
인터페이스를 통해 clickListener를 다이얼로그 클래스 내부에 선언해놓은 뒤에 액티비티에서 해당 인터페이스를 구현하는 방법으로 로직을 구성하였다.
dialog class
lateinit var listener: LessonDeleteDialogClickedListener
interface LessonDeleteDialogClickedListener {
fun onDeleteClicked()
}
btnDelete.setOnClickListener {
listener.onDeleteClicked()
dlg.dismiss()
}
Activity class
btnDetailDeleteLesson.setOnClickListener {
val dlg = LessonDeleteDialog(this@LessonDetailActivity)
dlg.listener = object: LessonDeleteDialog.LessonDeleteDialogClickedListener {
override fun onDeleteClicked() {
deleteLesson(accessToken, lessonId)
}
}
dlg.start()
}
부연설명을 하자면 저 btnDetailDeleteLesson 이라는 버튼을 클릭하면
다이얼로그가 화면에 나타나며
메소드를 재정의하지 않고
해당 다이얼로그의 리스너 변수에 인터페이스를 구현하는 식으로 코드를 작성해보았다.
Recyclerview의 Adapter에 인터페이스를 통해 클릭리스너를 달아놓은 뒤에 recyclerview가 보여지는 activity 또는 fragment에서 해당 클릭리스너를 구현하는 것과 같은 플로우이다.
인터페이스를 통해 클릭 이벤트를 구현하는 것을 그저 다른 사람이 구현하는 것을 보고 그 코드를 인용해왔기 때문에 직접 인터페이스를 선언하고 구현하는 과정에서 막히는 부분이 많았다.
코틀린의 문법을 좀 더 봐야할것같다..
코드를 왜 이렇게 작성하는가(이거 왜 이렇게 짜셨죠?) 를 항상 고민하면서 개발을 해나가야 겠다..