Fragments에서 onBackPressed ()를 구현하는 방법은 무엇입니까?


459

onBackPressed()Android 활동에서 구현하는 방식과 비슷한 방식으로 Android Fragment에서 구현할 수 있는 방법이 있습니까?

조각 수명주기에는 없습니다 onBackPressed(). onBackPressed()안드로이드 3.0 프래그먼트 를 오버라이드 할 수있는 다른 방법이 있습니까?


13
IMHO, 조각은 BACK 버튼을 알거나 신경 쓰지 않아야합니다. 활동은 일반적으로 FragmentManager에 의해 처리되는 프래그먼트를 사용하여 BACK 버튼을 처리 할 수 ​​있습니다. 그러나 프래그먼트가 더 큰 환경에 대해 알지 못하기 때문에 (예 : 액티비티의 여러 환경 중 하나인지 여부) 프래그먼트가 적절한 BACK 기능이 무엇인지 판단하는 것은 실제로 안전하지 않습니다.
CommonsWare

61
모든 조각이 WebView를 표시하기 위해 언제하고 뒤로 버튼을 눌렀을 때 WebView가 이전 페이지로 "돌아 가기"를 원하십니까?
mharper

Michael Herbig의 답변은 저에게 완벽했습니다. 그러나 getSupportFragmentManager ()를 사용할 수없는 사람들을 위해. 대신 getFragmentManager ()를 사용하십시오.
Dantalian 2013

[Similar Question] [1]에 대한 Dmitry Zaitsev의 답변은 잘 작동합니다. [1] : stackoverflow.com/a/13450668/1372866
ashakirov 2013

6
mharper에 대답하기 위해 webview.setOnKeyListener (new OnKeyListener () {@ public public boolean onKey (View v, int keyCode, KeyEvent event) 재정의) {if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack ()) {webview.goBack (); 반환 true;} 반환 false;}});
Omid Aminiva

답변:


307

이 방법으로 onBackPressedActivity에서 재정의 를 해결했습니다 . 모든이 FragmentTransaction있습니다 addToBackStack커밋하기 전에 :

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}


4
count == 1은 단일 뒤로 버튼 누름으로 첫 번째 조각을 닫을 수 있습니다. 그러나 그렇지 않으면 가장 좋은 해결책
Kunalxigxag

2
지원 v7 라이브러리를 사용 중이고 활동이 FragmentActivity (또는 AppCompatActivity와 같은 서브 클래스 )에서 확장 된 경우 기본적으로 발생합니다. 참조 FragmentActivity #은 onBackPressed
샤오

3
하지만이 코드에서는 프래그먼트 클래스의 변수, 클래스 및 함수를 사용할 수 없습니다.
user25

2
@Prabs 지원 v4 조각을 사용하는 경우 getSupportFragmentManager (). getBackStackEntryCount ()를 사용하고 있는지 확인하십시오.
Veer3383

151

내 생각에 가장 좋은 해결책은 다음과 같습니다.

자바 솔루션

간단한 인터페이스를 만듭니다.

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

그리고 당신의 활동에서

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

마지막으로 조각에서 :

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

코 틀린 솔루션

1-인터페이스 작성

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2-활동 준비

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3-대상 조각 구현

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}

1
무엇입니까 R.id.main_container? 이것이 FragmentPager의 ID입니까?
Nathan F.

1
Kotlin 솔루션의 @MaximeJallu, 안전한 통화 ( .?)를 혼합하고 널이 아닌 값 ( !!)을 적용 하면 NPE가 발생할 수 있습니다. 캐스트가 항상 안전하지는 않습니다. 차라리 글을 if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }쓰거나 좀 더 kotliney 하고 싶습니다(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek

1
@Antek 안녕하세요. 반품 주셔서 감사합니다. 수정을 위해 게시물을 수정했을 수 있습니다. 전체 솔루션을 심각하게 판단하지 마십시오.
Maxime Jallu

4
그렇지 .onBackPressed()?.takeIf { !it }?.let{...}않습니까? .not()그냥 역을 반환합니다.
David Miguel

1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }이것은 Kotlin과 NavigationController를 사용하는 사람들을위한 것입니다
Pavle Pavlov

97

@HaMMeRed에 따르면 여기에 의사 코드가 어떻게 작동 해야하는지에 대한 대답이 있습니다. BaseActivitySlidingMenu lib 예제와 같이 하위 조각이있는 기본 활동이 호출되었다고 가정 해 봅시다. 단계는 다음과 같습니다.

먼저 일반적인 메소드를 갖도록 인터페이스를 구현하는 인터페이스와 클래스를 만들어야합니다.

  1. 수업 인터페이스 만들기 OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. 의 기술을 구현하는 수업 만들기 OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. 이제부터 코드 BaseActivity와 그 조각 에 대해 작업하겠습니다.

  4. 수업 위에 비공개 청취자 만들기 BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. 리스너를 설정하는 메소드 작성 BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. 재정의에서 onBackPressed그런 것을 구현하십시오.

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. 당신의 조각에 onCreateView당신의 청취자를 추가해야합니다

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

Voila, 이제 조각으로 다시 클릭하면 사용자 정의 on back 메소드를 잡아야합니다.


둘 이상의 프래그먼트가 리스너 인 onBackPressed()경우 뒤로 버튼을 눌렀을 때 어떤 프래그먼트가 표시되고 있는지를 어떻게 확인 합니까?
Al Lelopath

2
새로운 baseBackPressedListener 대신 클릭 리스너를 사용자 정의하고 익명으로 호출하여 다음과 같이 고유 한 동작을 설정할 수 있습니다.((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish

고마워, 나는 이것에 대한 나의 입장을 바꿨다. 그러나 나는 지금 로컬 방송 메시지와의 분리를 권장한다. 나는 그것이 분리 된 고품질의 구성 요소를 생산할 것이라고 생각합니다.
HaMMeReD


이것은 나를 위해 완벽하게 작동했습니다, 나는 활동을 다시 시작합니다
raphaelbgr

86

이것은 나를 위해 일했다 : https://stackoverflow.com/a/27145007/3934111

@Override
public void onResume() {
    super.onResume();

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}

2
완벽하게 일했습니다! 그러나 IF 조건을 하나 더 작성하여 기본 동작을 처리해야합니다. 이후, SlideUpPanel이 펼쳐져 있으면 내 렸습니다. 그래서, 나는 먼저 수표를 표시해야했고 (패널이 올라
왔을

18
이 솔루션의 문제점은 getView ()가 기본보기 만 리턴하고 하위보기는 초점을 얻는 경우를 리턴하지 않는다는 것입니다. 따라서 EditText가 있고 일부 텍스트를 입력 한 다음 "뒤로"를 한 번 눌러 키보드를 숨기면 "뒤로"를 두 번 누르면 "EditText"(보기 자체)에서 시작되며이 방법은 " back "버튼을 누른 다음 (메인 뷰만 잡기 때문에) 내가 이해 한 것처럼 "EditText"및 다른보기에서 키 누르기 이벤트를 잡을 수는 있지만 "복잡한"양식이 있으면 깨끗하고 유지 관리 할 수 ​​없습니다.
Pelpotronic

이것은 파편 인 악몽에 대한 훌륭한 간단한 솔루션입니다. '복잡한'편집 텍스트 양식이있는 경우 프로젝트를 삭제하고 다시 시작해야합니다. 표준 동작을 사용하려면 false를 반환하십시오. 당신이 다시 언론을 차단하고 그것으로 뭔가를 할 때 true를 돌려줍니다
behelit

65

그런 종류의 기능을 원한다면 활동에서 기능을 재정의 한 다음 YourBackPressed모든 조각에 인터페이스를 추가 해야합니다.이 단추를 누를 때마다 관련 조각을 호출합니다.

편집 : 이전 답변을 추가하고 싶습니다.

오늘이 작업을 수행하려면 다른 패널이 마스터 / 메인 콘텐츠 패널과 동시에 업데이트 될 것으로 예상되는 경우 브로드 캐스트 또는 주문 된 브로드 캐스트를 사용합니다.

LocalBroadcastManager지원 라이브러리에서이 기능을 사용하면 브로드 캐스트를 전송하고 onBackPressed관심있는 조각을 구독하면됩니다. 메시징은 더 분리 된 구현이며 더 확장 성이 좋으므로 지금 공식 구현 권장 사항이 될 것입니다. 그냥 사용하는 Intent메시지의 필터로의 조치를. 새로 만든 ACTION_BACK_PRESSED을 보내고 활동에서 보내고 관련 조각에서 듣습니다.


4
꽤 멋진 아이디어! 몇 가지 추가 생각 : + 활동> 조각 (다른 방식으로 진행되지 않음)에서 나오는 방송 으로이 논리를 실현하는 것이 중요합니다. OnBackPressed는 액티비티 레벨에서만 사용할 수 있으므로 이벤트를 트랩하고 모든 "듣기"프래그먼트에 브로드 캐스트하는 것이 중요합니다. + Square의 오토와 같은 EventBus를 사용하면 이처럼 사소한 경우를 구현할 수 있습니다!
Kaushik Gopal 2018 년

2
Square 's EventBus를 아직 사용하지 않았지만 향후 제품에 포함될 예정입니다. 필자는 이것이 더 나은 순수 자바 솔루션이라고 생각하며 아키텍처의 Android 섹션을 중단 할 수 있다면 더 좋습니다.
HaMMeReD

상단 조각 만 이벤트를 처리하도록하려면 어떻게해야합니까? 예를 들어, 현재 보이는 조각
eugene

브로드 캐스트 수신기는 사용중인 경우 수명주기에 바인딩되어야하므로 표시되지 않으면 이벤트를 수신하지 않아야합니다.
HaMMeReD

또한 LocalBroadcastManager주문 된 방송 을 할 수 없습니다
Xiao

46

그 중 어느 것도 구현하기 쉽지 않으며 최적의 방식으로 작동하지 않습니다.

프래그먼트에는 작업을 수행 할 메소드 호출 onDetach가 있습니다.

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

이 작업을 수행합니다.


20
그러나 여기서 문제는 백 프레스로 인해 또는 다른 동작으로 인해 프래그먼트가 분리되었는지 여부를 알 수 없다는 것입니다. 이로 인해 사용자가 다른 활동 또는 프래그먼트로 이동하게됩니다.
Sunny

7
onDetach ()는 프래그먼트가 더 이상 활동에 첨부되지 않을 때 호출됩니다. 이것은 onDestroy () 이후에 호출됩니다. 뒤로 버튼을 누를 때이 코드가 표시되지만 조각에서 조각으로 변경하면이 코드가 표시됩니다.
그래도이

DialogFragment에 잘 작동합니다.
CoolMind

2
stackoverflow.com/a/27103891/2914140에isRemoving() 설명 된대로 추가 하는 것을 잊지 마십시오 .
CoolMind

1
이것은 잘못이다. 몇 가지 이유로 부를 수 있습니다
Bali

40

androidx.appcompat:appcompat:1.1.0이상을 사용하는 경우 OnBackPressedCallback다음과 같이 조각에을 추가 할 수 있습니다

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

참조 https://developer.android.com/guide/navigation/navigation-custom-back를


를 사용하는 경우 다음을 사용할 androidx-core-ktx수 있습니다.requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
Max

그것은주의하는 것이 중요 LifecycleOwnerPARAM이 예에서와 같이 추가해야합니다. 그것없이, handleBackPressed()뒤로 버튼을 누르면 이후에 시작된 조각이 호출 됩니다.
Eric B.

이 방법이 탐색 라이브러리와 함께 있어야합니까?
침묵

25

addToBackStack아래처럼 조각 사이를 전환하는 동안 추가 하십시오.

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

쓰면 addToBackStack(null)자체적으로 처리하지만 태그를 제공하면 수동으로 처리해야합니다.


수행해야 할 다른 것이 있거나 addToBackStack 메소드를 추가하는 것으로 충분합니까?
Sreecharan Desabattula

1
작동한다고 추가하면 여전히 작동하지 않으면 코드에 무언가가 있어야합니다. @SreecharanDesabattula 좀 볼 수있는 코드의 어딘가를 남겨주세요
Rudi


20

이 질문과 일부 답변이 5 년이 넘었으므로 솔루션을 공유하겠습니다. 이것은 @oyenigun의 답변에 대한 후속 및 현대화입니다.

최신 정보: 이 기사의 맨 아래에는 Activity가 전혀 포함되지 않는 추상 Fragment 확장을 사용하여 대체 구현을 추가했습니다.이 기능은 다른 백 동작이 필요한 중첩 된 조각과 관련된 더 복잡한 조각 계층 구조를 가진 사람에게 유용합니다.

내가 사용하는 일부 프래그먼트가 작은 정보 뷰와 같이 뒤로 버튼으로 닫고 싶은 더 작은 뷰를 가지고 있기 때문에 이것을 구현해야했지만, 이는 동작을 재정의 해야하는 모든 사람에게 좋습니다 조각 내부의 뒤로 버튼.

먼저 인터페이스를 정의하십시오

public interface Backable {
    boolean onBackPressed();
}

내가 부르는이 인터페이스 Backable(명명 규칙의 stickler) onBackPressed()boolean값을 반환 해야하는 단일 메소드 를 가지고 있습니다. 뒤로 버튼 누름이 뒤로 이벤트를 "흡수"했는지 알아야하기 때문에 부울 값을 적용해야합니다. 리턴 true은 추가 조치가 필요하지 않음을 의미하며, 그렇지 않으면 false기본 뒤로 조치가 여전히 수행되어야한다고 말합니다. 이 인터페이스는 자체 파일 (바람직하게는 별도의 패키지 interfaces) 이어야합니다 . 클래스를 패키지로 분리하는 것이 좋습니다.

둘째, 최고의 조각을 찾으십시오

Fragment백 스택에서 마지막 객체 를 반환하는 메서드를 만들었습니다 . 태그를 사용합니다 ... ID를 사용하는 경우 필요한 사항을 변경하십시오. 탐색 상태 등을 처리하는 유틸리티 클래스 에이 정적 메소드가 있지만 물론 가장 적합한 위치에 배치하십시오. 교화를 위해이라는 클래스에 내 것을 넣었습니다 NavUtils.

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

백 스택 수가 0보다 큰지 확인하십시오. 그렇지 않으면 ArrayOutOfBoundsException런타임에 가 발생할 수 있습니다. 0보다 크지 않으면 null을 반환합니다. 나중에 null 값을 확인합니다 ...

셋째, 조각으로 구현

구현 Backable뒤로 단추 동작을 대체해야하는 조각이있는 인터페이스를 . 구현 방법을 추가하십시오.

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

에서 onBackPressed()재정의, 당신이 필요로하는 어떤 논리했습니다. 뒤로 버튼으로 백 스택 이 나오지 않게 하려면 (기본 동작) true를 백 이벤트가 흡수되었음을 하십시오. 그렇지 않으면 false를 반환하십시오.

마지막으로, 활동에서 ...

onBackPressed()메소드를 대체 하고이 로직을 추가하십시오.

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

백 스택에서 현재 조각을 얻은 다음 null 검사를 수행하여 Backable인터페이스를 구현하는지 확인합니다 . 그렇다면 이벤트가 흡수되었는지 판별하십시오. 그렇다면 우리는 끝났습니다onBackPressed() 돌아올 수 있습니다. 그렇지 않은 경우에는 일반 백 프레스로 처리하고 super 메소드를 호출하십시오.

활동을 포함하지 않는 두 번째 옵션

때때로, 당신은 Activity가 이것을 처리하기를 원하지 않으며, 조각 내에서 직접 처리해야합니다. 그러나 누가 당신이 할 수 없다고 백 프레스 API로 프래그먼트를 가질 합니까? 조각을 새 클래스로 확장하십시오.

Fragment를 확장하고 View.OnKeyListner인터페이스를 구현하는 추상 클래스를 작성하십시오 .

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

보시다시피, 확장되는 조각 BackableFragmentView.OnKeyListener인터페이스를 사용하여 클릭을 자동으로 캡처합니다 . 표준 논리를 사용하여 onBackButtonPressed()구현 된 onKey()메소드 내 에서 추상 메소드를 호출하여 뒤로 버튼 누름을 식별하십시오. 뒤로 버튼 이외의 키 클릭을 등록 해야하는 경우 조각에서 super재정의 할 때 메소드 를 호출해야합니다 onKey(). 그렇지 않으면 추상화의 동작을 재정의 합니다.

사용하기 쉽고 확장하고 구현하십시오.

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

onBackButtonPressed()수퍼 클래스 의 메소드는 추상적이므로 일단 확장하면 구현해야합니다 onBackButtonPressed(). void프래그먼트 클래스 내에서 작업을 수행하기 만하면되므로 반환 되며 흡수를 다시 액티비티로 전달할 필요가 없습니다. 뒤로 버튼으로 수행하는 작업에 처리가 필요하지 않은 경우 Activity 메서드를 호출 해야 합니다 onBackPressed(). 그렇지 않으면 뒤로 버튼이 비활성화되고 원하지 않습니다!

주의 사항 보시다시피, 이것은 키 리스너를 프래그먼트의 루트 뷰로 설정하므로 집중해야합니다. 이 클래스를 확장하는 프래그먼트에 포함 된 편집 텍스트 (또는 다른 포커스 스틸 링보기)가 있거나 다른 내부 프래그먼트 또는보기가있는 경우이를 별도로 처리해야합니다. EditText 확장에 대한 좋은 기사가 있습니다.백 프레스에 초점을 잃기 를 .

누군가가 이것을 유용하게 사용하기를 바랍니다. 행복한 코딩.


고마워, 그것은 좋은 해결책입니다. 왜 super.onBackPressed();두 번 전화 합니까?
CoolMind

의 null 상태와 관련하여 시나리오 당 한 번만 호출 currentFragment됩니다. 프래그먼트가 널이 아니고 프래그먼트가 Backable인터페이스를 구현하면 조치가 수행되지 않습니다. 프래그먼트가 null이 아니고 구현하지 않으면 Backablesuper 메소드를 호출합니다. 프래그먼트가 null이면 마지막 슈퍼 호출로 건너 뜁니다.
mwieczorek

죄송합니다. 이해가되지 않았습니다. a currentFragment가 null이 아니고 Backable의 인스턴스 인 경우 분리 된 경우 ( back버튼 을 누르고 조각을 닫음)의 첫 번째 발생을 super.onBackPressed();호출 한 다음 두 번째 발생을 호출합니다.
CoolMind

탁월한 솔루션
David Toledo

어쩌면 "Closeable"은 "Backable"보다 약간 나을 수 있습니다.
pumnao

15

해결책은 간단합니다.

1) 모든 조각이 확장되는 기본 조각 클래스가있는 경우이 코드를 해당 클래스에 추가하십시오. 그렇지 않으면 기본 조각 클래스를 작성하십시오

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2) 활동 클래스에서 다음과 같이 onBackPressed를 대체하십시오.

private BaseFragment _currentFragment;

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

3) Fragment 클래스에서 원하는 코드를 추가하십시오.

 @Override
 public void onBackPressed()
 {
    setUpTitle();
 }


9

onBackPressed() 조각이 활동에서 분리됩니다.

@Sterling Diaz의 답변에 따르면 나는 그가 옳다고 생각합니다. 그러나 어떤 상황은 잘못 될 것입니다. (예 : 화면 회전)

isRemoving()목표 달성 여부 를 감지 할 수 있다고 생각합니다 .

onDetach()또는에 쓸 수 있습니다 onDestroyView(). 일입니다.

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}

8

글쎄, 나는 이것을 이렇게했고, 그것은 나를 위해 일한다.

간단한 인터페이스

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

구현 예

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

그런 다음 활동에서 onBackPressed를 재정의하십시오.

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}

작동하지 않는 ! super.onbackpressed ()는 항상 호출됩니다 :(
Animesh Mangla

이 이벤트를 전달하는 방법 Inner Fragment. 활동에 ViewPager가 있고 ViewPager에 Fragments가 있으며 이제 모든 Fragment에 하위 단편이 있습니다. 해당 자식 조각에 버튼 이벤트를 전달하는 방법.
Kishan Vaghela

@KishanVaghela 잘 그때부터 안드로이드를 만지지 않았지만 한 가지 아이디어가 있습니다. 24 번째 답변을 드리겠습니다.
Błażej

문제 없습니다. 한 가지 옵션은 부모 조각에서 모든 자식 조각으로 리스너 이벤트를 전달해야한다는 것입니다. 그러나 모든 조각에 대해이 작업을 수행해야하기 때문에 이상적인 방법은 아닙니다.
Kishan Vaghela

@KishanVaghela 나는 활동 관리자에 대해 생각하고있었습니다. 해당 인터페이스를 사용하여 조각을 추가 / 제거 할 수 있습니다. 그러나이 방법을 사용하면 하위 조각이있는 모든 조각에서 관리자를 구현해야합니다. 따라서 더 나은 솔루션은 관리자를 싱글 톤으로 만드는 것입니다. 그런 다음 조각 / 객체를 추가 / 제거해야하며 'onBackPressed'에서이 관리자의 'onBackPressed'메서드를 호출합니다. 해당 메소드는 관리자에 추가 된 모든 오브젝트에서 'onClick'메소드를 호출합니다. 그것은 당신에게 다소 명확합니까?
Błażej

8

아래와 같이 프로젝트에 인터페이스를 추가해야합니다.

public interface OnBackPressed {

     void onBackPressed();
}

그런 다음 조각에이 인터페이스를 구현해야합니다.

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

그리고 당신은 당신의 활동 onBackPressed 이벤트 아래 에서이 onBackPressed 이벤트를 트리거 할 수 있습니다;

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}

좋은 방법이지만주의해야합니다 getActivity().onBackPressed();. "java.lang.StackOverflowError : stack size 8MB"예외를 호출하므로 호출하지 마십시오 .
CoolMind

8

이것은 트릭을 수행하는 작은 코드 일뿐입니다.

 getActivity().onBackPressed();

그것이 누군가를 돕기를 바랍니다 :)


4
그는 단순히 액티비티 메소드를 호출하지 않고 동작을 대체하도록 요청했습니다.
mwieczorek

2
고마워 읽어 봤어요 솔루션은 대체 방법을 물었을 때 onBackPressed 메소드를 Activity로 다시 중계합니다.
mwieczorek

7

EventBus를 사용하는 경우 훨씬 간단한 솔루션 일 것입니다.

조각에서 :

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

활동 클래스에서 다음을 정의 할 수 있습니다.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java는 POJO 객체 일뿐입니다.

이것은 매우 깨끗하며 인터페이스 / 구현 번거 로움이 없습니다.


7

이것은 내 솔루션입니다.

MyActivity.java :

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

그리고 단편에서 :

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});

5

Navigation 구성 요소 를 사용하면 다음 과 같이 할 수 있습니다 .

자바

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

코 틀린

class MyFragment : Fragment() {

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

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}

4

onDestroyView ()를 사용하는 것은 어떻습니까?

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

5
실제 조각에서 다른 조각으로 이동하는 방법은 무엇입니까? :)
Choletski

가장 쓸모없는 대답. 그것은 서면과 동일 i = i또는 if (1 < 0) {}.
CoolMind

4
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}

프래그먼트에 포커스가있는보기 (예 : 텍스트 편집)가없는 경우에만이 답변 에서처럼 프래그먼트가 버튼 자체를 다시 처리하도록하십시오. 그렇지 않으면 첫 번째 답변이 작성 될 때 프래그먼트가 포함 된 액티비티가 OnBackPress ()에 의해 수행되도록하십시오. 포커스 가능한 뷰는 프래그먼트의 포커스를 훔치기 때문입니다. 그러나 clearFocus () 메서드를 사용하여 모든 포커스 가능 뷰를 명확하게 포커스하고 프래그먼트에 다시 초점을 맞추면 조각에 대한 포커스를 다시 얻을 수 있습니다.
Nguyen Tan Dat

4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

조각에 다음을 추가하고 mainactivity의 인터페이스를 구현하는 것을 잊지 마십시오.

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}

4

내 솔루션에서 (Kotlin);

나는에 매개 변수로 onBackAlternative 기능을 사용하고 BaseActivity .

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

BaseFragmentonBackPressAlternative 를 설정하는 기능이 있습니다.

BaseFragment

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

그런 다음 onBackPressAlternative를 조각에 사용할 준비가되었습니다.

하위 조각

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}

3

뒤로 버튼을 눌렀을 때 활동이 완료되도록 ft.addToBackStack () 메소드를 구현하지 마십시오.

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();


3

다음 단계를 따르십시오.

조각을 추가하는 동안 항상

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

그런 다음 주요 활동에서 재정의 onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

앱에서 뒤로 버튼을 처리하려면

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

그게 다야!


3

활동 수명주기에서 FragmentActivity 또는 AppCompatActivity를 사용하면 항상 Android 뒤로 버튼이 FragmentManager 트랜잭션을 처리합니다.

백 스택을 처리하기 위해 백 스택 수 또는 태그를 처리 할 필요는 없지만 조각을 추가하거나 교체하는 동안 초점을 유지해야합니다. 뒤로 버튼 케이스를 처리하기 위해 다음 스 니펫을 찾으십시오.

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

여기에서는 내 응용 프로그램의 홈 페이지이므로 홈 조각에 대한 스택을 다시 추가하지 않습니다. addFBackStack을 HomeFragment에 추가하면 응용 프로그램이 acitivity의 모든 frament를 제거하기 위해 대기 한 다음 빈 화면이 표시되므로 다음과 같은 조건을 유지합니다.

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

이제 acitvity에서 이전에 추가 된 조각을 볼 수 있으며 HomeFragment에 도달하면 앱이 종료됩니다. 다음 스 니펫을 볼 수도 있습니다.

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}

3

조각 : 메소드를 배치하는 BaseFragment를 작성하십시오.

 public boolean onBackPressed();

활동:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

액티비티는 첨부되고 보이는 조각을 실행하고 각 조각에서 onBackPressed () 를 호출하고 그중 하나가 'true'를 반환하면 중단됩니다 (처리되었으므로 더 이상의 작업이 없음).


선생님, 이것은 가장 우아한 방법이며 완벽하게 작동합니다!
asozcan

3

에 따르면 AndroidX 릴리스 노트 , androidx.activity 1.0.0-alpha01출시 및 출시되고 ComponentActivity, 기존의 새로운 기본 클래스 FragmentActivityAppCompatActivity. 이 릴리스에는 새로운 기능이 있습니다.

이제 활동의 메소드를 재정의하지 않고도 OnBackPressedCallback비아 addOnBackPressedCallback를 등록하여 onBackPressed()콜백 을 수신 할 수 있습니다.


이것의 예를 보여줄 수 있습니까? 그 방법으로 해결할 수 없습니다.
Bryan Bryce

1
@BryanBryce 나는 예제를 찾지 못했지만 공식 문서는 developer.android.com/reference/androidx/activity/…
TonnyL

이 방법은 b / c를 해결하지 못합니다 AppCompatActivity는 실제로 androidx.activity.ComponentActivity 대신 androidx.core.app.ComponentActivity에서 확장됩니다.
wooldridgetm 22

3

역행을 처리하기 위해 활동에 단편을 등록 할 수 있습니다.

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

용법:

조각에서 :

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

활동 중 :

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}

3

조각 내에서 콜백을 사용하면 onBackPressed를 처리하여 커스텀 백 네비게이션을 제공하는 것이 더 쉬워졌습니다.

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

어떤 조건에 따라 기본 뒤로 동작을 원하면 다음을 사용할 수 있습니다.

 NavHostFragment.findNavController(this@MyFragment).navigateUp();

3

프래그먼트의 onCreate 메소드 안에 다음을 추가하십시오.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

이 myFragment backpress를 추가 한 후 작동하지만 지금은 backpress가 작동하지 않습니다
Sunil Chaudhary

2

최고의 솔루션

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

            }
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.