Android 개발을 함에 있어서 AlertDialog를 사용하는 경우를 많이 접해봤을 것이다. 예를 들면, 야놀자 앱을 켰을 때 뜨는 광고가 Dialog의 한 예라고 볼 수 있다. 필자도 이러한 Dialog를 만들어야 하는데, 기본으로 제공하는 것이 아니라 Custom 해야 했다.

그래서 xml로 원하는 뷰를 작성하고 CustomDialog라는 클래스를 만들어 xml을 inflate했다.
그리고 Click이 필요하다면 ClickListener도 설정하면 된다.

CustomDialog.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CustomDialog(context: Context) : Dialog(context), View.OnClickListener {

private val layout = R.layout.custom_dialog

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout)
init()

}

private fun init() {
dialogOkayButton.setOnClickListener(this)
}

override fun onClick(v: View) {
when (v.id) {
R.id.dialogOkayButton -> {
dismiss()
}
}
}
}

그리고 MyPageFragmet 클래스에서 CustomDialog를 사용하려고 한다.
간단하게 호출하면 된다. 코드를 한번 봐보자.

MyPageFragment.class

1
2
3
4
5
6
fragmentMyPageDataBinding.myPageMissionSuggest.setOnClickListener {
toast(requireContext().toString())
val dialog = CustomDialog(activity?.applicationContext)
dialog.setCanceledOnTouchOutside(false)
dialog.show()
}

필자는 DataBinding을 사용하기 때문에 위와 같은 코드를 구성했다. 버튼 클릭 시 Dialog를 띄우는 코드이다. Dialog 클래스는 Context를 필요로 하기 때문에 applicationContext를 넘겼다.
‘그럼 이제 Dialog가 뜨겠지?’ 라고 기대해본다.

Unable to add window – token null is not for an application

흠, 이게 무슨 상황인가. 왜 실행이 되지 않는 것일까? 역시 해결책은 구글링이다. 찾아보니 Dialog를 생성할 때 넘겨야 하는 Context는 Activity의 Context를 넘겨야 한다. 나는 지금까지 applicationContext를 넘겼기 때문에 저런 에러를 겪은 것이다.

이유는 Dialog가 Activity에 종속적이기 때문이다. 그래서 applicationContext가 아니라 Activity의 Context를 넘겨야 한다. 아마도 Dialog는 Activity가 떠야 그 위에서 그릴 수 있기 때문일 것이다. Application의 생명주기를 따르지 않고 Activity의 생명주기를 따라서 Dialog가 함께 존재하기 때문이다.

그럼 fragment에서 Activity의 context는 어떻게 얻을 수 있을까?

이에 대한 답변은 자바와 코틀린이 조금 다르다.

  • Java : getActivity() 혹은 getContext()를 통해서 얻을 수 있다.
  • Kotlin : requireContext() 함수를 통해서 얻을 수 있다.

그래서 위의 MyPageFragment.class의 코드를 수정하면 아래와 같다.

1
2
3
4
5
6
fragmentMyPageDataBinding.myPageMissionSuggest.setOnClickListener {
toast(requireContext().toString())
val dialog = CustomDialog(requireContext())
dialog.setCanceledOnTouchOutside(false)
dialog.show()
}

그러면 말끔하게 에러가 해결된 모습을 만나게 될 것이다.

참고