일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- PagingLib
- 인앱결제
- Reactive
- rx
- google play
- liveData
- Kotlin
- GCP
- RxKotlin
- MediaPlayer
- Android
- MotionLayout
- 동영상
- 테스트 자동화
- paging
- mvvm
- Android 13
- junit
- node.js
- MediaSession
- Koin
- Animation
- Observable
- SwiftUI Tutorial
- node
- databinding
- list
- android13
- SWIFTUI
- mysql
- Today
- Total
봄날은 갔다. 이제 그 정신으로 공부하자
Dialog 커스텀하기 본문
이번 글에서는 android에서 기본으로 지원하는 AlertDialog를 활용하여 Dialog를 custom하는 방식에 대해 설명합니다.
저는 기본 AlertDialog가 좋은데 다른 사람들은 AlertDialog를 싫어하더라구요. ㅠ_ㅠ
일반 Dialog 만들기
android에서 지원하는 AlertDialog는 Builder(디자인 패턴의 Builder pattern 맞습니다.)를 지원하기 때문에 아래와 같이 간단히 dialog를 만드는 것이 가능합니다.
정말 심플하네요.
AlertDialog.Builder(this)
.setTitle("TITLE")
.setMessage("MESSAGE")
.setNegativeButton("NO", { dialogInterface: DialogInterface, i: Int ->
})
.setPositiveButton("YES", { dialogInterface: DialogInterface, i: Int ->
})
.show()
Custom Dialog 만들기
Custom Dialog는 복잡하게 만들기보다는 일반적인 AlertDialog와 같이 Title과 Message가 있고 Yes & No 두개의 버튼만 만들어보겠습니다. 예제는 단순하지만 layout에서 만들어준 View를 dialog에 추가해주는 방식이다보니 List 등 다양한 방식으로 확장이 가능합니다.
loyout 만들기
우선 dialog를 커스텀하기 위해서는 우선 Dialog에서 사용할 layoutxml을 만들어 줍니다.
위에서 언급한 것과 같이 Title과 Message 그리고 Yes & No 버튼 두개만 존재하는 기본적인 Dialog 형태를 띄고 있습니다.
<?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="400dp"
android:layout_height="wrap_content"
android:padding="20dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:textColor="@color/colorGrape"
android:textSize="@dimen/dp28"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/vDivider"
android:layout_width="0dp"
android:layout_height="5dp"
android:layout_marginTop="20dp"
android:background="@color/colorOatmeal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
app:layout_constraintBottom_toTopOf="@+id/tvMessage"/>
<TextView
android:id="@+id/tvMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center"
android:layout_marginTop="50dp"
android:layout_marginBottom="30dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:textColor="@color/colorGrape"
android:textSize="@dimen/dp24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/vDivider" />
<Button
android:id="@+id/btnNo"
android:layout_width="0dp"
android:layout_height="70dp"
android:paddingStart="20dp"
android:paddingLeft="20dp"
android:paddingEnd="20dp"
android:paddingRight="20dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_marginTop="50dp"
android:layout_marginRight="10dp"
android:textSize="@dimen/dp24"
android:textStyle="bold"
android:textColor="@color/colorWhite"
android:background="@drawable/button_round20_gray_white"
android:stateListAnimator="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnYes"
app:layout_constraintTop_toBottomOf="@+id/tvMessage" />
<Button
android:id="@+id/btnYes"
android:layout_width="0dp"
android:layout_height="70dp"
android:paddingStart="20dp"
android:paddingLeft="20dp"
android:paddingEnd="20dp"
android:paddingRight="20dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_marginTop="50dp"
android:layout_marginLeft="10dp"
android:textSize="@dimen/dp24"
android:textStyle="bold"
android:textColor="@color/colorWhite"
android:background="@drawable/button_round20_orange_tomato"
android:stateListAnimator="@null"
app:layout_constraintStart_toEndOf="@+id/btnNo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvMessage" />
</androidx.constraintlayout.widget.ConstraintLayout>
custom dialog 생성
커스텀 다이얼로그를 생성해주는 방법은 아래와 같습니다.
자세한 설명은 코드에 주석으로 추가하였습니다.
fun showCustomDialog(){
// AlertDialog에 덧씌워줄 layout view를 생성합니다.
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val dialogView = inflater.inflate(R.layout.dialog_custom_yesno, null)
// dialog에 view를 추가합니다.
val dialog = AlertDialog.Builder(this)
.setView(dialogView)
.create()
// dialog의 배경 이미지를 새로운 스타일로 변경합니다.
dialog.window?.let {
val windowLayoutParam = it.attributes
windowLayoutParam.gravity = Gravity.CENTER_VERTICAL or Gravity.CENTER_HORIZONTAL
it.attributes = windowLayoutParam
it.setBackgroundDrawableResource(R.drawable.dra_round_white)
}
// Text를 입력합니다.
dialogView.tvTitle.text = "Custom"
dialogView.tvMessage.text = "My Custom dialog"
// 버튼 Text와 이벤트를 정의해줍니다.
dialogView.btnNo.text = "NO"
dialogView.btnNo.setOnClickListener {
Toast.makeText(this@MainActivity, "No Button Click", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
dialogView.btnYes.text = "YES"
dialogView.btnYes.setOnClickListener {
Toast.makeText(this@MainActivity, "Yes Button Click", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
// dialog가 종료 될때 처리가 필요한 경우 처리할 수 있도록 이벤트를 정의합니다.
dialog?.setOnDismissListener {
}
// dialog를 사용자에게 보여줍니다.
dialog.show()
}
Custom Dialog에 빌더 패턴 더하기
이렇게 만든 경우, 위 커스텀 다이얼로그를 생성할 때마다 위 언급된 긴 코드를 작성해야 하기 때문에 재사용성 및 기타 여러가지 측면에서 조금은 부족해보입니다. 재사용성 및 가독성 향상을 위해 커스텀 다이얼로그에 Builder 패턴을 적용해보도록 하겠습니다.
굳이 Builder 패턴을 적용해서 만들려는 이유는 일반적인 class로 만들 경우, 생성 과정이 복잡해 오히려 재사용성 및 가독성이 더 떨어질 수 있기 때문 입니다.
Custom Dialog class 만들기
아래와 같이 빌더 패턴을 적용한 MyCustomDialog class를 만들어줍니다.
class MyCustomDialog {
data class Builder(
var context: Context? = null,
var textTitle: String = "",
var textMessage: String = "",
var textNo: String = "",
var textYes: String = "",
var onFinished: (()->Unit)? = null,
var onClickNo: ((AlertDialog)->Unit)? = null,
var onClickYes: ((AlertDialog)->Unit)? = null
){
lateinit var dialog: AlertDialog
fun context(context: Context) = apply { this.context = context }
fun setTitle(textTitle: String) = apply { this.textTitle = textTitle }
fun setMessage(textMessage: String) = apply { this.textMessage = textMessage }
fun setOnFinished(onFinished: (() -> Unit)) = apply { this.onFinished = onFinished }
fun setOnClickNo(textNo: String, onClickNo: ((AlertDialog) -> Unit)) = apply {
this.textNo = textNo
this.onClickNo = onClickNo
}
fun setOnClickYes(textYes: String, onClickYes: ((AlertDialog) -> Unit)) = apply {
this.textYes = textYes
this.onClickYes = onClickYes
}
fun build(): AlertDialog{
context?.run {
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val dialogView = inflater.inflate(R.layout.dialog_custom_yesno, null)
dialog = AlertDialog.Builder(this)
.setView(dialogView)
.create()
dialog?.window?.let {
val windowLayoutParam = it.attributes
windowLayoutParam.gravity = Gravity.CENTER_VERTICAL or Gravity.CENTER_HORIZONTAL
it.attributes = windowLayoutParam
it.setBackgroundDrawableResource(R.drawable.dra_round_white)
}
dialogView.tvTitle.text = textTitle
dialogView.tvMessage.text = textMessage
if(textNo.isNullOrEmpty()){
dialogView.btnNo.visibility = View.GONE
}else {
dialogView.btnNo.visibility = View.VISIBLE
dialogView.btnNo.text = textNo
dialogView.btnNo.setOnClickListener {
onClickNo?.invoke(dialog)
}
}
if(textYes.isNullOrEmpty()){
dialogView.btnYes.visibility = View.GONE
}else {
dialogView.btnYes.visibility = View.VISIBLE
dialogView.btnYes.text = textYes
dialogView.btnYes.setOnClickListener {
onClickYes?.invoke(dialog)
}
}
dialog?.setOnDismissListener {
onFinished?.invoke()
}
}
return dialog
}
}
}
Custom Dialog 사용하기
위와 같이 만들어진 Custom Dialog는 아래와 같이 사용하면 됩니다.
아래 코드를 보면 기존 사용하던 AlertDialog와 유사한 것을 확인하실 수 있습니다.
MyCustomDialog.Builder()
.context(this@MainActivity)
.setTitle("Custom")
.setMessage("My Custom dialog")
.setOnClickNo("NO", {
Toast.makeText(this@MainActivity, "No Button Click", Toast.LENGTH_SHORT).show()
it.dismiss()
})
.setOnClickYes("YES", {
Toast.makeText(this@MainActivity, "Yes Button Click", Toast.LENGTH_SHORT).show()
it.dismiss()
})
.setOnFinished {
}.build()
.show()
정리
위 정리한 것처럼 생성 시 파라미터가 여러개 들어가 생성이 복잡한 instance를 만들때는 Builder 패턴을 사용해주는것이 좋습니다.
위 코드는 아래 링크에서 확인하실 수 있습니다.
github.com/lee-kil-jae/MyListCollection
'android Tip' 카테고리의 다른 글
앱 재실행 (App Restart) (0) | 2021.01.22 |
---|---|
PopupWindow로 Spinner 대체하기 (0) | 2021.01.18 |
Callback Java에서 Kotlin으로… (0) | 2021.01.13 |
여러가지 방식으로 List 만들어보기 - OldStyle (0) | 2021.01.11 |
Coroutine - part2 (0) | 2020.12.28 |