안드로이드

[Android/Kotlin] 대화상자(Dialog) (1) AlertDialog

란서 2022. 3. 16. 21:13

대화상자 Dialog

사용자에게 결정을 내리거나 추가 정보를 입력하라는 메세지를 표시하는 작은 창이다. (경우에 따라서 화면을 가득 채우기도 한다.)

 

사용자는 어떠한 뷰(버튼, 뷰 클릭)와 상호작용 했을때 다음으로 계속 진행하기 위한 옵션이 있는 Modal 이벤트를 dialog를 통해 선택할 수 있다.

 

Dialog 종류

Dialog Class는 가장 상위 클래스이지만, 직접적인 사용은 권장되지 않는다. 따라서 다음과 같은 하위 클래스를 구현한다.

 

  • AlertDialog

    Title 1개
    [선택가능한 항목 또는 목록, 맞춤 레이아웃]
    Button 3개(최대 Positive, Negative, Netural)

    으로 보통 이루어진 대화상자.

  • DatePickerDialog or TimePickerDialog

    미리 정의된 UI (라이브러리를 통해) 가 있는 대화상자이며, 사용자가 날짜 또는 시간을 선택할 수 있다.

  • CustomDialog

 

 

DialogFragment

보통 대화상자를 생성할 때 DialogFragment를 통해 대화상자 컨테이너를 만들고, 대화상자를 구체화한다.

Dialog Class 클래스(및 서브 클래스)가 dialog의 스타일과 구조를 정의 한다면

DialogFragment는 사용자가 dialog를 통해 발생시키는 수명 주기 이벤트를 올바르게 캐치하여 적절한 action을 수행하게 만든다.



DialogFragment & AlertDialog 생성


DialogFrament를 구현하는 Fragment class 생성

이 후 onCreateDialog() override 메서드를 통해 AlertDialog를 생성한다.


class tmpDialogFragment : DialogFragment() {
	override fun onCreateDialog(~) : Dialog {
    	return activity?.let{
        	val builder = AlertDialog.Builder(it)
            builder.setMessage(R.res.string.~)
            	.setPositiveButton(R.res.string.pos, DialogInterface.onClickListener {dialog,id ->
                	...
                })
                .setNegativeButton(R.res.string.neg, DialogInterface.onClickListener {dialog,id ->
                	...
                })
               builder.create()
        } ?: throw IllegalStateException("Activity Cannot be null")
    }
}

 

Fragment 에 dialogFragment를 보여줘야 하는 경우.

class SaveDialogFragment(_listener: EditSaveDialogListener) : DialogFragment() {
    internal var listener: EditSaveDialogListener = _listener

    interface EditSaveDialogListener {
        fun onDialogPositiveClick(dialog: DialogFragment)
        fun onDialogNegativeClick(dialog: DialogFragment)
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return requireParentFragment().let {
            val builder = AlertDialog.Builder(it.context)
            builder?.setMessage("저장하시겠습니까?")
                .setPositiveButton("저장", DialogInterface.OnClickListener { dialogInterface, i ->
                    listener.onDialogPositiveClick(this)
                })
                .setNegativeButton("취소", DialogInterface.OnClickListener { dialogInterface, i ->
                    listener.onDialogNegativeClick(this)
                })
                .create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }

}

* AlertDialog Builder 하는법

  1. 제목 (title) : 선택사항으로써 콘텐츠 영역(중간부분)에 따라 필요없을수도 있다.
  2. 콘텐츠 영역(content) : 상세한 메세지, 목록, 맞춤 레이아웃 표시
  3. 작업 버튼 : 긍정버튼, 부정버튼, 중립버튼 등 대화상자 하나에 3개 이하의 버튼추천.

1. val builder : AlertDialog.Builder? = activity?.let{} 또는 requireParentFragment().let{} -> let { AlertDialog.Builder(it) }


2. builder?.setMessage() & .setTitle() & .setPositiveButton 등등

3. val dialog : AlertDialog? = builder?.create()

 


*버튼 추가 하는 법.

.setPositiveButton or .setNegativieButton

버튼에는 제목이 필요 (R.String.~) + DialogInterface.onClickListener 필요.

 

*목록 추가 하는 법.

AlertDialog와 사용가능한 목록 3가지.

 

  • 일반적인 단일 선택 목록
  • 지속적인 단일 선택 목록 (Radio box)
  • 지속적인 다중 선택 목록 (Check box)

 

일반적인 단일 선택 목록의 경우

 

builder.setItem(R.array.colors_array, DialogInterface.onClickListener{ dialog, which ->
	...
})

 

지속적인 (단일 또는 다중) 선택의 경우

builder.setSingleChoiceItems(R.array.colors_array, DialogInterface.onClickListener{ dialog, which, isChecked ->
	...
})

builder.setMultiChoiceItems(R.array.colors_array, DialogInterface.onClickListener{ dialog, which, isChecked ->
	if(isChecked) {
    	selectedItems.add(which)
    } else if(selectedItems.contains(which)) {
    	selectedItems.remove(which)
    }
})


*맞춤 레이아웃 추가 하는법

setView 호출 : 레이아웃을 생성하고, AlertDialog에 추가.

기본적으로 맞춤 레이아웃이 대화상자를 가득 채운다. 따라서

버튼과 제목은추가 가능하지만 다른 콘텐츠 영역의 콘텐츠는 사용불가능. (항목, 목록, 메시지 등)

val builder = AlertDialog.Builder(it)
val inflater = requireActivity().layoutInflater
builder.setView(inflater.inflate(R.layout.dialog_signin, null))
	.~~

 


 

이벤트를 Dialog의 호스트에 다시 전달

사용자가 dialog의 작업 버튼 중 하나를 터치하거나 목록에서 항목을 선택한다면 DialogFragment가 필요한 작업을 알아서 실행할 수도 있지만 (내부 코드 구현 후 실행), 대부분 dialog를 실행한 액티비티 및 프래그먼트에 이벤트를 전달하여 액션을 취하게 만든다.

이를 위해 각 클릭 이벤트 메서드를 interface로 정의하고 dialog를 통해 이벤트를 수신할 호스트에 인터페이스를 구현한다.

class SaveDialogFragment(_listener: EditSaveDialogListener) : DialogFragment() {
    internal var listener: EditSaveDialogListener = _listener

    interface EditSaveDialogListener {
        fun onDialogPositiveClick(dialog: DialogFragment)
        fun onDialogNegativeClick(dialog: DialogFragment)
    }

    ...
}

 

class MainActivity : Activity(), SaveDialogFragment.EditSaveDialogListener {
        override fun onDialogPositiveClick(dialog: DialogFragment) {
            ...
        }
        override fun onDialogNegativeClick(dialog: DialogFragment) {
            ...
        }
}

 

*Dialog display

 

//In activity or Fragment (호스트)

fun showDialog() {
        val newDialogFragment = SaveDialogFragment(this) //this 는 interface 를 매개변수로 넣어준것..
        newDialogFragment.show(supportFragmentManager 또는 parentFragmentManager, TAG)
    
}