[안드로이드] onBackPressed와 onCancel의 차이점을 알 수 있을까요?

조회수 1549회

dialog fragment를 구현한다음 띄운 창에서 뒤로가기 버튼을 구현하려는데

onBackPressed 버튼을 누르면 동작이 안하더군요..혹은 onBackPressedCallback() 이요

DialogFragment 의 경우에는 Activitywindow 상위에 발생 되기 때문에 최상단에서 back key 이벤트를 가로채게 된다고하는데.. 정확하게 무슨 말인지 모르겠습니다

다이얼로그가 back key이벤트를 가로채게 되는데 왜 onBackPressed의 구현(또는 onBackPressedCallBack이 동작안하는지

모르겠습니다..

그래서 뒤로가기를 구현하려면 onCancel을 구현하라는데 왜 그런건가요?

이 두개의 차이점이 무엇이지요

1 답변

  • 좋아요

    1

    싫어요
    채택 취소하기

    안녕하세요. 살펴보니 제가 예전에 드린 답변에서 혼동이 있으셨던 것 같습니다. 설명이 충분하지 않았던 것 같네요 :)

    Android 에는 Window 클래스가 있는데 내부 클래스이므로 접근은 불가능 하지만 정확하게는 PhoneWindow 라는 클래스가 활용 되죠. 이 PhoneWindow 에는 최상위 부모 View 로 사용 되는 DecorView 가 있으며 그리고 Window 를 관리하는 WindowManager 클래스가 있습니다.

    Activity 는 각각의 Window 안에 생성 되며 Dialog 또한 Window 안에 생성 됩니다. 따라서, 어플리케이션의 UI 는 Window 가 겹겹이 쌓여있는 구조라고 볼 수 있습니다.

    "Dialog 가 back key 이벤트를 가로챈다" 라는 표현이 혼동의 여지가 있을 것 같은데요. 정확하게 표현하자면 KeyEvent 는 TouchEvent 처럼 Window 에 먼저 전달 되며 Activity (또는 dialog) -> View 이렇게 UI 계층을 따라 전달 됩니다. 실제로 중간에 이벤트를 가로채기 위한 onInterceptTouchEvent() 등의 API 가 존재하기도 하죠.


    본론으로 들어가면, Dialog 가 발생한 상태에서 back key 이벤트가 발생하면 Dialog 를 구성하는 Window 가 해당 이벤트를 받게 됩니다.

    또한 onBackPressedCallBackActivityonBackPressedDispatcher 를 통해 호출 됩니다. 그러나 이미 Activity 위에 새로운 Window 에서 Dialog 가 발생 하고 있고 이 Window 에서 먼저 KeyEvent 를 받기 때문에 onBackPressedCallback 을 사용 할 수 없는 것입니다.

    지난 답변에서 onCancel, onDismiss 를 언급 했던 것은 질문자님께서 어떤 시나리오를 구현하시고자 하는지 정확히 모르는 상태에서 말씀 드렸는데, 해당 callback 의 경우 Dialog 가 사라질 때 호출 되는 callback 이므로 위 질문에 대한 답변으로는 적절치 못한 것 같습니다.

    두가지 방법을 생각 해 볼 수 있는데요.

    첫번째는 Fragment 클래스는 onBackPressed() 를 제공하지 않으므로 Dialog 클래스의 onBackPresesed() 를 사용하는 방법입니다. onCreateDialog 에서 Dialog 생성 시 onBackPressed() 를 구현합니다.

     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return object : Dialog(requireContext()) {
            override fun onBackPressed() {
                //TODO: back key 이벤트 시 필요한 코드 추가
            }
        }
    }
    

    두번째 방법은 이미 생성 된 Dialog 객체에서 KeyListener 를 등록 한 뒤 Back key event 를 처리 하는 방식입니다.

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            dialog?.setOnKeyListener { dialog, keyCode, event ->
                if(keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
                    //TODO: back key 이벤트 시 필요한 코드 추가
                    return@setOnKeyListener true
                }
                return@setOnKeyListener false
            }
        }
    

    그러나 Dialog.java 코드를 살펴보면 onBackPressed() 의 호출은 이미 KeyListener 를 통해 구현 되어져 있기 때문에 내부 코드에 대한 차이점을 유의하시고 사용하시길 바랍니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 감사합니다 죄송하지만 몇가지만 다시 질문드리겠습니다. 첫번째로 최상단 View라는 의미가 XML 코드에서 계층상으로 최상위가(ConstraintLayout같은)이 아니라 액티비티, 프래그먼트 같은 각각의 뷰에서 가장 위(z축?) 에 떠있는 화면(뷰)을 말씀하시는 것이죠? 두번째로, 꼭 최상단의 뷰만 윈도우를 가지나요? 선생님이 언급해주신 윈도우의 개념이 궁금해서 여러 블로그를 찾아보았는데요 그중 한곳(https://gpark.tistory.com/m/entry/Android-Window )입니다. 블로그마다 그림으로 표현한게 비슷하더라구요.. 아무튼 액티비티화면에서 다이얼로그를 띄우면 액티비티도 윈도우를 가지고 또한 다이얼로그도 윈도우를 가진 상태가 되나요? 블로그 링크의 그림처럼 status bar, navigation bar도 윈도우를 각각 가진다면 액티비티에서 다이얼로그를 띄워도 각각 가질것같은데요. 최상단 뷰는 윈도우를 가진다고 하셔서 조금 헷갈립니다. 세번째,(https://medium.com/@yonghan_89267/android-window-8c2edbcdebe8 ) 이 블로그에서 는 액티비티화면는 status bar, navigation bar, 그리고 사용자 view 3가지를 각각 보여주는데 이 각각의 컴포넌트들은 서로 다른 window를 가지고있다고 적어놨습니다. 이 말은 액티비티자체가 윈도우를 가지고 있고 그 내부에 status bar, navigation bar,를 위한 window를 또 가지고 있다는 말인가요? 마지막으로 위 링크의 그림에서 저는 window가 액티비티를 감싸는? 그런 그림인줄 알았는데 오히려 반대로 activity안에 window가 있는 구조네요.. 원래이게 정상인가요? 너무 긴것같지만 답변해주시면 감사하겠습니다 감사합니다 codeslave 2021.6.27 01:59
    • 안녕하세요. 답변 내용에 또 혼동의 여지를 드렸네요 ^^; 우선 navigation bar, status bar 에 대해 자세히 설명 하려면 SystemUI 에 대한 언급이 필요합니다. 따라서 해당 내용은 생략하겠습니다. 다만 현재 문의 주신 내용에 대해서 Activity, Dialog 는 서로 다른 Window 에서 동작한다는 것으로 이해해주시면 될 것 같습니다. 나머지 내용에 대해서 요약 드리자면, Activity 는 setContentView() 의 매개변수로 넘어오는 layout View 를 inflate 하기 위해 Window 를 사용 합니다. 정확히는 PhoneWindow 의 DecorView 안에 inflate 되죠. 그러면, Activity 의 View 가 Window DecorView 의 ChildView 가 되므로 이 관점에서는 window 가 Activity 를 감싸고 있다 라는 표현을 굳이 할 수도 있는 것이고, Activity 클래스 내부에서 PhoneWindow 객체를 생성하여 전역 변수로 가지고 있고 UI 구성을 위해 사용하고 있는 관점에서 보면 Activity 안에 window 가 있는 구조 라고도 표현 할 수 있습니다. Activity 를 단순히 View 관점으로만 본다면, 언급하신 표현대로 Window 가 Activity 를 감싸는 형태라고 이해 하시는 편이 좋을 것 같네요. 알 수 없는 사용자 2021.6.27 17:37
    • layout을 activity의 window 안에 있는 Decorview 안에 inflate 하게 되는데..Activity를 뷰로 본다면 window(정확히는 PhonwWindow)의 DecorView 안에 위치하게 되므로(ChildeView가 되므로), 말씀대로 window가 뷰(액티비티)를 감싸는 형태로도 어떻게 보면 볼수도 있을것이지만,, 사실상은 Activty가 내부적으로 window를 가지고 있는 형태다.. 그래서 선생님의 제일 첫번째 답변에서 Activiy와 Dialog는 Window의 안에 생성된다는 표현은 정확히 말하면 Acivity 및 Dialog의 PhoneWindow의 DecorView안에 Activity 뷰와 Dialog 뷰가 생성된다라고 선생님 답변을 이해해봤는데 맞을까요? codeslave 2021.6.29 03:59
    • 추가적으로 이 개념을 적용?해서 왜 DialogFragment에서 onBackPressedCallBack()을 구현하고 액티비티의 onBackPressedDispatcher()를 이용해서 액티비티에 콜백을 전달했을때 동작 안했는가 본다면..구현은 액티비티에서 호출하는 콜백( onBackPressedCallBack())으로 구현을 하여 전달했는데 말씀하셨듯이 다이얼로그가 뜨면 액티비티보다 상단에 존재하게 되고 이 다이얼로그의 윈도우에서 백 키(BACK KEY) 이벤트를 가로채어서 액티비티에서 동작해야할 백 키 이벤트가 제대로 동작안됐다.. 인가요? codeslave 2021.6.29 04:13
    • 네 ㅋㅋ 이해하신 부분이 맞습니다. 알 수 없는 사용자 2021.6.29 14:25
    • 상세하게 적어주신 답변덕분에 드디어 이해했습니다..! 감사합니다 :) codeslave 2021.7.1 00:54
    • 다소 설명이 일목요연하지 못해서 혼동을 드렸네요 :) 이해되셨다니 다행입니다! 알 수 없는 사용자 2021.7.1 23:16

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)