Arka yığından devam eden parçalar


96

Fragments'ı Android 2.2 ile kullanmak için uyumluluk paketini kullanıyorum. Parçaları kullanırken ve aralarında arka yığına geçişler eklerken, bir etkinliğin onResume ile aynı davranışını elde etmek isterim, yani, bir parçadan çıktıktan sonra "ön plana" (kullanıcı tarafından görünür) getirildiğinde Backstack, parça içinde bir tür geri aramanın etkinleştirilmesini istiyorum (örneğin, paylaşılan bir UI kaynağında belirli değişiklikleri gerçekleştirmek için).

Parça çerçevesi içinde yerleşik bir geri arama olmadığını gördüm. Bunu başarmak için iyi bir uygulama var mı?


13
Harika soru. Bu senaryo için bir kullanım örneği olarak hangi parçanın görünür olduğuna bağlı olarak eylem çubuğu başlığını değiştirmeye çalışıyorum ve bana API'de eksik bir geri arama gibi görünüyor.
Manfred Moser

3
Aktiviteniz, herhangi bir zamanda hangi parçaların görüntülendiğini bildiği için başlığı ayarlayabilir. Ayrıca onCreateViewbir poptan sonra fragman için çağrılacak bir şey yapabileceğinizi düşünüyorum .
PJL

1
@PJL +1 Referansa göre yapılması gereken budur. Yine de dinleyici yolunu tercih ederim
momo

Yanıtlar:


116

Daha iyi bir çözüm olmadığından, bunu benim için çalıştırdım: 1 etkinliğim (MyActivity) ve birbirinin yerine geçen birkaç parçam olduğunu varsayın (bir seferde yalnızca biri görünür).

MyActivity'de bu dinleyiciyi ekleyin:

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(Gördüğünüz gibi uyumluluk paketini kullanıyorum).

getListener uygulaması:

private OnBackStackChangedListener getListener()
    {
        OnBackStackChangedListener result = new OnBackStackChangedListener()
        {
            public void onBackStackChanged() 
            {                   
                FragmentManager manager = getSupportFragmentManager();

                if (manager != null)
                {
                    MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);

                    currFrag.onFragmentResume();
                }                   
            }
        };

        return result;
    }

MyFragment.onFragmentResume()"Geri" tuşuna basıldıktan sonra çağrılacaktır. yine de birkaç uyarı:

  1. Tüm işlemleri backstack'e eklediğinizi varsayar (kullanarak FragmentTransaction.addToBackStack())
  2. Her yığın değişikliğinde etkinleştirilir (arka yığında animasyon gibi başka şeyler de saklayabilirsiniz), böylece aynı parça örneği için birden fazla çağrı alabilirsiniz.

3
Sizin için işe yaradıysa kendi cevabınızı doğru kabul etmelisiniz.
powerj1984

7
Sorguladığınız parça kimliği açısından bu nasıl çalışır? Her parça için nasıl farklıdır ve doğru olan arka yığında bulunur?
Manfred Moser

2
@Warpzit bu yöntem çağrılmaz ve android dokümanlar sessizce onun asla çağrılmayacağını itiraf eder (sadece aktivite devam ettirildiğinde çağrılır - bu aktivite zaten aktifse asla olmaz)
Adam

2
Bunu yapmak zorunda olmamız çok saçma! Ancak başka birinin bu "özelliği" bulabilmesine sevindim
stevebot

3
onResume () çağrılmamış
Marty Miller

33

Önerilen çözümü biraz değiştirdim. Benim için böyle daha iyi çalışıyor:

private OnBackStackChangedListener getListener() {
    OnBackStackChangedListener result = new OnBackStackChangedListener() {
        public void onBackStackChanged() {
            FragmentManager manager = getSupportFragmentManager();
            if (manager != null) {
                int backStackEntryCount = manager.getBackStackEntryCount();
                if (backStackEntryCount == 0) {
                    finish();
                }
                Fragment fragment = manager.getFragments()
                                           .get(backStackEntryCount - 1);
                fragment.onResume();
            }
        }
    };
    return result;
}

1
lütfen farkı açıklayın ve bunu neden kullanın.
Math chiller

2
Benim çözümüm ile önceki çözüm arasındaki fark, parça yığınına ekleme, değiştirme veya geri gitme gibi her parça değişikliğinde üst parçanın "onResume" yönteminin çağrılmasıdır. Bu yöntemde kodlanmış parça kimliği yoktur. Temel olarak, hafızaya yüklenmiş olup olmadığına bakılmaksızın, her değişiklikte baktığınız parçadaki onResume () işlevini çağırır.
Brotoo25

9
getFragments()SupportFragmentManager'da çağrılan bir yöntem için kayıt yok ... -1: - /
Protostome

1
OnResume () Çağrıldı.Ama Boş Ekran Görüyorum .. Bu sorunu çözmenin başka bir yolu var mı?
kavie

BackStackEntryCount 0 ise bunun ArrayOutOfBounds İstisnasına yol açtığını düşünüyorum. Hatalı mıyım? Gönderiyi düzenlemeye çalıştı ancak reddedildi.
Mike T

6

A'dan sonra popStackBack()aşağıdaki geri aramayı kullanabilirsiniz: parçanızın onHiddenChanged(boolean hidden)içinde


2
Bu, uygulama uyumluluk parçalarıyla çalışmıyor gibi görünüyor.
Lo-Tan

Ben öyle kullandım. Teşekkürler!
Albert Vila Calvo

En basit çözüm! Parçanın şu anda görünür olup olmadığını bir kontrol edin ve işte, appbar başlığını ayarlayabilirsiniz.
EricH206

4

Aşağıdaki Android Geliştiricileri bölümünde, etkinliğe olay geri aramaları oluşturma bir iletişim mekanizması açıklanmaktadır . Ondan bir satır alıntı yapmak için:

Bunu yapmanın iyi bir yolu, parçanın içinde bir geri arama arabirimi tanımlamak ve ana bilgisayar etkinliğinin bunu uygulamasını gerektirmektir. Etkinlik arabirim aracılığıyla bir geri arama aldığında, bilgileri gerektiği gibi düzendeki diğer parçalarla paylaşabilir.

Düzenleme: Parça, onStart(...)kullanıcı tarafından görüldüğünde çağrılan bir parçaya sahiptir . Benzer şekilde onResume(...)görünür ve aktif olarak çalışırken. Bunlar faaliyet meslektaşlarına bağlıdır. Kısaca: kullanımonResume()


1
Ben sadece Fragment sınıfında kullanıcıya her gösterildiğinde etkinleştirilen bir yöntem arıyorum. FragmentTransaction.add () / replace () veya FragmentManager.popBackStack () nedeniyle olsun.
oriharel

@oriharel Güncellenen yanıta bakın. Aktivite yüklendiğinde, onAttach, onCreate, onActivityCreated, onStart, onResume gibi çeşitli çağrılar alacaksınız. `` SetRetainInstance (true) '' çağrısı yaptıysanız, geri tıklandığında parça yeniden gösterildiğinde onCreate alamazsınız.
PJL

15
onStart (), aynı etkinlikteki parçalar arasında "Geri" tıklandığında asla çağrılmaz. Belgelerdeki geri aramaların çoğunu denedim ve hiçbiri böyle bir senaryoda çağrılmadı. bir çözüm için cevabıma bakın.
oriharel

uyumlu lib v4 ile hmm Parçam için bir onResume görüyorum. @ Oriharel'in cevabını beğeniyorum, çünkü sadece ön plana getirilmek yerine bir popBackStack aracılığıyla görünür hale gelmeyi ayırt ediyor.
PJL

3

Bir parça arka diziye yerleştirilirse, Android onun görünümünü yok eder. Parça örneğinin kendisi öldürülmez. Başlamanın basit bir yolu, onViewCreated olayını dinlemektir, sizi oraya "onResume ()" mantığını koyar.

boolean fragmentAlreadyLoaded = false;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null && !fragmentAlreadyLoaded) {
            fragmentAlreadyLoaded = true;

            // Code placed here will be executed once
        }

        //Code placed here will be executed even when the fragment comes from backstack
    }

3

OnCreate () etkinliğimde

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

Belirli Parçayı yakalamak ve onResume () öğesini çağırmak için bu yöntemi kullanın.

private FragmentManager.OnBackStackChangedListener getListener()
    {
        FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
                if (currentFragment instanceof YOURFRAGMENT) {
                    currentFragment.onResume();
                }
            }
        };

        return result;
    }

2

Biraz geliştirildi ve bir yönetici çözümüne sarıldı.

Akılda tutulması gereken şeyler. FragmentManager bir tekil değildir, sadece Aktivite içindeki Parçaları yönetir, bu nedenle her aktivitede yeni olacaktır. Ayrıca, bu çözüm şimdiye kadar ViewPager'ı dikkate almıyor, setUserVisibleHint () yöntemini çağırarak Parçaların görünürlüğünü kontrol etmeye yardımcı oluyor.

Bu sorunla uğraşırken aşağıdaki sınıfları kullanmaktan çekinmeyin (Dagger2 enjeksiyonunu kullanır). Aktivitede Çağrı:

//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager(); 
myFragmentBackstackStateManager.apply(fragmentManager);

FragmentBackstackStateManager.java:

@Singleton
public class FragmentBackstackStateManager {

    private FragmentManager fragmentManager;

    @Inject
    public FragmentBackstackStateManager() {
    }

    private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
        @Override
        public void onFragmentPushed(Fragment parentFragment) {
            parentFragment.onPause();
        }

        @Override
        public void onFragmentPopped(Fragment parentFragment) {
            parentFragment.onResume();
        }
    };

    public FragmentBackstackChangeListenerImpl getListener() {
        return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
    }

    public void apply(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        fragmentManager.addOnBackStackChangedListener(getListener());
    }
}

FragmentBackstackChangeListenerImpl.java:

public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {

    private int lastBackStackEntryCount = 0;
    private final FragmentManager fragmentManager;
    private final BackstackCallback backstackChangeListener;

    public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
        this.fragmentManager = fragmentManager;
        this.backstackChangeListener = backstackChangeListener;
        lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
    }

    private boolean wasPushed(int backStackEntryCount) {
        return lastBackStackEntryCount < backStackEntryCount;
    }

    private boolean wasPopped(int backStackEntryCount) {
        return lastBackStackEntryCount > backStackEntryCount;
    }

    private boolean haveFragments() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList != null && !fragmentList.isEmpty();
    }


    /**
     * If we push a fragment to backstack then parent would be the one before => size - 2
     * If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
     *
     * @return fragment that is parent to the one that is pushed to or popped from back stack
     */
    private Fragment getParentFragment() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList.get(Math.max(0, fragmentList.size() - 2));
    }

    @Override
    public void onBackStackChanged() {
        int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
        if (haveFragments()) {
            Fragment parentFragment = getParentFragment();

            //will be null if was just popped and was last in the stack
            if (parentFragment != null) {
                if (wasPushed(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPushed(parentFragment);
                } else if (wasPopped(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPopped(parentFragment);
                }
            }
        }

        lastBackStackEntryCount = currentBackStackEntryCount;
    }
}

BackstackCallback.java:

public interface BackstackCallback {
    void onFragmentPushed(Fragment parentFragment);

    void onFragmentPopped(Fragment parentFragment);
}

0

Bu, parçanın aktiviteye eklenmesi koşuluyla onResume () 'yi çağırabileceğiniz doğru cevaptır. Alternatif olarak onAttach ve onDetach'ı kullanabilirsiniz.


1
Örnek Gönderebilir misiniz Lütfen?
kavie

0

onResume () parça için iyi çalışıyor ...

public class listBook extends Fragment {

    private String listbook_last_subtitle;
...

    @Override
       public void onCreate(Bundle savedInstanceState) {

        String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
        listbook_last_subtitle = thisFragSubtitle;
       }
...

    @Override
        public void onResume(){
            super.onResume();
            getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
        }
...

0
public abstract class RootFragment extends Fragment implements OnBackPressListener {

 @Override
 public boolean onBackPressed() {
  return new BackPressImpl(this).onBackPressed();
 }

 public abstract void OnRefreshUI();

}


public class BackPressImpl implements OnBackPressListener {

 private Fragment parentFragment;

 public BackPressImpl(Fragment parentFragment) {
  this.parentFragment = parentFragment;
 }

 @Override
 public boolean onBackPressed() {
  ((RootFragment) parentFragment).OnRefreshUI();
 }
}

ve son olarak RootFragment'tan Frament'inizin etkisini görmek için


0

Çözümüm, yeni başlığa ayarlamadan önce Parça'daki eylem çubuğunun geçerli başlığını almaktır. Böylelikle Fragman patladığında o başlığa geri dönebilirim.

@Override
public void onResume() {
    super.onResume();
    // Get/Backup current title
    mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
            .getTitle();
    // Set new title
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(R.string.this_fragment_title);
}

@Override
public void onDestroy() {
    // Set title back
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(mTitle);

    super.onDestroy();
}

0

FragmentTagsTüm parça sınıflarımı tanımlamak için enum kullandım .

TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)

geçmesi FragmentTags.TAG_FOR_FRAGMENT_A.name()fragmanı etiketi olarak.

ve şimdi

@Override
public void onBackPressed(){
   FragmentManager fragmentManager = getFragmentManager();
   Fragment current
   = fragmentManager.findFragmentById(R.id.fragment_container);
    FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());

  switch(fragmentTag){
    case TAG_FOR_FRAGMENT_A:
        finish();
        break;
   case TAG_FOR_FRAGMENT_B:
        fragmentManager.popBackStack();
        break;
   case default: 
   break;
}
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.