1. 程式人生 > >解決Fragment中使用ViewPager時,ViewPager裡的Fragment錯位和空白問題。

解決Fragment中使用ViewPager時,ViewPager裡的Fragment錯位和空白問題。

這兩天開始在改OSChina的開源android客戶端,打算用Fragment來分離Main這個Activity裡的功能。用Fragment巢狀ViewPager+Fragment的時候發現問題。

紅色框的是主Fragment,藍色框是主Fragment內嵌的ViewPager+Fragment。

例如當”資訊“切換到”問答“的時候,”問答“內的ViewPager+Fragment顯示不符合預期,因為裡面的Fragment錯位了,前面幾個顯示的是”資訊“裡面的Fragment。

而且有些顯示Fragment顯示空白。檢查了下沒問題,檢視原始碼發現是建立FragmentPagerAdapter時用getFragmentManager()傳入的FragmentManager都是獲取自Activity的同一個FragmentManager。

FragmentManager裡用ArrayList自動快取了Fragment,如果兩個主Fragment用同樣的佈局ID會使得快取的tag相同,結果會導致子Fragment互相替換。

FragmentPagerAdapter裡的原始碼:

複製程式碼
 1 @Override
 2     public Object instantiateItem(ViewGroup container, int position) {
 3         if (mCurTransaction == null) {
 4             mCurTransaction = mFragmentManager.beginTransaction();
5 } 6 7 final long itemId = getItemId(position); 8 9 // Do we already have this fragment? 10 String name = makeFragmentName(container.getId(), itemId); 11 Fragment fragment = mFragmentManager.findFragmentByTag(name); 12 if (fragment != null) { 13 if
(DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); 14 mCurTransaction.attach(fragment); 15 } else { 16 fragment = getItem(position); 17 if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); 18 mCurTransaction.add(container.getId(), fragment, 19 makeFragmentName(container.getId(), itemId)); 20 } 21 if (fragment != mCurrentPrimaryItem) { 22 fragment.setMenuVisibility(false); 23 fragment.setUserVisibleHint(false); 24 } 25 26 return fragment; 27 }
複製程式碼

還有Fragment顯示空白的問題,列印所有Fragment的生命週期發現,當”資訊“切換到”問答“的時候主Fragment會執行onCreate到onResume,但是ViewPager裡的Fragment卻沒動靜。

呼叫notifyDataSetChanged()也無法重新整理子Fragment。檢視原始碼後發現使用getChildFragmentManager()來替代getFragmentManager()獲取FragmentManager能解決問題,

因為getChildFragmentManager()會為本Fragment建立一個私有的FragmentManager。

複製程式碼
 1 /**
 2      * Return the FragmentManager for interacting with fragments associated
 3      * with this fragment's activity.  Note that this will be non-null slightly
 4      * before {@link #getActivity()}, during the time from when the fragment is
 5      * placed in a {@link FragmentTransaction} until it is committed and
 6      * attached to its activity.
 7      *
 8      * <p>If this Fragment is a child of another Fragment, the FragmentManager
 9      * returned here will be the parent's {@link #getChildFragmentManager()}.
10      */
11     final public FragmentManager getFragmentManager() {
12         return mFragmentManager;
13     }
14 
15     /**
16      * Return a private FragmentManager for placing and managing Fragments
17      * inside of this Fragment.
18      */
19     final public FragmentManager getChildFragmentManager() {
20         if (mChildFragmentManager == null) {
21             instantiateChildFragmentManager();
22             if (mState >= RESUMED) {
23                 mChildFragmentManager.dispatchResume();
24             } else if (mState >= STARTED) {
25                 mChildFragmentManager.dispatchStart();
26             } else if (mState >= ACTIVITY_CREATED) {
27                 mChildFragmentManager.dispatchActivityCreated();
28             } else if (mState >= CREATED) {
29                 mChildFragmentManager.dispatchCreate();
30             }
31         }
32         return mChildFragmentManager;
33     }
複製程式碼 複製程式碼
 1 void instantiateChildFragmentManager() {
 2         mChildFragmentManager = new FragmentManagerImpl();
 3         mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
 4             @Override
 5             public View findViewById(int id) {
 6                 if (mView == null) {
 7                     throw new IllegalStateException("Fragment does not have a view");
 8                 }
 9                 return mView.findViewById(id);
10             }
11         }, this);
12     }
複製程式碼

同時還會根據本Fragment現在所處的狀態來更新私有FragmentManager裡所快取的Fragment。

複製程式碼
 1     ArrayList<Fragment> mActive;
 2     ArrayList<Fragment> mAdded;
 3     ArrayList<Integer> mAvailIndices;
 4     ArrayList<BackStackRecord> mBackStack;
 5     ArrayList<Fragment> mCreatedMenus;
 6 
 7     public void dispatchStart() {
 8         mStateSaved = false;
 9         moveToState(Fragment.STARTED, false);
10     }
11     
12     public void dispatchResume() {
13         mStateSaved = false;
14         moveToState(Fragment.RESUMED, false);
15     }
16     
17     public void dispatchPause() {
18         moveToState(Fragment.STARTED, false);
19     }
20     
21     public void dispatchStop() {
22         // See saveAllState() for the explanation of this.  We do this for
23         // all platform versions, to keep our behavior more consistent between
24         // them.
25         mStateSaved = true;
26 
27         moveToState(Fragment.STOPPED, false);
28     }
29     
30     public void dispatchReallyStop() {
31         moveToState(Fragment.ACTIVITY_CREATED, false);
32     }
33 
34     public void dispatchDestroyView() {
35         moveToState(Fragment.CREATED, false);
36     }
37 
38     public void dispatchDestroy() {
39         mDestroyed = true;
40         execPendingActions();
41         moveToState(Fragment.INITIALIZING, false);
42         mActivity = null;
43         mContainer = null;
44         mParent = null;
45     }
46 
47     void moveToState(int newState, int transit, int transitStyle, boolean always) {
48         if (mActivity == null && newState != Fragment.INITIALIZING) {
49             throw new IllegalStateException("No activity");
50         }
51 
52         if (!always && mCurState == newState) {
53             return;
54         }
55 
56         mCurState = newState;
57         if (mActive != null) {
58             boolean loadersRunning = false;
59             for (int i=0; i<mActive.size(); i++) {
60                 Fragment f = mActive.get(i);
61                 if (f != null) {
62                     moveToState(f, newState, transit, transitStyle, false); //更新Fragment
63                     if (f.mLoaderManager != null) {
64                         loadersRunning |= f.mLoaderManager.hasRunningLoaders(); //是否在裝載中
65                     }
66                 }
67             }
68 
69             if (!loadersRunning) {
70                 startPendingDeferredFragments();  //不是的話啟動更新
71             }
72 
73             if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
74                 mActivity.supportInvalidateOptionsMenu(); // 重新整理選項選單
75                 mNeedMenuInvalidate = false;
76             }
77         }
78     }
79 
80     void startPendingDeferredFragments() {
81         if (mActive == null) return;
82 
83         for (int i=0; i<mActive.size(); i++) {
84             Fragment f = mActive.get(i);
85             if (f != null) {
86                 performPendingDeferredStart(f);
87             }
88         }
89     }
90     
91     
複製程式碼

根據Fragment所處的狀態,啟動和恢復Fragment的檢視。

複製程式碼
  1 void moveToState(Fragment f, int newState, int transit, int transitionStyle,
  2             boolean keepActive) {
  3         // Fragments that are not currently added will sit in the onCreate() state.
  4         if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
  5             newState = Fragment.CREATED;
  6         }
  7         if (f.mRemoving && newState > f.mState) {
  8             // While removing a fragment, we can't change it to a higher state.
  9             newState = f.mState;
 10         }
 11         // Defer start if requested; don't allow it to move to STARTED or higher
 12         // if it's not already started.
 13         if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
 14             newState = Fragment.STOPPED;
 15         }
 16         if (f.mState < newState) {
 17             // For fragments that are created from a layout, when restoring from
 18             // state we don't want to allow them to be created until they are
 19             // being reloaded from the layout.
 20             if (f.mFromLayout && !f.mInLayout) {
 21                 return;
 22             }  
 23             if (f.mAnimatingAway != null) {
 24                 // The fragment is currently being animated...  but!  Now we
 25                 // want to move our state back up.  Give up on waiting for the
 26                 // animation, move to whatever the final state should be once
 27                 // the animation is done, and then we can proceed from there.
 28                 f.mAnimatingAway = null;
 29                 moveToState(f, f.mStateAfterAnimating, 0, 0, true);
 30             }
 31             switch (f.mState) {
 32                 case Fragment.INITIALIZING:
 33                     if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
 34                     if (f.mSavedFragmentState != null) {
 35                         f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
 36                                 FragmentManagerImpl.VIEW_STATE_TAG);
 37                         f.mTarget = getFragment(f.mSavedFragmentState,
 38                                 FragmentManagerImpl.TARGET_STATE_TAG);
 39                         if (f.mTarget != null) {
 40                             f.mTargetRequestCode = f.mSavedFragmentState.getInt(
 41                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
 42                         }
 43                         f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
 44                                 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
 45                         if (!f.mUserVisibleHint) {
 46                             f.mDeferStart = true;
 47                             if (newState > Fragment.STOPPED) {
 48                                 newState = Fragment.STOPPED;
 49                             }
 50                         }
 51                     }
 52                     f.mActivity = mActivity;
 53                     f.mParentFragment = mParent;
 54                     f.mFragmentManager = mParent != null
 55                             ? mParent.mChildFragmentManager : mActivity.mFragments;
 56                     f.mCalled = false;
 57                     f.onAttach(mActivity);
 58                     if (!f.mCalled) {
 59                         throw new SuperNotCalledException("Fragment " + f
 60                                 + " did not call through to super.onAttach()");
 61                     }
 62                     if (f.mParentFragment == null) {
 63                         mActivity.onAttachFragment(f);
 64                     }
 65 
 66                     if (!f.mRetaining) {
 67                         f.performCreate(f.mSavedFragmentState);
 68                     }
 69                     f.mRetaining = false;
 70                     if (f.mFromLayout) {
 71                         // For fragments that are part of the content view
 72                         // layout, we need to instantiate the view immediately
 73                         // and the inflater will take care of adding it.
 74                         f.mView = f.performCreateView(f.getLayoutInflater(
 75                                 f.mSavedFragmentState), null, f.mSavedFragmentState);
 76                         if (f.mView != null) {
 77                             f.mInnerView = f.mView;
 78                             f.mView = NoSaveStateFrameLayout.wrap(f.mView);
 79                             if (f.mHidden) f.mView.setVisibility(View.GONE);
 80                             f.onViewCreated(f.mView, f.mSavedFragmentState);
 81                         } else {
 82                             f.mInnerView = null;
 83                         }
 84                     }
 85                 case Fragment.CREATED:
 86                     if (newState > Fragment.CREATED) {
 87                         if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
 88                         if (!f.mFromLayout) {
 89                             ViewGroup container = null;
 90                             if (f.mContainerId != 0) {
 91                                 container = (ViewGroup)mContainer.findViewById(f.mContainerId);
 92                                 if (container == null && !f.mRestored) {
 93                                     throwException(new IllegalArgumentException(
 94                                             "No view found for id 0x"
 95                                             + Integer.toHexString(f.mContainerId) + " ("
 96                                             + f.getResources().getResourceName(f.mContainerId)
 97                                             + ") for fragment " + f));
 98                                 }
 99                             }
100                             f.mContainer = container;
101                             f.mView = f.performCreateView(f.getLayoutInflater(
102                                     f.mSavedFragmentState), container, f.mSavedFragmentState);
103                             if (f.mView != null) {
104                                 f.mInnerView = f.mView;
105                                 f.mView = NoSaveStateFrameLayout.wrap(f.mView);
106                                 if (container != null) {
107                                     Animation anim = loadAnimation(f, transit, true,
108                                             transitionStyle);
109                                     
            
           

相關推薦

解決Fragment使用ViewPagerViewPagerFragment錯位空白問題

這兩天開始在改OSChina的開源android客戶端,打算用Fragment來分離Main這個Activity裡的功能。用Fragment巢狀ViewPager+Fragment的時候發現問題。 紅色框的是主Fragment,藍色框是主Fragment內嵌的ViewP

Android 多層fragment 巢狀viewPager不顯示的問題

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/zkll200/article/details/73692518 先看一段錯誤程式碼,這段程式碼寫在一個 Fragment 中: private void initVi

ViewPager + Fragment + RecyclerView 切換ViewPagerRecyclerView自動滾動的問題

最近專案中有用到ViewPager + Fragment + RecyclerView的佈局,發現切換ViewPager時,有一個RecyclerView(裡面又嵌套了一個RecyclerView)會自動滾動,原因是因為RecyclerView搶佔了事件的焦點導致。 解決辦法: 在Rec

安卓在ScrollView巢狀ViewPager設定ViewPager的單個頁面高度隨內容變化

在開發過程中,有時候頁面佈局比較複雜,會使用到ScrollView和ViewPager,如果不進行處理,會出現不顯示ViewPager的子頁面,或者子頁面高度一樣,內容顯示不全,或者是留白太多。我的需求是需要在頁面下面加一個可以左右滑動的分欄,而且兩個分類的頁面高度不一致且不

解決listview 點選itemitem的所有設定了selector的元件全部一起變色

listview中如果不設定,item裡面的每個元件的背景顏色的話,預設情況下,點選item時,會有listview的預設,點選變色。預設的效果很醜,所以我們一般都會使用自定義的顏色。 現在我要實現這種效果,如上圖紅框中的兩個元件。這是一個listview,我想要實現

fragmentTabHost中點選一個fragment的按鈕跳轉到另外一個fragment解決辦法

最近專案中用到fragmentTabHost,在其中的一個fragment中有多個按鈕,點選可跳轉到別的fragment,實現方法是,在MainActivity中寫方法: public void setTab(int tab){ tabHost.setCurrentTa

配置MySQL遇到的一些問題解決方法

Q1.配置好路徑和環境變數以後,準備安裝mysql,但是顯示MySQL服務無法啟動 A:網上查了好多解決辦法,最後解決辦法是需要初始化,操作步驟為: mysql -remove mysql -install mysql -initialize Q2.正常啟動mysql後,輸入

【Web篇03】Spring框架下servlet響應的res在jsp頁面顯示防止亂碼的操作

首先,在Spring軟體中,找到Web工程,src下的對應的servlet; 在servlet中獲取請求引數之後,給瀏覽器傳送響應之前; 需要鍵入一段程式碼即可,如下: response.setContentType("text/html;charset=UTF-8"); 括號內的

python使用matplotlib繪圖圖片上文字無法顯示問題

rom image ans width 文字 bsp png block 出現 在使用python過程中,我們往往需要使用matplotlib進行圖片的繪制,在繪圖過程中,我們有時需要在圖片上進行文字的顯示,在使用過程中,會出現文字無法顯示的問題。如下圖: 遇到上述問題我

有效解決ajax傳中文亂碼的情況php處理接收到的值

在抽獎環節時,需把獲獎名單通過ajax的post方式傳輸給php後臺進行儲存,但是php接收到的值確是亂碼。在百度之後並沒有找到合適的解決方法。       則使用js的encodeURI函式可以有效解決,但不知為何需使用兩次。 此時傳輸的值獲取到的為:

【已解決】APP啟動總是出現一個空白

APP啟動時,總是出現一個空白頁,簡單說:閃屏。 導致原因(AndroidManifest檔案): <application         android:allowBackup="true"         android:icon="@mipmap/logo"

解決 axios 跨域傳送 post 請求變 options 的問題

前端:VUE 後端:django 前後端除錯時post請求,發現請求方式為options,服務端不接受,後查詢發現遇到大名鼎鼎的跨域問題。 跨域:協議、ip、埠只要前後端有一個不一樣就會出現跨域問題。瀏覽器會嘗試向後端傳送option請求->想後端詢問是否支援從前端的這個域名發起跨

Fragment呼叫Activity的方法很簡單 Fragment呼叫Activity的方法很簡單

Fragment呼叫Activity中的方法,很簡單 2016年02月25日 10:12:10 閱讀數:1497 假如 父Activity的類名叫 ParentActivity,有一個test()方法 在Fragm

如何解決IDEA輸入soutpsvm後沒有自動聯想功能的問題

File ----> Setting 選中Live Templates 搜尋sout和psvm 注意左下角:   當然你的電腦上的顯示會與我不同,但是你只要知道,這裡的作用是限制“sout”之類的縮寫能夠聯想的適用範圍。 點選change 然後

解決監聽label點選label內input執行兩次的bug

   有的時候,我們需要監聽label點選事件和input點選事件,如下所示:          <label id="checkbox"  data-role="checkbox">  <input id="storepwd"  type="checkb

使用springmvc從頁面獲取資料然後根據獲得的引數資訊進行修改如果修改的資料含有不是基本資料型別的引數比如傳的引數有Date型別的資料需要我們進行引數型別轉換

1.1 需求   在商品修改頁面可以修改商品的生產日期,並且根據業務需求自定義日期格式。 1.2 需求分析   由於日期資料有很多格式,所以springmvc沒辦法把字串轉換成日期型別。所以需要自定義引數繫結。前端控制器接收到請求後,找到註解形式的處理器介面卡,對RequestMapping標記的方法進

在eclipsedebug加斷點出現Exception Processing async thread queue

在除錯一個formBean時發現有異常,然後在Eclipse中加了一個斷點,當進入debug時卻彈出了一個JDI thread evaluations的對話方塊,錯誤為Exception Processing async thread queue。我所鍾愛的debug竟然也會開點小玩笑,習慣了deb

解決maven包衝突去除依賴包或修改依賴包版本

這幾年springBoot很火,本人在遷移專案的過程中遇到了一些問題,比如以前用的springMVC依賴了org.json包,轉成了springBoot專案之後,由於springBoot預設依賴了其他的包,有用到org.json。但是卻沒有new JSONObject(Ob

解決 python 使用tesserocrImag模組 處理驗證碼的問題

python程式: from PIL import Image import tesserocr imag=Image.open(r"path") print(tesserocr.image_to_text(imag)) 會報以下錯誤: Traceback (most recent cal

解決mysql導資料格式不對、匯入慢、丟資料的問題

    如果希望一勞永逸的解決慢的問題,不妨把你的mysql升級到mysql8.0吧,mysql8.0預設的字符集已經從latin1改為utf8mb4,因此現在UTF8的速度要快得多,在特定查詢時速度提