2016-11-23 1 views
6

Ich habe 6 Seiten in meinem ViewPager und OffscreenPageLimit=2 (einfach meinen Fehler zu reproduzieren).
Alle Daten auf den 6 Seiten stammen vom Server. In onCreateView, ich sende eine Anfrage an den Server, und UI aktualisieren, wenn ich Daten vom Server bekomme.Fragment Erstellen/Wiederherstellen einer doppelten Ansicht beim erneuten Anfügen

Wenn ich die erste Registerkarte wähle und mehrmals schnell hintereinander wechsle, werden einige Pager falsch angezeigt. Und zu diesem Zeitpunkt ist mein Feld mMainLayout im Fragment nicht null.

Zum Beispiel habe ich eine ListView in meiner ersten Seite. Wenn die Seite falsch ist, ist ein weiterer ListView oben rechts ListView. Wenn ich versuche, ListView zu scrollen, wird nur die rechte (untere) verschoben.

Ich weiß, dass meine Antwort Zuhörer Bezug von mMainLayout hält und einige andere Ansichten, ich erstellen Sie eine neue mMainLayout in Methode onCreateView, dachte ich Fragment neu verwenden mMainLayout, wenn es wieder anbringen/Restore und lasse die alte fallen (oder entferne sie aus dem Container). Aber ich habe mich getäuscht.

Ich weiß, dass FragmentPagerAdapter Fragment in instantiateItem anfügen/erneut anfügen und in destroyItem lösen. Adapter hat kein Fragment entfernt. Adapter hat kein neues Fragment erstellt. Fragment behalten Sie den Blick und sehen Sie sich selbst an.

I mMainLayout aus dem Behälter entfernen und alle Ansichten in meinem Fragmente null auf onDestroyView gesetzt und nichts in onSaveInstanceState speichern. Aber immer noch einfach den Fehler zu reproduzieren.

Aktivität:

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

    registerReceiver(receiver, new IntentFilter("com.souyidai.intent.action.log_fragment")); 
    FragmentManager fragmentManager = getFragmentManager(); 
    mResources = getResources(); 
    mMainPagerAdapter = new MainPagerAdapter(fragmentManager); 
    mPager = (ViewPager) findViewById(R.id.pager); 
    mPager.setAdapter(mMainPagerAdapter); 
    mPager.setOffscreenPageLimit(CACHE_SIZE); 
    mIndicator = (TabPageIndicator) findViewById(R.id.indicator); 
    mIndicator.setViewPager(mPager); 
    ... 
} 

private class MainPagerAdapter extends FragmentPagerAdapter { 
    public MainPagerAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     MainConfig.TabItem tab = mTabs.get(position); 
     String tabType = tab.getTabType(); 
     String code = tab.getCode(); 
     MainConfig.TabItem.SubTabItem subTabItem = tab.getSubitem().get(0); 
     Fragment fragment = MainFragment.newInstance(code, subTabItem); 
     return fragment; 
    } 

    @Override 
    public CharSequence getPageTitle(int position) { 
     MainConfig.TabItem tab = mTabs.get(position); 
     return tab.getTitle(); 
    } 

    @Override 
    public int getCount() { 
     return mTabs.size(); 
    } 
} 

Fragment:

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    mContainer = container; 
    mMainLayout = inflater.inflate(R.layout.fragment_main, container, false); 
    ... 
} 


@Override 
public void onDestroyView() { 
    super.onDestroyView(); 
    mContainer.removeView(mMainLayout); 
    mContainer = null; 
    mMainLayout = null; 
    mSwipeRefreshLayout = null; 
    mListView = null; 
    mHeaderLayout = null; 
    mFooterLayout = null; 
    ... 
} 

android.support.v13.app.FragmentPagerAdapter

@Override 
public Object instantiateItem(ViewGroup container, int position) { 
    if (mCurTransaction == null) { 
     mCurTransaction = mFragmentManager.beginTransaction(); 
    } 

    final long itemId = getItemId(position); 

    // Do we already have this fragment? 
    String name = makeFragmentName(container.getId(), itemId); 
    Fragment fragment = mFragmentManager.findFragmentByTag(name); 
    if (fragment != null) { 
     if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); 
     mCurTransaction.attach(fragment); 
    } else { 
     fragment = getItem(position); 
     if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); 
     mCurTransaction.add(container.getId(), fragment, 
       makeFragmentName(container.getId(), itemId)); 
    } 
    if (fragment != mCurrentPrimaryItem) { 
     FragmentCompat.setMenuVisibility(fragment, false); 
     FragmentCompat.setUserVisibleHint(fragment, false); 
    } 

    return fragment; 
} 

ich diese finden:

Android fragment onCreateView creating duplicate views on top of each other

Aber ich erstelle nur neue Fragment in FragmentPagerAdapter.getItem. Also sind wir anders.

Ich löse dies, indem alle Kinder von Entfernen mMainLayout wenn mMainLayout nicht null in Methode ist onCreateView. Dann ist alles in Ordnung. Aber ich bin immer noch verwirrt, warum dieser Fehler passiert?

Ich habe einige Tests gemacht.
1. Versuchen Sie, ein Fragment zweimal hinzuzufügen, app crash und log sagt: java.lang.IllegalStateException: Fragment bereits hinzugefügt: ...
2.versuchen, ein Fragment zweimal zu befestigen, System nicht Fragment.onCreate oder Fragment.onCreateView ruft

+0

Fügen Sie bitte Ihren Code hinzu –

+0

Verwenden Sie 'setRetainInstance (true)' api des Fragments Wenn Sie Ihren Fragmentstatus beibehalten möchten. Beim zweiten Laden desselben Fragments wird die UI-Komponente von der vorherigen Instanz geladen, wenn sie im Speicher ist. Andernfalls wird ein neues Fragment erstellt. Sie müssen also nicht alle UI-Komponenten in der ondestroyview-Methode null machen. –

+0

@ keyur9779 Ich möchte meine Fragment-Zustände nicht beibehalten. aber es sieht so aus, als ob dieses Fragment noch immer den vorherigen Zustand wiederherstellt. –

Antwort

3

Diese Situation trat wegen der kundenspezifischen Implementierung von instantiateItem() Eine Sache, die ich bemerkt haben. Sie überprüfen, ob das Fragment bereits hinzugefügt wurde.

@Override 
public Object instantiateItem(ViewGroup container, int position) { 
    if (mCurTransaction == null) { 
     mCurTransaction = mFragmentManager.beginTransaction(); 
    } 

    final long itemId = getItemId(position); 

    // Do we already have this fragment? 
    String name = makeFragmentName(container.getId(), itemId); 
    Fragment fragment = mFragmentManager.findFragmentByTag(name); 
    if (fragment != null) { 
     if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); 
     //make sure you are not attaching already added fragment 
     //try with comment and uncomment two lines below 
     //if(!fragment.isAdded()) 
     //mCurTransaction.attach(fragment); 
    } else { 
     fragment = getItem(position); 
     if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); 
     mCurTransaction.add(container.getId(), fragment, 
       makeFragmentName(container.getId(), itemId)); 
    } 
    if (fragment != mCurrentPrimaryItem) { 
     FragmentCompat.setMenuVisibility(fragment, false); 
     FragmentCompat.setUserVisibleHint(fragment, false); 
    } 

    return fragment; 
} 

nicht wissen Sie, warum Sie Klasse Member-Feld schaffen für mMainLayout

//try to go with default 
View view = inflater.inflate(R.layout.fragment_main, container, false); 

Sie empfehlen this

+0

Wenn ich ein Fragment zweimal, app crash und log sagt: java.lang.IllegalStateException: Fragment bereits hinzugefügt: ... –

+0

Sie haben Recht, es ist nicht notwendig, Referenz von mMainLayout zu halten. Aber ich muss eine Ansichtsreferenz halten, um die Ansicht zu aktualisieren, wenn Daten vom Server abgerufen werden. –

+0

Meine App ist nicht abstürzen, so denke ich, es ist keine Hilfe, fragment.isAdded –

2

Sie versuchen, mit dem Code kann zu studieren, ich habe ähnliches Problem konfrontiert war gelöst mit Brüllen ändert sich. In diesem Fall müssen Sie nicht alle Referenzen in onDestroyView null machen. In diesem Fall müssen Sie immer einen Null-Check durchführen, bevor Sie diese Referenz irgendwo anders im Code verwenden.

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    if(mMainLayout == null) 
    { 
     mMainLayout = inflater.inflate(R.layout.fragment_main, container,false); 
... 
    } 
    return mMainLayout; 
} 

Wenn mMainlayout nicht null ist, bedeutet dies, dass Ihr Fragment Instanz bereits eine Instanz des mMainLayout hat und bereits ViewGroup container keine Notwendigkeit gegeben, um es wieder hinzuzufügen. Sie stehen vor dem Problem, dass Sie demselben Container dieselbe Ansicht erneut hinzufügen.

+0

Ich habe zuletzt in meiner Frage erwähnt, dass ich das Problem gelöst, das gleiche wie Sie. Ich möchte wissen, warum die Ansicht/das Fragment zweimal hinzugefügt wird. –

+0

Da das gleiche Layout bereits in Ihrem ViewGroup-Container hinzugefügt wurde, müssen wir eine Nullprüfung durchführen, um es nicht zweimal hinzuzufügen. – Swapnil

Verwandte Themen