網站首頁 編程語言 正文
最近遇到比較奇怪的bug,TableLayout+ViewPager實現點擊頂部tab切換viewpager視圖。但是在Viewpager設置dapter時,最開始設置的是FragmentPagerAdapter,會導致tab切換后FragmentPagerAdapter內的視圖未刷新(與上一個tab內容重復或展示成空白,展示成空白一般出現在頁面重啟后不能完成刷新成功)。替換成FragmentStatePagerAdapter或者FragmentStateAdapter,便解決了這一問題。這其實是個比較常見的bug,網絡上有很多推薦的解決方案。那么到底FragmentPagerAdapter、FragmentStateAdapter以及FragmentStatePagerAdapter有何具體的區別呢?在這篇文章中我將詳細解答。
根據類圖進行分析
FragmentPagerAdapter與FragmentPagerStateAdapter區別點:
一:二者在狀態保存有差異:FragmentPagerAdapter并未實現saveState()、restoreState()
public class FragmentPagerAdapter{
? ? // ......
? ? public static final int POSITION_UNCHANGED = -1;
? ? public static final int POSITION_NONE = -2;
?
? ? public Parcelable saveState() {
? ? ? ? return null;
? ? }
?
? ? public void restoreState(Parcelable state, ClassLoader loader) {
? ? ? ??
? ? }
}
而FragmentPagerStateAdapter則實現了saveState()、restoreState()這倆方法:
?public Parcelable saveState() {
? ? ? ? Bundle state = null;
? ? ? ? if (mSavedState.size() > 0) {
? ? ? ? ? ? state = new Bundle();
? ? ? ? ? ? Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
? ? ? ? ? ? mSavedState.toArray(fss);
? ? ? ? ? ? state.putParcelableArray("states", fss);
? ? ? ? }
? ? ? ? for (int i=0; i<mFragments.size(); i++) {
? ? ? ? ? ? Fragment f = mFragments.get(i);
? ? ? ? ? ? if (f != null && f.isAdded()) {
? ? ? ? ? ? ? ? if (state == null) {
? ? ? ? ? ? ? ? ? ? state = new Bundle();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? String key = "f" + i;
? ? ? ? ? ? ? ? mFragmentManager.putFragment(state, key, f);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return state;
? ? }
?
? ? @Override
? ? public void restoreState(Parcelable state, ClassLoader loader) {
? ? ? ? if (state != null) {
? ? ? ? ? ? Bundle bundle = (Bundle)state;
? ? ? ? ? ? bundle.setClassLoader(loader);
? ? ? ? ? ? Parcelable[] fss = bundle.getParcelableArray("states");
? ? ? ? ? ? mSavedState.clear();
? ? ? ? ? ? mFragments.clear();
? ? ? ? ? ? if (fss != null) {
? ? ? ? ? ? ? ? for (int i=0; i<fss.length; i++) {
? ? ? ? ? ? ? ? ? ? mSavedState.add((Fragment.SavedState)fss[i]);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? Iterable<String> keys = bundle.keySet();
? ? ? ? ? ? for (String key: keys) {
? ? ? ? ? ? ? ? if (key.startsWith("f")) {
? ? ? ? ? ? ? ? ? ? int index = Integer.parseInt(key.substring(1));
? ? ? ? ? ? ? ? ? ? Fragment f = mFragmentManager.getFragment(bundle, key);
? ? ? ? ? ? ? ? ? ? if (f != null) {
? ? ? ? ? ? ? ? ? ? ? ? while (mFragments.size() <= index) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? mFragments.add(null);
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? f.setMenuVisibility(false);
? ? ? ? ? ? ? ? ? ? ? ? mFragments.set(index, f);
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? Log.w(TAG, "Bad fragment at key " + key);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
FragmentStatePagerAdapter對Fragment的狀態進行了保存
二:二者在視圖管理方法差異:
FragmentStatePagerAdapter是整個Fragment對象的移除和重建
?public Object instantiateItem(ViewGroup container, int position) {
? ? ? ? if (mFragments.size() > position) {
? ? ? ? ? ? Fragment f = mFragments.get(position);
? ? ? ? ? ? if (f != null) {
? ? ? ? ? ? ? ? return f;
? ? ? ? ? ? }
? ? ? ? }
?
? ? ? ? if (mCurTransaction == null) {
? ? ? ? ? ? mCurTransaction = mFragmentManager.beginTransaction();
? ? ? ? }
?
? ? ? ? // 實例化fragment(交給我們實現的getItem方法)
? ? ? ? Fragment fragment = getItem(position);
?
? ? ? ? if (mSavedState.size() > position) {
? ? ? ? ? ? Fragment.SavedState fss = mSavedState.get(position);
? ? ? ? ? ? if (fss != null) {
? ? ? ? ? ? ? ? fragment.setInitialSavedState(fss);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 如果緩存 <= ViewPager傳入的position,說明當前位置還未存入緩存.
? ? ? ? while (mFragments.size() <= position) {
? ? ? ? ? ? // 先占個坑
? ? ? ? ? ? mFragments.add(null);
? ? ? ? }
? ? ? ? fragment.setUserVisibleHint(false);
? ? ? ? // 填坑
? ? ? ? mFragments.set(position, fragment);
? ? ? ? // 填充視圖
? ? ? ? mCurTransaction.add(container.getId(), fragment);
? ? ? ? return fragment;
? ? }
?
? ? @Override
? ? public void destroyItem(ViewGroup container, int position, Object object) {
? ? ? ? Fragment fragment = (Fragment) object;
?
? ? ? ? if (mCurTransaction == null) {
? ? ? ? ? ? mCurTransaction = mFragmentManager.beginTransaction();
? ? ? ? }
? ? ? ? // 從緩存中移除
? ? ? ? mFragments.set(position, null);
? ? ? ? // 從FragmentManager中移除
? ? ? ? mCurTransaction.remove(fragment);
? ? }
FragmentPagerAdapter是視圖的attach和detach,不會對整個fragment進行完全的添加和刪除操作。
因此,可見二者在使用場景上不同,如果頁面較少,仍舊希望能夠將生成的Fragment保存在內存中,在需要顯示的時候直接調用。而不要產生生成、銷毀對象的額外開銷。這樣效率最高。這種情況下,選中FragmentPagerAdapter更合適。
對于在使用FragmentPagerAdapter出現白屏或者刷新不了的bug,除了替換成FragmentStatePagerAdapter,還需要重載getItem()和instantiateItem()對象。
對于getItemPosition()方法,兩個累的區別是:FragmentStatePagerAdapter會在因POSITION_NONE觸發調用的destroyItem中真正的釋放資源,重新建立一個新的Fragment;而FragmentPagerAdapter僅僅會在destoryItem()中detach這個Fragment,在instantiateItem()時會使用舊的Fragment,并觸發attach,并沒有觸發資源及重建的過程。
原文鏈接:https://www.cnblogs.com/yuanting/archive/2022/08/16/16590977.html
相關推薦
- 2022-07-19 typedef struct LNode *p和typedef struct LNode筆記
- 2022-11-15 Apache?Doris的Bitmap索引和BloomFilter索引使用及注意事項_Linux
- 2022-09-26 Go并發同步Mutex典型易錯使用場景_Golang
- 2023-10-25 更簡單的方法實現el-calendar日歷組件中點擊上個月、今天、下個月按鈕時的點擊事件
- 2023-05-20 工程級?React?注冊登錄全棧級流程分析_React
- 2023-01-14 C++?win系統如何用MinGW編譯Boost庫_C 語言
- 2022-06-30 Python+SymPy實現秒解微積分詳解_python
- 2022-11-18 React網絡請求發起方法詳細介紹_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支