Android 4.2 : 중첩 조각이있는 백 스택 동작


108

Android 4.2에서 지원 라이브러리는 중첩 된 조각을 지원합니다 . 여기를 참조하세요 . 나는 그것을 가지고 놀았고 백 스택 및 getChildFragmentManager () 에 관한 흥미로운 동작 / 버그를 발견했습니다 . getChildFragmentManager () 및 addToBackStack (String name)을 사용할 때 뒤로 버튼을 누르면 시스템이 이전 조각으로 백 스택을 실행하지 않습니다. 반면에 getFragmentManager () 및 addToBackStack (String name)을 사용하는 경우 뒤로 버튼을 누르면 시스템이 이전 조각으로 돌아갑니다.

저에게는이 동작이 예상치 못한 일입니다. 내 장치의 뒤로 버튼을 누르면 마지막으로 추가 된 조각이 자식 조각 관리자의 백 스택에 추가 된 경우에도 백 스택에 마지막으로 추가 된 조각이 팝업 될 것으로 예상됩니다.

이 동작이 맞습니까? 이 동작이 버그입니까? 이 문제에 대한 해결 방법이 있습니까?

getChildFragmentManager ()를 사용한 샘플 코드 :

public class FragmentceptionActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle arg0) {
    super.onCreate(arg0);

    final FrameLayout wrapper1 = new FrameLayout(this);
    wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT));
    wrapper1.setId(1);

    final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT);
    params.topMargin = 0;

    final TextView text = new TextView(this);
    text.setLayoutParams(params);
    text.setText("fragment 1");
    wrapper1.addView(text);

    setContentView(wrapper1);

    getSupportFragmentManager().beginTransaction().addToBackStack(null)
            .add(1, new Fragment1()).commit();
}

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper2 = new FrameLayout(getActivity());
        wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper2.setId(2);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 100;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 2");
        wrapper2.addView(text);

        return wrapper2;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(2, new Fragment2()).commit();
    }
}

public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper3 = new FrameLayout(getActivity());
        wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper3.setId(3);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 200;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 3");
        wrapper3.addView(text);

        return wrapper3;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getChildFragmentManager().beginTransaction().addToBackStack(null)
                .add(3, new Fragment3()).commit();
    }
}

public class Fragment3 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper4 = new FrameLayout(getActivity());
        wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper4.setId(4);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 300;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 4");
        wrapper4.addView(text);

        return wrapper4;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getChildFragmentManager().beginTransaction().addToBackStack(null)
                .add(4, new Fragment4()).commit();
    }
}

public class Fragment4 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper5 = new FrameLayout(getActivity());
        wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper5.setId(5);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 400;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 5");
        wrapper5.addView(text);

        return wrapper5;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

}

getFragmentManager ()를 사용한 샘플 코드 :

public class FragmentceptionActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle arg0) {
    super.onCreate(arg0);

    final FrameLayout wrapper1 = new FrameLayout(this);
    wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT));
    wrapper1.setId(1);

    final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT);
    params.topMargin = 0;

    final TextView text = new TextView(this);
    text.setLayoutParams(params);
    text.setText("fragment 1");
    wrapper1.addView(text);

    setContentView(wrapper1);

    getSupportFragmentManager().beginTransaction().addToBackStack(null)
            .add(1, new Fragment1()).commit();
}

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper2 = new FrameLayout(getActivity());
        wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper2.setId(2);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 100;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 2");
        wrapper2.addView(text);

        return wrapper2;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(2, new Fragment2()).commit();
    }
}

public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper3 = new FrameLayout(getActivity());
        wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper3.setId(3);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 200;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 3");
        wrapper3.addView(text);

        return wrapper3;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(3, new Fragment3()).commit();
    }
}

public class Fragment3 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper4 = new FrameLayout(getActivity());
        wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper4.setId(4);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 300;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 4");
        wrapper4.addView(text);

        return wrapper4;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getFragmentManager().beginTransaction().addToBackStack(null)
                .add(4, new Fragment4()).commit();
    }
}

public class Fragment4 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final FrameLayout wrapper5 = new FrameLayout(getActivity());
        wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT));
        wrapper5.setId(5);

        final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        params.topMargin = 400;

        final TextView text = new TextView(getActivity());
        text.setLayoutParams(params);
        text.setText("fragment 5");
        wrapper5.addView(text);

        return wrapper5;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

}

흠 그것은 매우 흥미 롭습니다. 저도 그 행동을 기대하지 않습니다. 이 경우 OnBackPressed에 대한 재정의가있을 수 있으며 해당 메서드에서 자식 조각이있는 조각을 확보 한 다음 ChildFragmentManager를 확보하여 팝 트랜잭션을 실행합니다.
Marco RS

@Marco, 정확하지만 해결 방법입니다. API가 제대로 작동하지 않는 이유는 무엇입니까?
Egor

답변:


67

이 솔루션은 @Sean 답변의 더 나은 버전 일 수 있습니다.

@Override
public void onBackPressed() {
    // if there is a fragment and the back stack of this fragment is not empty,
    // then emulate 'onBackPressed' behaviour, because in default, it is not working
    FragmentManager fm = getSupportFragmentManager();
    for (Fragment frag : fm.getFragments()) {
        if (frag.isVisible()) {
            FragmentManager childFm = frag.getChildFragmentManager();
            if (childFm.getBackStackEntryCount() > 0) {
                childFm.popBackStack();
                return;
            }
        }
    }
    super.onBackPressed();
}

다시 한 번 위의 @Sean 답변을 기반으로이 솔루션을 준비했습니다.

@ AZ13이 말했듯이이 솔루션은 한 수준의 자식 조각 상황에서만 가능합니다. 여러 단계의 조각의 경우 작업이 약간 복잡해 지므로 제가 말한 가능한 경우에만이 솔루션을 시도하는 것이 좋습니다. =)

주 : 이후 getFragments메소드는 현재 개인 방법이다,이 솔루션이 작동하지 않습니다. 이 상황에 대한 해결책을 제안하는 링크에 대한 설명을 확인할 수 있습니다.


2
林奕忠답변 대신 하나의 표기는 - 올바르게 다중 중첩 단편을 처리
Hameno

정말 작동하는 코드입니까? getFragments조차도 공용 메서드가 아닙니다. stackoverflow.com/a/42572412/4729203
wonsuc

한 번 작동했지만 getFragments가 더 이상 공개되지 않으면 지금 작동하지 않을 수 있습니다.
ismailarilik 2017-04-25

1
getFragments이제 공개되었습니다.
grrigore

FragA-> Frag B-> Frag C가 Pager에있는 경우 올바르게 작동하지 않습니다.
Zhar

57

버그처럼 보입니다. http://code.google.com/p/android/issues/detail?id=40323을 살펴보세요 .

해결 방법을 위해 성공적으로 사용했습니다 (의견에서 제안한대로) :

    @Override
public void onBackPressed() {

    // If the fragment exists and has some back-stack entry
    if (mActivityDirectFragment != null && mActivityDirectFragment.getChildFragmentManager().getBackStackEntryCount() > 0){
        // Get the fragment fragment manager - and pop the backstack
        mActivityDirectFragment.getChildFragmentManager().popBackStack();
    }
    // Else, nothing in the direct fragment back stack
    else{
        // Let super handle the back press
        super.onBackPressed();          
    }
}

12
불행히도 이것은 적절한 해결책이 아니며 가장 간단한 경우에 대한 해결 방법입니다. 다른 조각을 포함하는 다른 조각을 포함하는 조각이 있다고 가정하십시오. 그런 다음 다른 하나 이상의 조각을 포함하는 마지막 조각으로 호출을 전파해야합니다. /
AZ13

@MuhammadBabar 아니, 당신은하지 않습니다. null 인 경우 이미 거짓으로 확인 되었기 때문에 문의 두 번째 부분은 평가되지 않습니다. 피연산자는 &가 아니라 | .
Sean

25

이 질문에 대한 실제 답은 setPrimaryNavigationFragment라는 Fragment Transaction의 함수에 있습니다.

/**
 * Set a currently active fragment in this FragmentManager as the primary navigation fragment.
 *
 * <p>The primary navigation fragment's
 * {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
 * to process delegated navigation actions such as {@link FragmentManager#popBackStack()}
 * if no ID or transaction name is provided to pop to. Navigation operations outside of the
 * fragment system may choose to delegate those actions to the primary navigation fragment
 * as returned by {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
 *
 * <p>The fragment provided must currently be added to the FragmentManager to be set as
 * a primary navigation fragment, or previously added as part of this transaction.</p>
 *
 * @param fragment the fragment to set as the primary navigation fragment
 * @return the same FragmentTransaction instance
 */
public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);

활동이 추가 할 때 초기 상위 프래그먼트에이 함수를 설정해야합니다. 내 활동 내에 다음과 같은 replaceFragment 함수가 있습니다.

public void replaceFragment(int containerId, BaseFragment fragment, boolean addToBackstack) {
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.setPrimaryNavigationFragment(fragment);
    if (addToBackstack) {
        fragmentTransaction.addToBackStack(fragment.TAG);
    }

    fragmentTransaction.replace(containerId, fragment).commit();
}

이렇게하면 일반 Fragment B에서 Fragment A로 다시 클릭하는 것과 동일한 동작을 제공합니다. 단, 이제 하위 조각에도 적용됩니다!


3
좋은 찾기! 이것은 OP에서 언급 한 문제를 훨씬 깨끗하고 덜 해키 방식으로 해결합니다.
Jona

HavHostFragment로 탐색 라이브러리의 일부가 아닌 이상 충돌이 발생합니다
Didi

22

이 솔루션은 @ismailarilik 답변의 더 나은 버전 일 수 있습니다.

중첩 된 조각 버전

private boolean onBackPressed(FragmentManager fm) {
    if (fm != null) {
        if (fm.getBackStackEntryCount() > 0) {
            fm.popBackStack();
            return true;
        }

        List<Fragment> fragList = fm.getFragments();
        if (fragList != null && fragList.size() > 0) {
            for (Fragment frag : fragList) {
                if (frag == null) {
                    continue;
                }
                if (frag.isVisible()) {
                    if (onBackPressed(frag.getChildFragmentManager())) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

@Override
public void onBackPressed() {
    FragmentManager fm = getSupportFragmentManager();
    if (onBackPressed(fm)) {
        return;
    }
    super.onBackPressed();
}

여러 중첩 된 조각의 수준이 때 작업에 표시되지 않습니다
splinter123가

클라우드에 중첩 된 조각의 아키텍처를 제공합니까? 내 앱에서 작동합니다. 어쩌면 내가 뭔가를 놓칠 수도 있습니다.
林奕忠

9
백 스택을 터뜨리는 순서는이 예제에서 예상 한 것과 다릅니다. 먼저 트리에서 가장 깊게 중첩 된 눈에 보이는 조각을 찾아서 그 위에 백 스택을 터뜨린 다음 점차적으로 트리 위로 이동하여 백 스택을 터뜨릴 수있는 다른 조각을 찾아야합니다. 가장 간단한 경우를 처리하는 빠른 수정은 onBackPressed (FragmentManager fm) 함수를 변경하고 백 스택을 팝하는 블록을 조각을 열거하는 블록으로 전환하는 것입니다. 더 깊이 중첩 된 자식 조각에 가기 backstack 터지는 그 방법은 먼저 일어날 것
테오에게

1
또한 중첩 된 조각이 많고 각각 화면의 일부를 덮을 수있는 자식 조각이있는 경우 각 자식 조각 관리자에 대해 별도의 백 스택을 갖는 아이디어가 더 이상 의미가 없습니다. 백 스택은 활동의 모든 조각에서 통합되어야하며 조각의 중첩 수준에 관계없이 발생하는 순서대로 조각 변경 사항을 추적해야합니다.
Theo

SupportFragmentManager표준 없이이 솔루션을 사용할 수 FragmentManager있습니까?
Peter Chappy

13

이 답변을 사용하면 재귀 적 백 검사를 처리하고 각 조각에 기본 동작을 재정의 할 수있는 기회를 제공합니다. 이것은 ViewPager를 호스팅하는 프래그먼트가 백 스택으로 페이지로 스크롤하거나 홈 페이지로 스크롤 한 다음 다음 백 프레스 종료와 같은 특별한 작업을 수행하도록 할 수 있음을 의미합니다.

AppCompatActivity를 확장하는 활동에 이것을 추가하십시오.

@Override
public void onBackPressed()
{
    if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
        super.onBackPressed();
    }
}

이것을 BaseFragment 또는 모든 조각이 상속받을 수있는 클래스에 추가하십시오.

public static boolean handleBackPressed(FragmentManager fm)
{
    if(fm.getFragments() != null){
        for(Fragment frag : fm.getFragments()){
            if(frag != null && frag.isVisible() && frag instanceof BaseFragment){
                if(((BaseFragment)frag).onBackPressed()){
                    return true;
                }
            }
        }
    }
    return false;
}

protected boolean onBackPressed()
{
    FragmentManager fm = getChildFragmentManager();
    if(handleBackPressed(fm)){
        return true;
    }
    else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
        fm.popBackStack();
        return true;
    }
    return false;
}

이 API 레벨 23/24과 24.1.1 LIB 지원과 협력, 좋은 대답이다
Rinav

한동안 이것을 사용해 왔지만 훌륭한 솔루션이지만 최근에는 외부 라이브러리 그룹에서 getFragments ()에 대한 직접 액세스에 대한 컴파일 경고가 있습니다. 해결 방법으로 이미 업데이트 했습니까?
사이먼 Huckett

문제가 해결되었거나 2020 년에도 여전히 문제가 있으며 솔루션을 구현해야합니까 ?? !!
Zhar

3

이 코드는 프래그먼트 관리자 트리를 탐색하고 스택에서 튀어 나올 수있는 프래그먼트가있는 마지막 추가 된 것을 반환합니다.

private FragmentManager getLastFragmentManagerWithBack(FragmentManager fm)
{
  FragmentManager fmLast = fm;

  List<Fragment> fragments = fm.getFragments();

  for (Fragment f : fragments)
  {
    if ((f.getChildFragmentManager() != null) && (f.getChildFragmentManager().getBackStackEntryCount() > 0))
    {
      fmLast = f.getFragmentManager();
      FragmentManager fmChild = getLastFragmentManagerWithBack(f.getChildFragmentManager());

      if (fmChild != fmLast)
        fmLast = fmChild;
    }
  }

  return fmLast;
}

메소드를 호출하십시오.

@Override
public void onBackPressed()
{
  FragmentManager fm = getLastFragmentManagerWithBack(getSupportFragmentManager());

  if (fm.getBackStackEntryCount() > 0)
  {
    fm.popBackStack();
    return;
  }

  super.onBackPressed();
}

올바르게 등록 된 경우 모든 중첩 조각에 대해 작동합니다! +100
Pratik Saluja

2

그 이유는 액티비티가 BACK 키 누름을 처리하는 FragmentActivity에서 파생되기 때문입니다 ( FragmentActivity의 173 행 참조) .

우리 응용 프로그램에서는 ViewPager (조각 포함)를 사용하고 있으며 각 조각에는 중첩 조각이있을 수 있습니다. 내가 이것을 처리 한 방법은 다음과 같습니다.

  • 단일 메소드로 인터페이스 OnBackKeyPressedListener 정의 void onBackKeyPressed()
  • ViewPager가 보여주는 "상위"조각에이 인터페이스를 구현했습니다.
  • onKeyDown을 재정의하고 BACK 누르기를 감지하고 뷰 페이저의 현재 활성 프래그먼트에서 onBackKeyPressed를 호출합니다.

또한 getChildFragmentManager()프래그먼트를 적절하게 중첩하기 위해 프래그먼트에서 사용 하고 있습니다. 이 android-developers 게시물 에서 토론과 설명을 볼 수 있습니다 .


2

조각에서 조각을 사용하는 경우 다음을 사용하십시오. getChildFragmentManager()

getChildFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

자식 조각을 사용하고 교체하는 경우 getParentFragmentManager()

getParentFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

둘 다 당신을 위해 작동하지 않으면 이것을 시도하십시오 getActivity().getSupportFragmentManager()

getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

1

onCreate View () 메서드에서이 메서드를 부모 조각에 추가하고 루트 뷰를 전달하여 조각 백 스택을 처리 할 수있었습니다.

private void catchBackEvent(View v){
    v.setFocusableInTouchMode(true);
    v.requestFocus();
    v.setOnKeyListener( new OnKeyListener()
    {
        @Override
        public boolean onKey( View v, int keyCode, KeyEvent event )
        {
            if( keyCode == KeyEvent.KEYCODE_BACK )
            {
                if(isEnableFragmentBackStack()){
                    getChildFragmentManager().popBackStack();
                                    setEnableFragmentBackStack(false);
                    return true;
                }
                else
                    return false;   
            }
            return false;
        }
    } );
}

isEnableFragmentBackStack () 메소드는 내가 메인 프래그먼트 또는 다음 프래그먼트에있을 때를 알 수있는 부울 플래그입니다.

스택이 필요한 조각을 커밋 할 때 addToBackstack 메서드를 추가해야합니다.


1

이 솔루션은 중첩 된 조각의 모든 수준을 확인하기 때문에 더 좋을 수 있습니다.

 /**
 * This method will go check all the back stacks of the added fragments and their nested fragments
 * to the the {@code FragmentManager} passed as parameter.
 * If there is a fragment and the back stack of this fragment is not empty,
 * then emulate 'onBackPressed' behaviour, because in default, it is not working.
 *
 * @param fm the fragment manager to which we will try to dispatch the back pressed event.
 * @return {@code true} if the onBackPressed event was consumed by a child fragment, otherwise {@code false}.
 */
public static boolean dispatchOnBackPressedToFragments(FragmentManager fm) {

    List<Fragment> fragments = fm.getFragments();
    boolean result;
    if (fragments != null && !fragments.isEmpty()) {
        for (Fragment frag : fragments) {
            if (frag != null && frag.isAdded() && frag.getChildFragmentManager() != null) {
                // go to the next level of child fragments.
                result = dispatchOnBackPressedToFragments(frag.getChildFragmentManager());
                if (result) return true;
            }
        }
    }

    // if the back stack is not empty then we pop the last transaction.
    if (fm.getBackStackEntryCount() > 0) {
        fm.popBackStack();
        fm.executePendingTransactions();
        return true;
    }

    return false;
}

활동에서 onBackPressed간단히 다음과 같이 부를 수 있습니다.

FragmentManager fm = getSupportFragmentManager();
                // if there is a fragment and the back stack of this fragment is not empty,
                // then emulate 'onBackPressed' behaviour, because in default, it is not working
                if (!dispatchOnBackPressedToFragments(fm)) {
                    // if no child fragment consumed the onBackPressed event,
                    // we execute the default behaviour.
                    super.onBackPressed();
                }

1

도움을 주신 모든 분들께 감사드립니다.

@Override
public void onBackPressed() {
    if (!recursivePopBackStack(getSupportFragmentManager())) {
        super.onBackPressed();
    }
}

/**
 * Recursively look through nested fragments for a backstack entry to pop
 * @return: true if a pop was performed
 */
public static boolean recursivePopBackStack(FragmentManager fragmentManager) {
    if (fragmentManager.getFragments() != null) {
        for (Fragment fragment : fragmentManager.getFragments()) {
            if (fragment != null && fragment.isVisible()) {
                boolean popped = recursivePopBackStack(fragment.getChildFragmentManager());
                if (popped) {
                    return true;
                }
            }
        }
    }

    if (fragmentManager.getBackStackEntryCount() > 0) {
        fragmentManager.popBackStack();
        return true;
    }

    return false;
}

참고 : 기본적으로 투명하므로 이러한 중첩 된 조각의 배경색을 앱 테마의 창 배경색으로 설정하는 것이 좋습니다. 이 질문의 범위를 다소 벗어나지 만 android.R.attr.windowBackground 속성을 확인하고 조각보기의 배경을 해당 리소스 ID로 설정하여 수행됩니다.


1

5 년 이상이 문제는 여전히 관련이 있습니다. 제한으로 인해 fragmentManager.getFragments ()를 사용하지 않으려는 경우. 아래 클래스를 확장하고 사용하십시오.

NestedFragmentActivity.java

abstract public class NestedFragmentActivity extends AppCompatActivity {

    private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
    private final Stack<String> mActiveFragmentTagStack = new Stack<>();

    @Override
    public void onBackPressed() {
        if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {

            // Find by id
            int lastFragmentId = mActiveFragmentIdStack.lastElement();
            NestedFragment nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentById(lastFragmentId);

            // If cannot find by id, find by tag
            if (nestedFragment == null) {
                String lastFragmentTag = mActiveFragmentTagStack.lastElement();
                nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
            }

            if (nestedFragment != null) {
                nestedFragment.onBackPressed();
            }

            // If cannot find by tag, then simply pop
            mActiveFragmentTagStack.pop();
            mActiveFragmentIdStack.pop();

        } else {
            super.onBackPressed();
        }
    }

    public void addToBackStack(int fragmentId, String fragmentTag) {
        mActiveFragmentIdStack.add(fragmentId);
        mActiveFragmentTagStack.add(fragmentTag);
    }
}

NestedFragment.java

abstract public class NestedFragment extends Fragment {

    private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
    private final Stack<String> mActiveFragmentTagStack = new Stack<>();

    private NestedFragmentActivity mParentActivity;
    private NestedFragment mParentFragment;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        if (getParentFragment() == null) {
            try {
                mParentActivity = (NestedFragmentActivity) context;
            } catch (ClassCastException e) {
                throw new ClassCastException(context.toString()
                        + " must implement " + NestedFragmentActivity.class.getName());
            }
        } else {
            try {
                mParentFragment = (NestedFragment) getParentFragment();
            } catch (ClassCastException e) {
                throw new ClassCastException(getParentFragment().getClass().toString()
                        + " must implement " + NestedFragment.class.getName());
            }
        }
    }

    public void onBackPressed() {

        if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {

            // Find by id
            int lastFragmentId = mActiveFragmentIdStack.lastElement();
            NestedFragment nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentById(lastFragmentId);

            // If cannot find by id, find by tag
            if (nestedFragment == null) {
                String lastFragmentTag = mActiveFragmentTagStack.lastElement();
                nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentByTag(lastFragmentTag);
            }

            if (nestedFragment != null) {
                nestedFragment.onBackPressed();
            }

            // If cannot find by tag, then simply pop
            mActiveFragmentIdStack.pop();
            mActiveFragmentTagStack.pop();

        } else {
            getChildFragmentManager().popBackStack();
        }
    }

    private void addToBackStack(int fragmentId, String fragmentTag) {
        mActiveFragmentIdStack.add(fragmentId);
        mActiveFragmentTagStack.add(fragmentTag);
    }

    public void addToParentBackStack() {
        if (mParentFragment != null) {
            mParentFragment.addToBackStack(getId(), getTag());
        } else if (mParentActivity != null) {
            mParentActivity.addToBackStack(getId(), getTag());
        }
    }
}

설명:

위의 클래스에서 확장 된 각 활동 및 조각은 각 자식 및 자식의 자식 등에 대한 자체 백 스택을 관리합니다. 백 스택은 단순히 "활성 조각"태그 / ID의 기록입니다. 따라서주의 할 점은 항상 조각에 태그 및 / 또는 ID를 제공하는 것입니다.

childFragmentManager에서 백 스택에 추가 할 때 "addToParentBackStack ()"도 호출해야합니다. 이렇게하면 조각의 태그 / ID가 나중에 팝을 위해 상위 조각 / 활동에 추가됩니다.

예:

    getChildFragmentManager().beginTransaction().replace(
            R.id.fragment,
            fragment,
            fragment.getTag()
    ).addToBackStack(null).commit();
    addToParentBackStack();

1

아무도 지금까지 답을 찾지 못했다면 올바르게 구현했습니다.

이 메서드를 자식 중첩 조각에 추가하십시오.

   @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true ) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
            FragmentManager fm= getFragmentManager();
            if (fm != null) {
                if (fm.getBackStackEntryCount() > 0) {
                    fm.popBackStack();
                    Log.e( "Frag","back" );

                }

                List<Fragment> fragList = fm.getFragments();
                if (fragList != null && fragList.size() > 0) {
                    for (Fragment frag : fragList) {
                        if (frag == null) {
                            continue;
                        }
                        if (frag.isVisible()) {
                          Log.e( "Frag","Visible" );
                        }
                    }
                }
            }


        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
    super.onCreate( savedInstanceState );
}

0

현재 열려있는 조각을 활동의 속성으로 유지하여이 문제를 해결했습니다. 그런 다음 메서드를 재정의합니다.

 // INSIDE ACTIVITY
 override fun onBackPressed() {
    val fragment = currentFragment

    if(fragment != null && fragment.childFragmentManager.fragments.any()){
        fragment.childFragmentManager.popBackStack()
    }
    else {
        super.onBackPressed()
    }
}

이것이 내가 현재 열린 조각에 자식 조각을 추가하는 방법입니다.

 // INSIDE FRAGMENT
 menu_fragment_view.setBackgroundColor(-((Math.random() * 10000 ).toInt() % 30000)) // to see change
 add_fragment_button.setOnClickListener {
        val transaction = childFragmentManager.beginTransaction()

        transaction.add(R.id.menu_fragment_fragment_holder, MenuFragment())

        transaction.addToBackStack(null)

        transaction.commit()
    }

이것은 부모 조각과 추가 조각의 xml 레이아웃입니다.

 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:id="@+id/menu_fragment_view">
     <Button
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@+id/add_fragment_button"
         android:text="Just add fragment"/>
     <FrameLayout
         android:id="@+id/menu_fragment_fragment_holder"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"/> 
</androidx.constraintlayout.widget.ConstraintLayout>

0

여기에 제시된 몇 가지 솔루션을 관찰 한 후 스택을 팝할 때 또는 백 액션을 무시해야 할 때 부모 조각에 대한 유연성 및 제어를 허용하기 위해 이러한 구현을 사용하는 것을 발견했습니다.

"ParentFragment"인터페이스 정의 :

interface ParentFragment {
/**
 * Fragments that host child fragments and want to control their BackStack behaviour when the back button is pressed should implement this
 *
 * @return true if back press was handled, false otherwise
 */
fun onBackPressed(): Boolean

}

상위 활동 (또는 BaseActivity)에서 "onBackPressed"재정의 :

override fun onBackPressed() {
    val fm: FragmentManager = supportFragmentManager
    for (frag in fm.fragments) {
        if (frag.isVisible && frag is ParentFragment && frag.onBackPressed()) {
            return
        }
    }
    super.onBackPressed()
}

그런 다음 부모 조각이 원하는대로 처리하도록 허용합니다. 예를 들면 다음과 같습니다.

override fun onBackPressed(): Boolean {
    val childFragment = childFragmentManager.findFragmentByTag(SomeChildFragment::class.java.simpleName)
    if (childFragment != null && childFragment.isVisible) {
        // Only for that case, pop the BackStack (perhaps when other child fragments are visible don't)
        childFragmentManager.popBackStack()
        return true
    }
    return false
}

이를 통해 예를 들어 뷰 페이저를 사용할 때 제거 할 합법적 인 자식 조각이 있다는 생각을 피할 수 있습니다 (및 백 스택 항목 수> 0).


-1

당신이있는 경우 DialogFragment다시 조각을 중첩시킨를의 '해결 방법'조금 다르다. 대신 설정의 onKeyListener받는 사람을 rootView, 당신은에 그렇게해야합니다 Dialog. 또한 당신은 하나가 DialogInterface.OnKeyListener아닌 a를 설정할 View것입니다. 물론 기억하세요 addToBackStack!

Btw, 콜백을 활동에 다시 위임하기 위해 백 스택에 1 개의 조각이있는 것이 제 개인적인 사용 사례입니다. 일반적인 시나리오는 개수가 0 일 수 있습니다.

onCreateDialog 내에서 수행해야 할 작업은 다음과 같습니다.

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog =  super.onCreateDialog(savedInstanceState);
        dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                if(keyCode == KeyEvent.KEYCODE_BACK){
                    FragmentManager cfm = getChildFragmentManager();
                    if(cfm.getBackStackEntryCount()>1){
                        cfm.popBackStack();
                        return true;
                    }   
                }   
                return false;
            }
        });
        return dialog;
    }

정말 이해할 수 없거나 완전하지 않습니다. DialogFragment 또는 수퍼 클래스 Fragment에 setOnKeyListener가 없습니다.
Ixx

@Ixx 리스너를 설정해야하며 Dialog(아님 DialogFragment) 사용하는 리스너는 다음과 같습니다 DialogInterface.OnKeyListener. 코드가 작동 중이고 완전하며 사용 중입니다. 상황을 알려 주시면 제가 도와 드릴 수 있습니다.
Sachin Ahuja 2014 년

-1

ChildFragments의 경우 작동합니다 ..

@Override
    public void onBackPressed() {

 if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            getSupportFragmentManager().popBackStack();
        } else {
            doExit(); //super.onBackPressed();
        }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.