Yön değişiklikleri üzerinden Android Fragment yaşam döngüsü


120

Fragments kullanarak 2.2'yi hedeflemek için uyumluluk paketini kullanma.

Bir uygulamada parçaları kullanmak için bir etkinliği yeniden kodladıktan sonra, yönelim değişikliklerini / durum yönetimini çalıştıramadım, bu yüzden tek bir FragmentActivity ve tek bir Fragment içeren küçük bir test uygulaması oluşturdum.

Oryantasyon değişikliklerinden gelen günlükler, OnCreateView parçalarına yapılan birden çok çağrı ile garip.

Açıkçası bir şeyi kaçırıyorum - yeni bir örnek oluşturmak yerine parçayı ayırmak ve yeniden eklemek gibi, ancak nerede yanlış yaptığımı gösteren herhangi bir belge göremiyorum.

Burada yanlış yaptığım şeye ışık tutan biri var mı lütfen? Teşekkürler

Oryantasyon değişikliklerinden sonra günlük aşağıdaki gibidir.

Initial creation
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 1
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 2
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null

Ana Etkinlik (FragmentActivity)

public class FragmentTestActivity extends FragmentActivity {
/** Called when the activity is first created. */

private static final String TAG = "FragmentTest.FragmentTestActivity";


FragmentManager mFragmentManager;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Log.d(TAG, "onCreate");

    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
}

Ve parça

public class FragmentOne extends Fragment {

private static final String TAG = "FragmentTest.FragmentOne";

EditText mEditText;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Log.d(TAG, "OnCreateView");

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false);

    // Retrieve the text editor, and restore the last saved state if needed.
    mEditText = (EditText)v.findViewById(R.id.editText1);

    if (savedInstanceState != null) {

        Log.d(TAG, "OnCreateView->SavedInstanceState not null");

        mEditText.setText(savedInstanceState.getCharSequence("text"));
    }
    else {
        Log.d(TAG,"OnCreateView->SavedInstanceState null");
    }
    return v;
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    Log.d(TAG, "FragmentOne.onSaveInstanceState");

    // Remember the current text, to restore if we later restart.
    outState.putCharSequence("text", mEditText.getText());
}

Belirgin

<uses-sdk android:minSdkVersion="8" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:label="@string/app_name"
        android:name=".activities.FragmentTestActivity" 
        android:configChanges="orientation">
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

Bunun doğru bir cevap olup olmadığını bilmiyorum, ancak parçayı eklerken bir etiket kullanmayı deneyin, ekleyin (R.id.fragment_container, parça, "MYTAG") veya başarısız olursa, değiştirin (R.id.fragment_container, fragment, "MYTAG ")
Jason

2
Bazı araştırmalar yapıyorum. Ana Aktivite (FragmentTestActivity) oryantasyon değişikliğinde yeniden başladığında ve yeni bir FragmentManager örneği elde ettiğimde, parçanın hala var olduğunu bulmak için bir FindFragmentByTag gerçekleştiriyorum, böylece parça ana aktivitenin yeniden oluşturulması üzerinde tutuluyor. Parçayı bulursam ve hiçbir şey yapmazsam, yine de MainActivity ile yeniden görüntülenir.
MartinS

Yanıtlar:


189

Parçalarınızı üst üste yerleştiriyorsunuz.

Bir yapılandırma değişikliği meydana geldiğinde, eski Parça, yeniden oluşturulduğunda kendisini yeni Etkinliğe ekler. Bu çoğu zaman arka tarafta büyük bir ağrıdır.

Yenisini yeniden oluşturmak yerine aynı Parçayı kullanarak oluşan hataları durdurabilirsiniz. Bu kodu eklemeniz yeterlidir:

if (savedInstanceState == null) {
    // only create fragment if activity is started for the first time
    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
} else {        
    // do nothing - fragment is recreated automatically
}

Yine de uyarılacaksınız: Yaşam döngüleri ince bir şekilde değişeceğinden, Etkinlik Görünümlerine Parçanın içinden erişmeye çalışırsanız sorunlar ortaya çıkacaktır. (Bir Parçadaki bir ana Etkinliğin Görüntülerini almak kolay değildir).


54
"Bu, çoğu zaman arkada çok büyük bir ağrıdır" (başparmak yukarı)
acele

1
ViewPage'in FragmentStatePagerAdapter ile kullanılması durumunda aynı senaryoyu nasıl ele alabilirim? Herhangi bir öneri?
CoDe

5
Resmi belgelerde benzer bir iddia var mı? Bu, rehberde belirtilenlerle bir çelişki değil "when the activity is destroyed, so are all fragments"mi? O zamandan beri "When the screen orientation changes, the system destroys and recreates the activity [...]".
cYrus

4
Cyrus - Hayır, Aktivite gerçekten yok edildi, içerdiği Parçalar sadece Aktiviteden değil FragmentManager'da referans alındı, bu yüzden kalır ve okunur.
Graeme

4
FragmentManager'da bulduktan sonra onCreate ve onDestroy yöntemlerinin yanı sıra hashcode'u günlüğe kaydetmek, parçanın yok edildiğini açıkça gösteriyor. otomatik olarak yeniden oluşturulur ve yeniden eklenir. sadece setRetainInstance'ı (true) onCreate yöntemine parçalara koyarsanız gerçekten yok edilmeyecek
Lemao1981

87

Bu kitaptan alıntı yapmak için , "tutarlı bir kullanıcı deneyimi sağlamak için Android, bir yapılandırma değişikliği nedeniyle bir Etkinlik yeniden başlatıldığında Parça düzenini ve ilişkili arka yığını sürdürür." (s. 124)

Ve buna yaklaşmanın yolu, önce Fragment arka yığının önceden doldurulup doldurulmadığını kontrol etmek ve yeni parça örneğini yalnızca aşağıdakileri yapmadıysa oluşturmaktır:

@Override
public void onCreate(Bundle savedInstanceState) {

        ...    

    FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 

    if (fragment == null) {
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.fragment_container, new FragmentOne());
        fragmentTransaction.commit();
    }
}

2
Muhtemelen bununla beni çok zaman kurtardın ... çok teşekkürler. Yapılandırma değişikliklerini ve parçalarını işlemek için mükemmel bir çözüm elde etmek için bu yanıtı Graeme'den gelen yanıtla birleştirebilirsiniz.
azpublic

10
Bu aslında doğru cevap, işaretli olan değil. Çok teşekkür ederim!
Uriel Frankel

ViewPager Fragment uygulaması durumunda aynı senaryoyu nasıl ele alabilirim.
CoDe

Bu küçük mücevher, birkaç gündür aradığım bir soruna yardımcı oldu. Teşekkür ederim! Çözüm kesinlikle bu.
whome

1
@SharpEdge Birden fazla parçanız varsa, kapsayıcıya eklerken bunlara etiketler vermelisiniz ve sonra bunlara referans almak için mFragmentManager.findFragmentByTag (findFragmentById yerine) kullanmalısınız - bu şekilde her parçanın sınıfını bilecek ve yapabileceksiniz. doğru şekilde yayın
k29

10

Etkinliğinizin onCreate () yöntemi, gördüğünüz yön değişikliğinden sonra çağrılır. Bu nedenle, aktivitenizdeki yönelim değişikliğinden sonra Fragmanı ekleyen FragmentTransaction'ı çalıştırmayın.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState==null) {
        //do your stuff
    }
}

Fragmanlar değiştirilmeli ve değiştirilmemelidir.


Parça oluşturulduktan ve eklendikten sonra örneğin kaydedileceğini biliyor muyuz? Demek istediğim, bir kullanıcı Fragment eklenmeden hemen önce dönerse ne olur? Hala parça durumunu içermeyen, null olmayan kaydedilmişInstanceState'e sahip olacağız
Farid

4

@OverrideFragmentActivity'yi kullanarak yapabilirsiniz onSaveInstanceState(). Lütfen super.onSaveInstanceState()yöntemde çağırmadığınızdan emin olun .


2
Bu, büyük olasılıkla faaliyetlerin yaşam döngüsünü bozarak, zaten oldukça karışık olan bu süreçte daha fazla potansiyel sorun ortaya çıkaracaktır. FragmentActivity'nin kaynak koduna bakın: oradaki tüm parçaların durumlarını kaydediyor.
Brian

Farklı yönelim için farklı adaptör sayım olması sorununu yaşadım. Bu yüzden cihazı çevirip bazı sayfaları kaydırdıktan sonra her zaman garip bir durum yaşadım, eski ve yanlış olanı aldım. kaydedilenInstance'ın döndürülmesiyle bellek sızıntıları olmadan en iyi şekilde çalışır (her yönelim değişikliğinde büyük bellek sızıntıları oluşmadan önce setSavedEnabled (false) kullandım ve sona erdi)
Informatic0re

0

Her zaman nullpointer istisnasını önlemeye çalışmalıyız, bu nedenle paket bilgileri için ilk olarak saveinstance yöntemini kontrol etmeliyiz. bu blog bağlantısını kontrol etmek için kısa açıklama için

public static class DetailsActivity extends Activity {

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

        if (getResources().getConfiguration().orientation
            == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    } 
}

0

Sadece bir proje yapıyorsanız, proje yöneticisi ekran değiştirme işlevine ulaşmanız gerektiğini söyler, ancak ekran geçişi farklı bir düzen yüklemek istemezsiniz (düzen ve yerleşim port sistemi oluşturabilir.

Ekran durumunu otomatik olarak belirleyecek, ilgili düzeni yükleyeceksiniz), etkinliği veya parçayı yeniden başlatma ihtiyacı nedeniyle, kullanıcı deneyimi iyi değil, doğrudan ekran anahtarında değil, bakıyorum? URL = YgNfP-VHY-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki ve wd = & eqid = f258719e0001f24000000004585a1082

Öncül, mizanpajınızın layout_weight düzeninin ağırlığını aşağıdaki gibi kullanmasıdır:

<LinearLayout
Android:id= "@+id/toplayout"
Android:layout_width= "match_parent"
Android:layout_height= "match_parent"
Android:layout_weight= "2"
Android:orientation= "horizontal" >

Bu yüzden benim yaklaşımım, ekran değiştirirken, görünüm dosyasının yeni bir düzenini yüklemeye gerek olmadığı, onConfigurationChanged dinamik ağırlıklarda düzeni değiştirmeye ihtiyaç duymamak, aşağıdaki adımlar: 1 ilk set: AndroidManifest.xml, activity özniteliğinde: android: configChanges = "keyboardHidden | orientation | screenSize" Ekran geçişini önlemek için, onConfigurationChanged yönteminde onConfigurationChanged 2 yeniden yazma etkinliğini veya parçasını izleyebilmek için yeniden yüklemeden kaçının.

@Override
Public void onConfigurationChanged (Configuration newConfig) {
    Super.onConfigurationChanged (newConfig);
    SetContentView (R.layout.activity_main);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        //On the layout / / weight adjustment
        LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout);
        LinearLayout.LayoutParams LP = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f);
        Toplayout.setLayoutParams (LP);
        LinearLayout tradespace_layout = (LinearLayout) findViewById(R.id.tradespace_layout);
        LinearLayout.LayoutParams LP3 = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f);
        Tradespace_layout.setLayoutParams (LP3);
    }
    else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
    {
        //On the layout / / weight adjustment
        LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout);
        LinearLayout.LayoutParams LP = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f);
        Toplayout.setLayoutParams (LP);
        LinearLayout tradespace_layout = (LinearLayout) findViewById (R.id.tradespace_layout);
        LinearLayout.LayoutParams LP3 = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f);
        Tradespace_layout.setLayoutParams (LP3);
    }
}

0

Yapılandırma değişikliğinde, çerçeve sizin için parçanın yeni bir örneğini oluşturacak ve bunu aktiviteye ekleyecektir. Yani bunun yerine:

FragmentOne fragment = new FragmentOne();

fragmentTransaction.add(R.id.fragment_container, fragment);

Bunu yap:

if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) {
    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG);
}

Lütfen setRetainInstance (true) 'u çağırmadığınız sürece çerçevenin oryantasyon değişikliğinde FragmentOne'ın yeni bir örneğini eklediğini unutmayın; bu durumda eski FragmentOne örneğini ekler.

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.