안드로이드 Custom Dialog 만들기 완벽정리

2021. 10. 29. 22:09개발을 파헤치다/Android

반응형

 

알림 창(Dialog)은 안드로이드 앱 개발 시 상당히 많이 쓰이는 요소인데요.
오늘은 내가 원하는 레이아웃을 적용한 Custom Dialog를 어떻게 구현하는지 살펴보도록 하겠습니다.

 

Builder 패턴 이해

Dialog에는 다양한 속성들이 있습니다.
Custom Dialog를 만들 때에도 기존 Dialog의 다양한 속성과 더불어 개발자가 새로운 속성 값들을 설정하도록 구현할 수 있습니다. 이렇게 다양한 설정을 효율적으로 하기 위해서 Custom Dialog를 구현할 때에 Builder 패턴을 적용하는 것이 좋습니다.

안드로이드에서 흔하게 쓰는 Alert Dialog를 사용할 때에도 Builder 패턴을 사용하게됩니다.
이 디자인 패턴은 속성 값이 많은 객체를 생성할 때 사용되는데요. 많은 속성 값을 가진 객체를 생성할 때 일부 속성만 선택적으로 초기화해서 만들 수 있다는 점과 새로운 속성 추가나 제거 시 코드 변경이 용이하다는 장점이 있습니다.

예를 들어, 아래의 Custom Dialog에는 다양한 속성 값들이 있습니다.

 

class CommentUpdateDialog(context: Context) : Dialog(context) {

    var commentMessage: String? = null
    var positiveButtonMsg: String? = null
    var negativeButtonMsg: String? = null
    var positiveButtonCallback: CommentUpdateDialog.CommentUpdateDialogCallback ?= null
    var negativeButtonCallback: CommentUpdateDialog.CommentUpdateDialogCallback ?= null
    
 }

 

위 Comment Update Dialog 객체를 생성하려면 모든 데이터를 초기화해주던지 아니면
속성 값 별로 초기화할 수 있도록 생성자를 여러 개를 만들어주어야 합니다.
이렇게 되면 노가다 작업이 늘어나겠죠. 효율적이지 못합니다.

Builder 패턴을 활용하면 아래와 같이 필요한 데이터만 초기화가 가능하고 새로운 속성이 추가됐을 때에도 기존 코드 변경 없이 사용이 가능합니다.

CommentUpdateDialog(this).setCommentMessage()
                         .setPositiveButtonMsg()
                         .setNegativeButtonMsg()
                         // 새롭게 속성이 추가되어도 기존 코드는 영향받지 않는다
                         .setNewMethod()

 

 

Custom Dialog 클래스 구현

 

class CommentUpdateDialog(context: Context) : Dialog(context) {

    var commentMessage: String? = null
    var positiveButtonMsg: String? = null
    var negativeButtonMsg: String? = null
    var positiveButtonCallback: CommentUpdateDialog.CommentUpdateDialogCallback ?= null
    var negativeButtonCallback: CommentUpdateDialog.CommentUpdateDialogCallback ?= null

    var mBinding: DialogCommentUpdateBinding? = null

    // Dialog의 show() 메서드가 호출되고 나서야 onCreate 메서드가 실행된다
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setCanceledOnTouchOutside(true)
        mBinding = DialogCommentUpdateBinding.inflate(layoutInflater)
        setContentView(mBinding!!.root)

        // Dialog 레이아웃 크기 및 위치 설정
        // setContentView 실행 후에 적용되어야 제대로 반영이된다
        window?.setGravity(Gravity.CENTER)
        // 기기 가로 너비의 85% 크기로 적용하겠다는 의미
        val width = (context.resources.displayMetrics.widthPixels * 0.85).toInt()
        window?.setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT)
        
        initView()
    }

    // Activity 종료시 Dialog를 종료시킨다
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        this.dismiss()
    }

    // Builder를 통해 받은 설정값들을 초기화하는 메서드
    private fun initView(){
        mBinding!!.commentUpdateDialogEditText.setText(commentMessage!!)
        mBinding!!.commentUpdateDialogPositiveButton.text = positiveButtonMsg
        mBinding!!.commentUpdateDialogPositiveButton.setOnClickListener(View.OnClickListener {
            val updatedMsg = mBinding!!.commentUpdateDialogEditText.text.toString()
            positiveButtonCallback!!.onClick(this, updatedMsg)
        })
        mBinding!!.commentUpdateDialogNegativeButton.text = negativeButtonMsg
        mBinding!!.commentUpdateDialogNegativeButton.setOnClickListener(View.OnClickListener {
         val updatedMsg = mBinding!!.commentUpdateDialogEditText.text.toString()
         negativeButtonCallback!!.onClick(this, updatedMsg)
        })
    }


    // Builder 클래스 구현
    class Builder(val context: Context){

        private var dialog = CommentUpdateDialog(context)

        fun setCommentMessage(message: String): Builder{
            dialog.commentMessage = message
            return this
        }
        fun setPositiveButton(msg: String, callback: CommentUpdateDialog.CommentUpdateDialogCallback)
                :Builder{
            dialog.positiveButtonMsg = msg
            dialog.positiveButtonCallback = callback
            return this
        }

        fun setNegativeButton(msg: String, callback: CommentUpdateDialogCallback): Builder{
            dialog.negativeButtonMsg = msg
            dialog.negativeButtonCallback = callback
            return this
        }

        fun show(): CommentUpdateDialog{
            dialog.show()
            return dialog
        }
    }

    // Button 클릭 이벤트 처리를 위해 호출할 콜백 함수
    interface CommentUpdateDialogCallback{
        fun onClick(dialog: CommentUpdateDialog, message: String)
    }
}

 

Custom Dialog 사용하기

 

// 사용하기
CommentUpdateDialog.Builder(context)
                    .setTitle(AppUtils.getString(R.string.menu_comment_update))
                    .setCommentMessage(item.comment)
                    .setPositiveButton(dialogOkMsg, object:CommentUpdateDialog.CommentUpdateDialogCallback{
                        override fun onClick(dialog: CommentUpdateDialog, message: String) {
                            // API를 호출한다
                            presenter.updateParentComment(commentId = item.id,
                                    message = message, position = position)
                            dialog.dismiss()
                        }
                    })
                    .setNegativeButton(dialogNoMsg, object:CommentUpdateDialog.CommentUpdateDialogCallback{
                        override fun onClick(dialog: CommentUpdateDialog, message: String) {
                            // Dialog를 내린다
                            dialog.dismiss()
                        }
                    })
                    .show()
반응형