android fragment- Üstüne başka bir parça itildiğinde parçaların görünüm durumlarını kaydetme


137

Android'de FragAbackstack'a bir parça (say ) eklenir ve başka bir parça (say FragB) en üste gelir. Şimdi geri vurmak FragAüstüne geliyor ve onCreateView()denir. Şimdi daha FragAönce belli bir durumdaydım FragB.

Benim sorum FragAönceki durumuna nasıl geri dönebilirim? Durumu kaydetmenin bir yolu var mı (örneğin bir Pakette söyleyin) ve eğer öyleyse hangi yöntemi geçersiz kılmalıyım?

Yanıtlar:


98

In fragman kılavuz FragmentList örneğin şunları bulabilirsiniz:

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("curChoice", mCurCheckPosition);
}

Hangi daha sonra böyle kullanabilirsiniz:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (savedInstanceState != null) {
        // Restore last state for checked position.
        mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
    }
}

Ben Fragments bir acemi değilim ama sorunun çözümü gibi görünüyor;) OnActivityCreated parça arka yığın döndükten sonra çağrılır.


18
KaydedilenInstanceState her zaman boştu çalışmak için bu alınamadı. Ben xml düzeni ile parça ekliyorum. MCurCheckPosition'ı statik olarak değiştirmek zorunda kaldı, o zaman işe yarıyor, ama hacky hissediyor.
scottyab

57
onSaveInstanceState çağırmıyor - neden? Yani, bu yaklaşım işe yaramıyor.
gece yarısı

10
Aynı Faaliyetteki başka bir parçadan dönerken parça durumunu korumak istediğimizde bu yaklaşım gerçekten işe yarar mı? onSaveInstanceState () yalnızca Activity onPause / onStop olaylarında çağrılır. Belgelere göre: "Ayrıca bir etkinlik gibi, etkinliğin sürecinin öldürülmesi ve bir etkinlik yeniden oluşturulduğunda parça durumunu geri yüklemeniz gerekirse, bir Paket kullanarak bir parçanın durumunu koruyabilirsiniz. parçanın onSaveInstanceState () geri çağrısı ve onCreate (), onCreateView () veya onActivityCreated () sırasında geri yükleyin. "
Paramvir Singh

26
Kayıt için bu yaklaşım yanlıştır ve sahip olduğu oyların yakınında hiçbir yere sahip olmamalıdır. onSaveInstanceStateyalnızca ilgili etkinlik de kapatıldığında çağrılır.
Martin Konecny

16
Yapılandırma değişiklikleri gerçekleştiğinde ve etkinlik yok edildiğinde onSaveInstanceState () onle olarak adlandırılır, bu cevap yanlıştır
Tadas Valaitis

83

Fragment en onSaveInstanceState(Bundle outState)kendisinin ve ekli parçaları üzerinde veridiliminin etkinlik çağrısı bunu sürece çağrılacak asla. Bu nedenle, bir şey (tipik olarak rotasyon) aktiviteyi SaveInstanceStatedaha sonra zorlamak ve geri yüklemek için bu yöntem çağrılmaz . Ancak yalnızca bir etkinliğiniz ve içinde büyük bir parça kümeniz varsa (yoğun kullanımıyla replace) ve uygulama yalnızca bir yönlendirme etkinliğinde çalıştırılırsa onSaveInstanceState(Bundle outState), uzun süre çağırılamayabilir.

Üç olası çözümü biliyorum.

İlk:

önemli verileri tutmak için parçanın argümanlarını kullanın:

public class FragmentA extends Fragment {
    private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable";

    private EditText persistentVariableEdit;

    public FragmentA() {
        setArguments(new Bundle());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_a, null);

        persistentVariableEdit = (EditText) view.findViewById(R.id.editText);

        TextView proofTextView = (TextView) view.findViewById(R.id.textView);

        Bundle mySavedInstanceState = getArguments();
        String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);

        proofTextView.setText(persistentVariable);


        view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager()
                        .beginTransaction()
                        .replace(R.id.frameLayout, new FragmentB())
                        .addToBackStack(null)
                        .commit();
            }
        });

        return view;
    }

    @Override
    public void onPause() {
        super.onPause();
        String persistentVariable = persistentVariableEdit.getText().toString();

        getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable);
    }
}

İkinci fakat daha az bilgiçlik yolu - değişkenleri tekil olarak tutma

Üçüncüsü - replace()parçalara ayırmayın, onun yerine add()/ show()/ hide().


3
En iyi çözüm Fragment.onSaveInstanceState()asla çağrılmadı. Liste verilerinizdeki öğeler veya yalnızca kimlikleri de dahil olmak üzere kendi verilerinizi bağımsız değişkene kaydedin (başka bir merkezi veri yöneticiniz varsa). Liste görünümünün konumunu kaydetmeye gerek yok - bu kaydedildi ve otomatik olarak geri yüklendi.
John Pang

Örneğini uygulamamda kullanmaya çalıştım, ancak bu: String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);her zaman null. Sorun ne?
fragon

Fragman kullanması getArguments()kesinlikle bir ViewPager içinde yuvalanmış fragmanları dahil, gitmek yoludur. 1 etkinlik kullanıyorum ve birçok parçayı içeri / dışarı değiştiriyorum ve bu mükemmel çalışıyor. Önerilen herhangi bir çözümü incelemeniz için basit bir test: 1) Parça A'dan Parça B'ye gidin; 2) cihaz yönünü iki kez değiştirin; 3) cihazdaki geri düğmesine basın.
Andy H.

İlk yaklaşımı denedim ama benim için işe yaramadı. getArguments () her zaman null değerini döndürür. Parça da değiştirildiğinden ve onCreate () 'de eski Paketin kaybolması için yeni bir Paket ayarladınız. Neyi özlüyorum ve yanıldım mı?
Zvi

@Zvi, 1,5 yıl önce bu kodu yazdım ve tüm ayrıntıları hatırlamıyorum, ancak hatırladığım gibi, yerine sadece kodunuzdan yeni bir örnek oluşturduysanız, parçaları yeniden oluşturmaz. Bu durumda, tabii ki, yapıcı setArguments(new Bundle());eski Bundle'ı çağırdı ve üzerine yazdı. Bu nedenle, yalnızca bir kez parça oluşturduğunuzdan emin olun ve her seferinde yeni bir tane oluşturmak yerine bu örneği kullanın.
Fyodor Volchyok

20

ViewPager kullanarak Fragments ile çalışıyorsanız, oldukça kolay olduğunu unutmayın. Yalnızca bu yöntemi çağırmak gerekir: setOffscreenPageLimit().

Belgelere göre:

Görünüm hiyerarşisinde geçerli sayfanın her iki tarafında tutulması gereken sayfa sayısını boş durumda ayarlayın. Bu sınırın ötesindeki sayfalar gerektiğinde bağdaştırıcıdan yeniden oluşturulacaktır.

Burada benzer sayı


5
Bu farklı. setOffScreenPageLimit bir önbellek görevi görür (ViewPager'in belirli bir anda kaç sayfa işlemesi gerektiği anlamına gelir), ancak bir Parçanın durumunu kaydetmek için kullanılmaz.
Renaud Mathieu

Benim durumum setOffscreenPageLimit () ile çalıştı - parçaları yok olmasına rağmen, görünüm durumu kaydedildi ve geri yüklendi.
Davincho

Teşekkürler, bana da yardımcı oldu.
Elijah

Yıllar sonra ve bu hala çok alakalı. Soruyu gerçekten cevaplamasa da, bir sorunu çözüyor
Supreme Dolphin

19

Görüşünüzü bir kez şişirmeniz yeterlidir.

Örnek şu:

public class AFragment extends Fragment {

private View mRootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if(mRootView==null){
        mRootView = inflater.inflate(R.id.fragment_a, container, false);
        //......
    }
    return mRootView;
}

}


ayrıca mevcut parçaları dizide veya başka bir şeyde
Amir

Bir parçanın rootView referansını tutmak kötü bir uygulama olduğunu duydum - sızıntılara neden olabilir [alıntı gerekli]?
giraffe.guru

1
@ giraffe.guru Fragmentkök görünümünün bunu yapmayacağını ifade eder. Bazı GC-kök öğelerinin referansı , global statik özellik gibi, ui olmayan iş parçacığı değişkeni olacaktır. Bir Fragmentörnek GC kökü değildir, bu yüzden çöp toplanabilir. Kök görünümü de öyle.
Lym Zoy

Benim günüm aynısın.
Vijendra patidar

Tatlı ve basit.
Rupam Das

8

Buna çok benzer bir konuyla çalıştım. Sık sık önceki bir parçaya geri döneceğimi bildiğim için parçanın .isAdded()doğru olup olmadığını kontrol ettim ve eğer öyleyse yapmak yerine transaction.replace()sadece bir yapıyorum transaction.show(). Bu, parçanın zaten yığın üzerindeyse yeniden oluşturulmasını önler - durum tasarrufu gerekmez.

Fragment target = <my fragment>;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if(target.isAdded()) {
    transaction.show(target);
} else {
    transaction.addToBackStack(button_id + "stack_item");
    transaction.replace(R.id.page_fragment, target);
}
transaction.commit();

Akılda tutulması gereken başka bir şey, bu, fragmanların kendileri için doğal düzeni korurken, yine de oryantasyon (yapılandırma) değişikliğinde yok edilen ve yeniden yaratılan aktivitenin kendisini ele almanız gerekebilir. Düğümünüz için AndroidManifest.xml dosyasında bu sorunu çözmek için:

android:configChanges="orientation|screenSize"

Android 3.0 ve sonraki sürümlerde, screenSizegörünüşe göre gereklidir.

İyi şanslar


transaction.addToBackStack (button_id + "stack_item"); // bu satır ne işe yarar. burada button_id nedir?
raghu_3

button_id sadece uydurma bir değişkendir. AddToBackStack öğesine iletilen dize bağımsız değişkeni, backstack durumu için yalnızca isteğe bağlı bir addır - yalnızca tek bir backstack'i yönetiyorsanız null olarak ayarlayabilirsiniz.
rmirabelle

, parçaları ile buna benzer bir sorun yaşıyorum, lütfen içine bakabilir
misiniz-

1
android:configChanges=everythinYouCanThinkOf|moreThingsYouFoundOnTheInternetsManifestinize asla ekleme . Bunun yerine durumu nasıl kaydedeceğinizi ve geri yükleyeceğinizi öğrenin, örneğin buradan: speakerdeck.com/cyrilmottier/…
Marcin Koziński

Ve sonra, ne kadar çirkin tasarruf durumunun olduğunu ve sunulan çözümün, kendi durumunuzdaki sorunu en temiz şekilde çözdüğünü öğrendikten sonra, devam etmeyi ve bunu kabul etmeyenlerin downvotes için endişe duymadan kullanın ;-)
rmirabelle

5

Bulduğum En İyi Çözüm aşağıda:

onSavedInstanceState (): etkinlik kapanırken her zaman parça içinde çağrılır (Etkinliği birinden diğerine taşı veya yapılandırma değişir). Dolayısıyla, aynı aktivite üzerinde birden fazla parça çağırıyorsak, aşağıdaki yaklaşımı kullanmalıyız:

Parçanın OnDestroyView () yöntemini kullanın ve tüm nesneyi bu yöntemin içine kaydedin. Sonra OnActivityCreated (): Nesnenin boş olup olmadığını kontrol edin (Çünkü bu yöntem her seferinde çağırır). Şimdi burada bir nesnenin durumunu geri yükleyin.

Her zaman çalışır!


4

android manifest'te belirtilen fragman etkinliğinizdeki yapılandırma değişikliklerini bu şekilde yönetiyorsanız

<activity
    android:name=".courses.posts.EditPostActivity"
    android:configChanges="keyboardHidden|orientation"
    android:screenOrientation="unspecified" />

Daha sonra onSaveInstanceStatefragmanın çağrılan olmayacak ve savedInstanceStatenesne daima boş olacaktır.


1

sanmıyorum onSaveInstanceStateiyi bir çözüm. sadece arzu edilen faaliyet için kullanılır.

Android 3.0'dan Fragmen, FragmentManager tarafından yönetildi, durum: bir aktivite eşleştirme manny fragmanları, parça eklendiğinde (değiştirilmeyecek: yeniden oluşturulacak), görünüm bozulacak. sonuncusuna geri döndüğünde, önceki gibi görüntülenecektir.

Bu yüzden fragmentManger ve işlemin üstesinden gelebilecek kadar iyi olduğunu düşünüyorum.


0

Liste görünümü içeren parçalar için karma bir yaklaşım kullandım. Şu anki parçayı değiştirmediğimden ziyade yeni parçayı eklediğimden ve şu anki parçayı gizlediğim için performans sergiliyor gibi görünüyor. Parçalarımı barındıran etkinlikte aşağıdaki yöntem var:

public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.setCustomAnimations(0,0,0,0);
    transaction.hide(currentFragment);
    // use a fragment tag, so that later on we can find the currently displayed fragment
    transaction.add(R.id.frame_layout, targetFragment, tag)
            .addToBackStack(tag)
            .commit();
}

Bir liste öğesi tıklatıldığında / dokunduğunda parçamda (liste görünümünü içeren) bu yöntemi kullanırım (ve bu nedenle ayrıntıları parçayı başlatmam / görüntülemem gerekir):

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]);
DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details");
((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details");

getFragmentTags()yeni bir parça eklediğimde farklı parçalar için etiket olarak kullandığım dizeler dizisini döndürür ( yukarıdaki transaction.addyöntemde addFragmentyönteme bakınız).

Liste görünümünü içeren parça, bunu onun onPause () yönteminde yaparım:

@Override
public void onPause() {
    // keep the list view's state in memory ("save" it) 
    // before adding a new fragment or replacing current fragment with a new one
    ListView lv =  (ListView) getActivity().findViewById(R.id.listView);
    mListViewState = lv.onSaveInstanceState();
    super.onPause();
}

Sonra parçanın onCreateView (aslında onCreateView içinde çağrılan bir yöntemde), ben durumu geri:

// Restore previous state (including selected item index and scroll position)
if(mListViewState != null) {
    Log.d(TAG, "Restoring the listview's state.");
    lv.onRestoreInstanceState(mListViewState);
}

0

Sonunda bu karmaşık çözümlerin çoğunu denedikten sonra sadece Parçamdaki tek bir değeri (bir EditText içeriği) kaydetmem / geri yüklemem gerektiğinden ve en şık çözüm olmasa da, bir SharedPreference oluşturup durumumu saklamak zorunda kaldım benim için çalıştı


0

Bir aktivitedeki farklı parçalardaki alanların değerlerini tutmanın basit bir yolu

Parça Örneklerini oluşturun ve değiştir ve kaldır yerine ekleyin

    FragA  fa= new FragA();
    FragB  fb= new FragB();
    FragC  fc= new FragB();
    fragmentManager = getSupportFragmentManager();
    fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.add(R.id.fragmnt_container, fa);
    fragmentTransaction.add(R.id.fragmnt_container, fb);
    fragmentTransaction.add(R.id.fragmnt_container, fc);
    fragmentTransaction.show(fa);
    fragmentTransaction.hide(fb);
    fragmentTransaction.hide(fc);
    fragmentTransaction.commit();

Ardından, parçaları tekrar eklemek ve kaldırmak yerine parçaları gösterin ve gizleyin

    fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.hide(fa);
    fragmentTransaction.show(fb);
    fragmentTransaction.hide(fc);
    fragmentTransaction.commit()

;


-1
private ViewPager viewPager;
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageSelected(int position) {
            // on changing the page
            // make respected tab selected
            actionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    });
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    // on tab selected
    // show respected fragment view
    viewPager.setCurrentItem(tab.getPosition());
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}

5
Lütfen yalnızca kod göndermek yerine cevabınızla ilgili bazı bilgiler eklemeyi düşünün. Sadece 'düzeltmeler' sağlamakla kalmıyor, aynı zamanda insanların öğrenmesine yardımcı oluyoruz. Orijinal kodda neyin yanlış olduğunu, farklı olarak ne yaptığınızı ve değişikliklerinizin neden işe yaradığını açıklamalısınız.
Andrew Barber
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.