關於ViewPager+Fragment中Fragment不銷燬/生命週期

今天咱們的界面大幅改動,最終改爲如今主流的Tab類型,和微信有點相似,下面有四個Tab對於四個不同的模塊。緩存

可是這個四個界面都須要訪問網絡,而且咱們開發框架用的是註解框架,致使對請求網絡很差管理。,微信

並且又是viewpager+fragment網絡

咱們計劃是在第一個頁面將四個模板所有初始化,而後放到集合裏面,這樣即便用戶任意切換都不會有問題,由於咱們已經初始化了,app

可是用過viewpager的就知道,他會預加載前面和後面的item,可是其餘的會銷燬掉。框架

這樣能夠避免內存浪費,可是咱們不想這樣,這樣用戶體驗特別很差,別說產品和測試不一樣意,我本身也看不過去,ide

特別是每次一點入這個tab而後訪問下網絡,再更新下UI讓我都莫名其妙。測試

最後仍是解決了問題,一行代碼。以下;this

 

  • 方案一:設置ViewPager的緩存界面數
此方案適用於界面數較少的狀況,避免緩存界面太多致使內存吃緊。
方法:
mPager .setOffscreenPageLimit(2);

參數:int limit    -    緩存當前界面每一側的界面數google

以上述爲例,當前界面爲1,limit = 2,表示緩存二、3兩個界面。如此便避免了界面3被銷燬。
 
  • 方案二:保存狀態並恢復
此方案適用於可用界面信息可由狀態保存和恢復實現的狀況。
在onDestroyView方法內保存相關信息,在onCreateView方法內恢復信息設置。
 
  • 方案三(推薦):複用Fragment的RootView
此方案適用通用場景,推薦使用。
步驟1:在onDestroyView方法內把Fragment的RootView從ViewPager中remove

 

 
 @Override
 public void onDestroyView() {
    LogUtils.d(TAG , "-->onDestroyView");
    super .onDestroyView();
     if (null != FragmentView) {
         ((ViewGroup) mFragmentView.getParent()).removeView(mFragmentView);
    }
 }

 

 

 

步驟2:在onCreateView方法內複用RootView
 
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
         Bundle savedInstanceState) {
     LogUtils.d (TAG, "-->onCreateView");
     if (null == mFragmentView) {
          mFragmentView = inflater.inflate(R.layout.fragment, container, false);
         mListView = (ListView) mFragmentView .findViewById(R.id.mm_listview);
        mListView.setAdapter(mAdapter);
      mPbar = (ProgressBar) mFragmentView.findViewById(R.id.pbar_mm_loading);
         mPbar.setVisibility(View.VISIBLE);
    }
  
    return mFragmentView ;
}

 

我省事用了第一種,固然後面改爲看第三種。spa

 

 補充下,剛剛看了下第一種方式的源碼:

 

顯示這個方法

public void setOffscreenPageLimit(int limit) {
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }

 其中我分開放出來:

   private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
。。。。  
private static final int DEFAULT_OFFSCREEN_PAGES = 1;

也就是說,咱們設置的數據不是1的話,就調用了populate()方法。

下面是這個方法的代碼:

首先
   void populate() {
        populate(mCurItem);
    }
其中mCurItem是當前的item
  private int mCurItem;   // Index of currently displayed page.

咱們看下一個參數的populate()方法:

void populate(int newCurrentItem) {
        ItemInfo oldCurInfo = null;
        int focusDirection = View.FOCUS_FORWARD;
        if (mCurItem != newCurrentItem) {
            focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
            oldCurInfo = infoForPosition(mCurItem);
            mCurItem = newCurrentItem;
        }

        if (mAdapter == null) {
            sortChildDrawingOrder();
            return;
        }

        // Bail now if we are waiting to populate.  This is to hold off
        // on creating views from the time the user releases their finger to
        // fling to a new position until we have finished the scroll to
        // that position, avoiding glitches from happening at that point.
        if (mPopulatePending) {
            if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
            sortChildDrawingOrder();
            return;
        }

        // Also, don't populate until we are attached to a window.  This is to
        // avoid trying to populate before we have restored our view hierarchy
        // state and conflicting with what is restored.
        if (getWindowToken() == null) {
            return;
        }

        mAdapter.startUpdate(this);

        final int pageLimit = mOffscreenPageLimit;
        final int startPos = Math.max(0, mCurItem - pageLimit);
        final int N = mAdapter.getCount();
        final int endPos = Math.min(N-1, mCurItem + pageLimit);

        if (N != mExpectedAdapterCount) {
            String resName;
            try {
                resName = getResources().getResourceName(getId());
            } catch (Resources.NotFoundException e) {
                resName = Integer.toHexString(getId());
            }
            throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +
                    " contents without calling PagerAdapter#notifyDataSetChanged!" +
                    " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +
                    " Pager id: " + resName +
                    " Pager class: " + getClass() +
                    " Problematic adapter: " + mAdapter.getClass());
        }

        // Locate the currently focused item or add it if needed.
        int curIndex = -1;
        ItemInfo curItem = null;
        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
            final ItemInfo ii = mItems.get(curIndex);
            if (ii.position >= mCurItem) {
                if (ii.position == mCurItem) curItem = ii;
                break;
            }
        }

        if (curItem == null && N > 0) {
            curItem = addNewItem(mCurItem, curIndex);
        }

        // Fill 3x the available width or up to the number of offscreen
        // pages requested to either side, whichever is larger.
        // If we have no current item we have no work to do.
        if (curItem != null) {
            float extraWidthLeft = 0.f;
            int itemIndex = curIndex - 1;
            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
            final int clientWidth = getClientWidth();
            final float leftWidthNeeded = clientWidth <= 0 ? 0 :
                    2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
            for (int pos = mCurItem - 1; pos >= 0; pos--) {
                if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                    if (ii == null) {
                        break;
                    }
                    if (pos == ii.position && !ii.scrolling) {
                        mItems.remove(itemIndex);
                        mAdapter.destroyItem(this, pos, ii.object);
                        if (DEBUG) {
                            Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
                                    " view: " + ((View) ii.object));
                        }
                        itemIndex--;
                        curIndex--;
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    }
                } else if (ii != null && pos == ii.position) {
                    extraWidthLeft += ii.widthFactor;
                    itemIndex--;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                } else {
                    ii = addNewItem(pos, itemIndex + 1);
                    extraWidthLeft += ii.widthFactor;
                    curIndex++;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                }
            }

            float extraWidthRight = curItem.widthFactor;
            itemIndex = curIndex + 1;
            if (extraWidthRight < 2.f) {
                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                final float rightWidthNeeded = clientWidth <= 0 ? 0 :
                        (float) getPaddingRight() / (float) clientWidth + 2.f;
                for (int pos = mCurItem + 1; pos < N; pos++) {
                    if (extraWidthRight >= rightWidthNeeded && pos > endPos) {
                        if (ii == null) {
                            break;
                        }
                        if (pos == ii.position && !ii.scrolling) {
                            mItems.remove(itemIndex);
                            mAdapter.destroyItem(this, pos, ii.object);
                            if (DEBUG) {
                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
                                        " view: " + ((View) ii.object));
                            }
                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                        }
                    } else if (ii != null && pos == ii.position) {
                        extraWidthRight += ii.widthFactor;
                        itemIndex++;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    } else {
                        ii = addNewItem(pos, itemIndex);
                        itemIndex++;
                        extraWidthRight += ii.widthFactor;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    }
                }
            }

            calculatePageOffsets(curItem, curIndex, oldCurInfo);
        }

        if (DEBUG) {
            Log.i(TAG, "Current page list:");
            for (int i=0; i<mItems.size(); i++) {
                Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
            }
        }

        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);

        mAdapter.finishUpdate(this);

        // Check width measurement of current pages and drawing sort order.
        // Update LayoutParams as needed.
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            lp.childIndex = i;
            if (!lp.isDecor && lp.widthFactor == 0.f) {
                // 0 means requery the adapter for this, it doesn't have a valid width.
                final ItemInfo ii = infoForChild(child);
                if (ii != null) {
                    lp.widthFactor = ii.widthFactor;
                    lp.position = ii.position;
                }
            }
        }
        sortChildDrawingOrder();

        if (hasFocus()) {
            View currentFocused = findFocus();
            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
            if (ii == null || ii.position != mCurItem) {
                for (int i=0; i<getChildCount(); i++) {
                    View child = getChildAt(i);
                    ii = infoForChild(child);
                    if (ii != null && ii.position == mCurItem) {
                        if (child.requestFocus(focusDirection)) {
                            break;
                        }
                    }
                }
            }
        }
    }

真長。。。。

其實也是他自身保存了ItemInfo,而後實現了效果

 

而後,。。

我又用了第一種方法,google寫好的,省的麻煩不用白不用,尚未bug.

相關文章
相關標籤/搜索