Android 4.2: iç içe geçmiş parçalarla arka yığın davranışı


108

Android 4.2 ile, destek kitaplığı iç içe geçmiş parçalar için destek aldı, buraya bakın . Bununla uğraştım ve back stack ve getChildFragmentManager () ile ilgili ilginç bir davranış / hata buldum . GetChildFragmentManager () ve addToBackStack (Dize adı) kullanılırken, geri düğmesine basılarak sistem, geri yığını önceki parçaya doğru çalıştırmaz. Öte yandan, getFragmentManager () ve addToBackStack (Dize adı) kullanılırken, geri düğmesine basarak sistem önceki parçaya geri döner.

Benim için bu davranış beklenmedik. Cihazımdaki geri düğmesine basarak, arka yığına eklenen son parçanın, parça çocuk parça yöneticisindeki arka yığına eklense bile fırlatılmasını bekliyorum.

Bu davranış doğru mu? Bu davranış bir hata mı? Bu sorun için bir çözüm var mı?

getChildFragmentManager () ile örnek kod:

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 () ile örnek kod:

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);
    }
}

}

hmmm bu çok ilginç. Ben de bu davranışı beklemiyorum. Eğer durum böyleyse, OnBackPressed üzerinde geçersiz kılma yapmış olabileceğimiz ve bu yöntemde alt parçaların bulunduğu parçanın tutulması ve ardından pop işlemi gerçekleştirmek için ChildFragmentManager'ın tutulması gibi görünüyor.
Marco RS

@Marco, Kesinlikle, ancak yine de bir çözüm. API neden düzgün çalışmıyor?
Egor

Yanıtlar:


67

Bu çözüm, @Sean yanıtının daha iyi bir versiyonu olabilir:

@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();
}

Yine bu çözümü yukarıdaki @Sean cevabına göre hazırladım.

@ AZ13'ün dediği gibi, bu çözüm yalnızca tek seviyeli çocuk fragmanları durumlarında uygulanabilir. Çok seviyeli fragmanlar durumunda işler biraz karmaşık hale gelir, bu yüzden bu çözümü sadece söylediğim uygulanabilir durumda denemenizi tavsiye ederim. =)

Not: bu yana getFragmentsyöntem şimdi özel bir yöntemdir, bu çözüm işe yaramaz. Bu durumla ilgili bir çözüm öneren bir bağlantı için yorumları kontrol edebilirsiniz.


2
林奕 忠 yanıtı bunun yerine kullanılmalı - birden fazla iç içe geçmiş parçayı doğru şekilde
işliyor

Gerçekten çalışan kod mu? GetFragments bile herkese açık bir yöntem değil mi? stackoverflow.com/a/42572412/4729203
wonsuc

Bir kez çalıştı, ancak getFragments artık herkese açık değilse şimdi çalışmayabilir.
ismailarilik

1
getFragmentsartık herkese açık.
grrigore

Çağrı Cihazı içinde FragA -> Frag B -> Frag C varsa bu doğru çalışmaz ...
Zhar

57

Böcek gibi görünüyor. Şu adrese bir göz atın: http://code.google.com/p/android/issues/detail?id=40323

Başarıyla kullandığım bir geçici çözüm için (yorumlarda önerildiği gibi):

    @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
Ne yazık ki bu uygun bir çözüm değil ve sadece en basit durum için bir geçici çözüm. Başka bir parça içeren, başka bir parça içeren bir parçanız olduğunu varsayalım. Ardından çağrıyı, başka bir veya daha fazla parça içeren son parçaya
yaymanız gerekir

@MuhammadBabar Hayır, yapmayacaksın. Boş ise, ifadenin ikinci kısmı değerlendirilmez çünkü zaten yanlış olarak belirlenir. işlenen & dir ve değildir | .
Sean

25

Bu sorunun gerçek yanıtı Fragment Transaction'ın setPrimaryNavigationFragment adlı işlevindedir.

/**
 * 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);

Bu işlevi, etkinlik eklerken ilk ana parçada ayarlamanız gerekir. Etkinliğimin içinde şuna benzeyen bir replaceFragment işlevi var:

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();
}

Bu size normal B Parçasından A Parçasına geri dönüyormuşsunuz gibi aynı davranışı verir, ancak artık alt parçalarda da vardır!


3
Harika bul! Bu, OP tarafından bahsedilen sorunu çok daha temiz ve daha az hilekâr bir şekilde çözer.
Jona

Bu, bir HavHostFragment olarak navigasyon kitaplığının bir parçası olmadığı sürece çöküyor
Didi,

22

Bu çözüm @ ismailarilik cevabının daha iyi bir versiyonu olabilir:

İç içe geçmiş parça sürümü

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();
}

birden fazla iç içe geçmiş parça düzeyi olduğunda çalışmıyor gibi görünüyor
splinter123

Bulut bana iç içe geçmiş parçalardan oluşan mimarini mi veriyorsun? Benim uygulamamda çalışıyor. Belki bir şeyi özlüyorum.
林奕 忠

9
Arka istifi patlatma sırası bu örnekte beklediğim gibi değil. Önce ağaçtaki en derin yuvalanmış görünür parçayı bulmanız ve bunun üzerine arka yığını açmaya çalışmanız gerektiğine inanıyorum, ardından ağaçta kademeli olarak yukarı çıkın ve bir tane elde edene kadar arka yığını patlatabileceğiniz diğer görünür parçaları arayın. En basit vakaları ele alan hızlı bir düzeltme, onBackPressed (FragmentManager fm) işlevini değiştirmek ve arka yığınları açan bloğu, parçaları numaralandıran blokla değiştirmektir. Böylelikle, daha derinlemesine iç içe geçmiş çocuk parçalarında arka yığını patlatmak ilk olarak gerçekleşecek
Theo

1
Ayrıca, her biri ekranın bir bölümünü kaplayabilecek alt parçalarına sahip çok sayıda iç içe geçmiş parçaya sahip olduğunuzda, her alt parça yöneticisi için ayrı bir arka yığın olması fikri mantıklı gelmez. Backstack, etkinlikteki tüm parçalar arasında birleştirilmeli ve parçanın iç içe geçme düzeyine bakılmaksızın, parça değişikliklerini meydana gelme sırasına göre izlemelidir.
Theo

Bu çözümü SupportFragmentManagersadece Standart olmadan ve bunun yerine kullanmak mümkün müdür FragmentManager?
Peter Chappy

13

Bu yanıtla, özyinelemeli geri denetimi ele alacak ve her parçaya varsayılan davranışı geçersiz kılma şansı verecektir. Bu, bir ViewPager'ı barındıran bir parçanın, arka yığın olarak sayfaya kaydırma gibi özel bir şey yapabileceği veya ana sayfaya kaydırıp bir sonraki geri çıkışa basabileceğiniz anlamına gelir.

Bunu, AppCompatActivity'yi genişleten Aktivitenize ekleyin.

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

Bunu BaseFragment'ınıza veya tüm parçalarınızın devralınacağı sınıfa ekleyin.

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;
}

Bu harika bir cevap, API seviyesi 23/24 üzerinde ve lib 24.1.1 desteği ile çalışıyor
Rinav

Bunu bir süredir kullanıyorum, bu harika bir çözüm - ancak son zamanlarda kütüphane grubunun dışından getFragments () öğesine doğrudan erişim hakkında derleme uyarıları alıyorum. Zaten bir çözüm ile güncellediniz mi?
Simon Huckett

düzeltilen bir şey mi yoksa 2020'de hala sorunumuz var ve çözümünüzü uygulamalıyız ?? !!
Zhar

3

Bu kod, parça yöneticileri ağacında gezinecek ve yığından çıkarabileceği herhangi bir parçaya sahip son eklenmiş olanı döndürecektir:

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;
}

Yöntemi çağırın:

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

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

  super.onBackPressed();
}

Bu, doğru şekilde kaydedilmişse tüm iç içe geçmiş parçalar için çalışır! +100
Pratik Saluja

2

Bunun nedeni, Aktivitenizin GERİ tuşuna basmayı işleyen FragmentActivity'den kaynaklanmasıdır ( FragmentActivity'nin 173. satırına bakın .

Uygulamamızda, bir ViewPager (parçalı) kullanıyorum ve her parça iç içe geçmiş parçalara sahip olabilir. Bunu halletme şeklim:

  • Tek bir yöntemle OnBackKeyPressedListener arabirimi tanımlama void onBackKeyPressed()
  • bu arayüzü ViewPager'ın gösterdiği "üst" parçalara uyguladı
  • onKeyDown'u geçersiz kılmak ve BACK tuşuna basmayı algılamak ve onBackKeyPressed'i görüntüleme sayfasındaki şu anda aktif bir parçada çağırmak.

Ayrıca getChildFragmentManager()parçaları düzgün şekilde yerleştirmek için parçalarda kullandığımı da unutmayın . Bu android geliştiricileri gönderisinde bir tartışma ve açıklama görebilirsiniz .


2

Bir parçada parça kullanıyorsanız o zaman kullanın getChildFragmentManager()

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

alt parçayı kullanıyorsanız ve değiştirin getParentFragmentManager()

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

İkiniz de sizin için çalışmıyorsa bunu deneyin getActivity().getSupportFragmentManager()

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

1

onCreate View () yönteminde bu yöntemi ana parçaya ekleyerek ve kök görünümü geçerek parça geri yığınını işleyebildim.

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;
        }
    } );
}

İsEnableFragmentBackStack () yöntemi, ana parçada veya sonraki parçada olduğumda bilinmesi gereken bir boole bayrağıdır.

Yığının olması gereken parçayı tamamladığınızda, addToBackstack Yöntemini eklemeniz gerektiğinden emin olun.


1

Bu çözüm daha iyi olabilir çünkü iç içe geçmiş parçaların tüm seviyelerini kontrol eder:

 /**
 * 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;
}

aktivitenizde onBackPressedbunu basitçe şöyle adlandırabilirsiniz:

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

Yardımları için herkese teşekkürler, bu (ince ayarlanmış sürüm) benim için çalışıyor:

@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;
}

NOT: Muhtemelen bu iç içe geçmiş parçaların arka plan rengini, varsayılan olarak saydam oldukları için uygulama temasının pencere arka plan rengine ayarlamak isteyeceksiniz. Bir şekilde bu sorunun kapsamı dışındadır, ancak android.R.attr.windowBackground özniteliğini çözerek ve Parça görünümünün arka planını bu kaynak kimliğine ayarlayarak gerçekleştirilir.


1

5 yıldan fazla ve bu konu hala geçerli. Kısıtlaması nedeniyle fragmentManager.getFragments () öğesini kullanmak istemiyorsanız. Aşağıdaki sınıfları genişletin ve kullanın:

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());
        }
    }
}

Açıklama:

Yukarıdaki sınıflardan genişletilen her etkinlik ve parça, her bir çocuğu ve çocuklarının çocukları için kendi sırt yığınını yönetir. Backstack, yalnızca "etkin parça" etiketlerinin / kimliklerinin bir kaydıdır. Bu nedenle, uyarı, parçanız için her zaman bir etiket ve / veya kimlik sağlamaktır.

ChildFragmentManager'da backstack'e ekleme yaparken, "addToParentBackStack ()" de çağırmanız gerekecektir. Bu, parçanın etiketinin / kimliğinin daha sonraki çıkarlar için ana parçaya / etkinliğe eklenmesini sağlar.

Misal:

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

1

Şimdiye kadar kimse cevap bulamadıysa doğru uyguladım

sadece bu yöntemi çocuğunuzun iç içe geçmiş parçasına ekleyin

   @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

Bu sorunu şu anda açık olan parçayı faaliyet özelliğinde tutarak çözdüm. Sonra yöntemi geçersiz kılıyorum

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

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

Şu anda açık olan parçaya kendi içinden alt parçayı bu şekilde ekliyorum.

 // 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()
    }

Bu, ana parçanın ve eklenen parçanın xml düzenidir

 <?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

Burada sunulan bazı çözümleri gözlemledikten sonra, üst parça için yığını açarken veya geri eylemin ne zaman yok sayılması gerektiği konusunda esneklik ve kontrole izin vermek için böyle bir uygulamayı kullanmayı tercih ettiğimi keşfettim:

Bir "ParentFragment" arayüzü tanımlama:

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

}

Üst etkinlikte (veya BaseActivity'de) "onBackPressed" öğesinin geçersiz kılınması:

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

Ve sonra ana parçanın istediği gibi işlemesine izin verin, örneğin:

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
}

Bu, örneğin bir görüntüleme çağrı cihazı kullanırken (ve arka yığın giriş sayısı> 0) kaldırılacak bazı meşru alt parçaların olduğunu düşünmekten kaçınmaya izin verir.


-1

Eğer bir varsa DialogFragmentsırayla parçaları iç içe olan, 'geçici çözüm' biraz farklıdır. Bir ' onKeyListenere ayarlamak yerine, rootViewbunu Dialog. Ayrıca bir DialogInterface.OnKeyListenerdeğil bir de Viewkuracaksınız. Tabii ki, unutmayın addToBackStack!

Btw, etkinliğe geri aramayı delege etmek için arka planda 1 parçaya sahip olmak benim kişisel kullanım durumum. Sayımın 0 olması tipik senaryolar olabilir.

İşte onCreateDialog içinde yapmanız gerekenler

    @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;
    }

Gerçekten anlaşılabilir değil veya tamamlanmamış. DialogFragment veya onun üst sınıfı Fragmentinde setOnKeyListener yok.
Ixx

@Ixx Dinleyiciyi açık Dialog(değil DialogFragment) ayarlamalısınız ve kullandığınız dinleyici DialogInterface.OnKeyListener- kod çalışıyor ve tamamlanmış (ve kullanımda). Neden durumunuzu sağlamıyorsunuz ve belki yardımcı olabilirim.
Sachin Ahuja

-1

ChildFragments için bu işe yarar ..

@Override
    public void onBackPressed() {

 if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            getSupportFragmentManager().popBackStack();
        } else {
            doExit(); //super.onBackPressed();
        }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.