핀수로그
  • fragment 에서 onBackPressed() 접근하기
    2022년 08월 13일 22시 50분 28초에 업로드 된 글입니다.
    작성자: 핀수
    728x90
    반응형

    onBackPressed()

    Called when the activity has detected the user's press of the back key.

    말그대로 사용자가 뒤로 가기 버튼을 클릭 했을 때 해당 메서드가 호출된다.

    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }

    ComponentActivity가 들고 있는 메서드이고
    AppCompatActivity 는 FragmentActivity를 상속 받고
    FragmentActivity는 ComponentActivity를 상속 받는다.

    따라서 onBackPressed 메서드는 activty에서는 얼마든지
    오버라이딩해서 입맛대로 사용이 가능하다.

    출처 : 마이케이티 앱

    메인화면에서 뒤로 가기 버튼을 눌렀을 때,
    앱을 종료하겠냐는 다이얼로그를 띄운다고 생각해보자

    MainActivity.java

    @Override
    public void onBackPressed() {
        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setMessage("앱을 종료하시겠습니까?")
                .setPositiveButton("앱 종료", (dialogInterface, i) -> finish())
                .setNegativeButton("취소", (dialogInterface, i) -> dialogInterface.dismiss())
                .create()
                .show();
    }


    만약 Single Activity Architecture (SAA) 구조라면?

    *SAA : 하나 혹은 적은 갯수의 activity만을 사용하고 나머지 화면은 fragment로 구성한 구조. 주로 Jetpack Navigation과 함께 사용된다.

    Fragment에서 해당 작업이 이루어져야 할 것이다.
    그러나 프래그먼트에서는 onBackPressed를 오버라이딩 할 수 없다.
    그래서 처음에는 현재 프래그먼트를 알아내 해당 프래그먼트의 타입을 알아내 필요한 행위를 지정했다.

    MainFragment.java

    @Override
    public void onBackPressed() {
        Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.nav_host).getChildFragmentManager().getFragments().get(0);
        if (currentFragment instanceof MainFragment){
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setMessage("앱을 종료하시겠습니까?")
                    .setPositiveButton("앱 종료", (dialogInterface, i) -> finish())
                    .setNegativeButton("취소", (dialogInterface, i) -> dialogInterface.dismiss())
                    .create()
                    .show();
        } else {
            super.onBackPressed();
        }
    }

    OnBackPressedCallback

    뒤로 가기 버튼 이벤트가 발생했을 때 처리해야하는 것들이 많아짐에 따라,
    처리 해야줘야 할 fragment들이 많아짐에 따라
    위의 방법은 부담스러웠다.
    fragment내에서 처리하지 못하고 MainActivity까지 끌고 나와야했기 때문이다.
    (fragment 내부의 요소에 접근해야할 때)

    MainFragment.java

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
    }
    
    
    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setMessage("앱을 종료하시겠습니까?")
                    .setPositiveButton("앱 종료", (dialogInterface, i) -> requireActivity().finish())
                    .setNegativeButton("취소", (dialogInterface, i) -> dialogInterface.dismiss())
                    .create()
                    .show();
        }
    };


    OnBackPressedDispatcher
    - 뒤로 가기 버튼 이벤트가 하나 이상의 OnBackPressedCallback 객체로 전달되는 방법을 제어

    OnBackPressedCallback
    - 해당 객체의 생성자는 초기 사용 설정 상태를 나타내며 콜백이 사용 설정됐다면 true 를 반환,
    디스패처가 handleOnBackPressed()를 호출해 해당 이벤트를 처리한다.

    addCallback(LifecycleOwner owner, OnBackPressedCallback onBackPressedCallback)
    - LifecycleOwner가 동작할 때만 해당 콜백이 추가되도록 설정
    - 해당 메서드를 통해 여러개의 콜백을 제공할 수 있다.
    - 콜백은 추가된 역순으로 호출된다. (A - B - C 순 -> C - B - A 순으로 호출될 것)
    - 책임 연쇄 패턴 (Chain of Responsibility) 을 따른다고 한다.
    -> 라이프사이클이 시작될 때 (Lifecycle.State.STARTED), 해당 상태에 진입할 때까지 chain에 추가되지 않는다.

    - 콜백을 제거하려는 경우 remove()를 호출해야한다.
    그러나 콜백은 연결된 LifecycleOwner가 제거될 때 자동으로 삭제 되기 때문에 필수는 아니다.




    References

    아래 글을 참고하여 작성 되었습니다.
    https://heegs.tistory.com/128
    https://developer.android.com/guide/navigation/navigation-custom-back?hl=ko

    728x90
    반응형
    댓글