6

縱向模式下,我的ViewPager有3個片段A,B,C,但在橫向模式下,它只有2個片段A和C.因此,我爲每種模式創建2個。問題是屏幕方向改變時,ViewPager恢復並使用以前的舊方向片段。例如,當將方向從縱向改爲橫向時,ViewPager現在顯示2個片段A,B而不是A和C.我知道爲什麼會發生這種情況,但找不到一個好的解決方案。ViewPager帶有不同的適配器,用於縱向和橫向

我目前的解決方法是使用不同的ID爲ViewPager(如:肖像和id/viewpager_landscape景觀佈局ID/viewpager_portrait)從重用片段防止但這使我內存泄漏,因爲舊片段將不會被破壞,仍然留在記憶中。

我在活動的onCreate嘗試了一些解決方法,如呼叫super.onCreate(空),或在活動的onSaveInstanceState去除ViewPager片段,但他們都讓我的應用程序崩潰。

所以我的問題是如何避免重新使用FragmentStatePagerAdapter方向更改時的一個或多個片段?

任何幫助將不勝感激。預先感謝。

+0

你嘗試重寫'onSaveInstanceState'和避免調用'super.onSaveInstanceState'該方法。這將防止'Fragment'被保存。 –

+0

這是我當前的解決方法,但是避免調用super.onSaveInstanceState,我清除ViewPager所做的所有狀態。我不認爲這是最好的解決方案,所以我仍然會問,因爲有時會在ViewPager中看到崩潰。 – Wayne

回答

3

問題可能是內置PagerAdapter實現了由Android提供Fragment小號假設項目將保持不變,所以保留和再利用到被添加到ViewPager所有Fragment S的基於指數的引用。即使在由於配置更改或進程被終止而重新創建Activity(和Fragment)之後,通過FragmentManager仍保留這些引用。

你需要做的是寫自己的實現的PagerAdapter,在與相關聯的每個Fragment自定義標籤和存儲Fragment個基於標記的(而不是基於索引的)格式。在添加一個抽象方法來提供基於索引的標記之後,您可以從現有的方法中派生出一個通用實現。當然,您必須從ViewPager刪除以前配置中添加的孤立/未使用Fragment(理想狀態下保持其狀態)。

如果您不想自己實現整個解決方案,那麼可以使用CWAC-Pager庫中的ArrayPagerAdapter來提供一個合理的實現,而且不費吹灰之力。初始化後,您可以根據提供的標籤分離相關Fragment,並根據需要從適配器中刪除/添加它。

+0

我知道寫我自己的適配器將解決問題,但我認爲有比這更好,更容易的解決方案,所以我問StackOverflow。 – Wayne

+0

@Wayne:我已增加了一些更多的信息來回答的末端,並且還使用[CWAC-尋呼機](https://github.com/commonsguy/cwac-pager)庫的建議。 – corsair992

+0

謝謝corsair992,我的解決方案是複製PagerAdapter源代碼並根據您的答案更改一些代碼。 我很抱歉,我的最後一個週末是不在線,所以我忘了給()全賞金你/ – Wayne

1

在您的Adapter中重寫getItemPosition()並返回POSITION_NONE。所以當ViewPager被重新創建時,它會調用getItemPosition(),並且由於您從這裏返回了POSITION_NONE,因此它將調用getItem()。你應該從這個getItem()返回新的片段。 Ref:http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getItemPosition%28java.lang.Object%29

+0

哦,我忘了getItemPosition。也許這將是一個很好的解決方案。謝謝 – Wayne

+1

這個答案不正確。 'ViewPager'只查找在調用'notifyDataSetChanged()'的'PagerAdapter'在項目位置的變化,而這將是因爲它承擔的項目爲不可變的無法正常從一個'FragmentStatePagerAdapter'反正移除項目工作,它會搞砸了它的保存狀態的關聯。對於'FragmentPagerAdapter'來說,它不會起作用,因爲它假設了同樣的事情,並且會重用原始的'Fragment'。 – corsair992

1

爲什麼在您的viewpager中使用兩個不同的ID,當您的方向改變時可以刪除Fragment B?

可以檢索內部onCreateView()或的onResume()這樣的片段(本例中工作的父母片段裏面,但也可以使用一個父活動內像的onResume()):

@Override 
public void onResume() { 
    super.onResume(); 

    // ... initialize components, etc. 

    pager.setAdapter(pagerAdapter); 

    List<Fragment> children = getChildFragmentManager().getFragments(); 

    if (children != null) { 
     pagerAdapter.restoreFragments(children, orientation); 
    } 
} 

然後在你的適配器內:

@Override 
public void restoreFragments(List<Fragment> fragments, int orientation) { 
    List<Fragment> fragmentsToAdd = new ArrayList<Fragment>(); 
    Collections.fill(fragmentsToAdd, null); 
    if (Configuration.ORIENTATION_LANDSCAPE == orientation) { 
     for (Fragment f : fragments) { 
      if (!(f instanceof FragmentB)) { 
       fragmentsToAdd.add(f); 
      } 
     } 
    } 
    this.fragmentsInAdapter = Arrays.copyOf(temp.toArray(new Fragment[0]), this.fragments.length); // array of all your fragments in your adapter (always refresh them, when config changes, else you have old references in your array! 
    notifyDataSetChanged(); // notify, to remove FragmentB 
} 

這應該工作。

Btw。如果您使用Support Library V13,則無法使用FragmentManager.getFragments(),因此您需要通過id或標記來獲取它們。

1

覆蓋OnConfigurationChanged()在你的活動(也添加機器人:configChanges =「方向」你在清單活動)這樣你手動管理的方向轉變。現在,「所有」你必須做的是更改OnOrientaionChanged中的適配器(也跟蹤當前位置)。這樣你就可以使用一個佈局,一個ViewPager,而且你不必擔心這個碎片沒有被回收(當然,你會有很多工作來彌補這一點)。 祝你好運!

相關問題