溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

FragmentStatePagerAdapter如何保存恢復(fù)下拉刷新Fragment內(nèi)存數(shù)據(jù)

發(fā)布時間:2023-02-22 11:05:28 來源:億速云 閱讀:91 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“FragmentStatePagerAdapter如何保存恢復(fù)下拉刷新Fragment內(nèi)存數(shù)據(jù)”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“FragmentStatePagerAdapter如何保存恢復(fù)下拉刷新Fragment內(nèi)存數(shù)據(jù)”文章能幫助大家解決問題。

一、前言

如果對于新開發(fā)的功能,建議使用 ViewPager2 + FragmentStateAdapter 來構(gòu)建; 對于需要維護的項目,且項目中使用到 ViewPage + FragmentStatePagerAdapter,還是需要學(xué)習(xí)下 FragmentStatePagerAdapter 的一些用法的。

FragmentStatePagerAdapter,適用于多個Fragment的場景,默認情況下會在內(nèi)存中保留3個Fragment,當(dāng)前Fragment、左側(cè)及右側(cè)Frgment,在已經(jīng)都訪問一遍的情況下,其他的Fragment會被銷毀掉(即走了onDestory)

二、FragmentStatePagerAdapter保存、恢復(fù)及刷新數(shù)據(jù)的效果

1、FragmentStatePagerAdapter保存、恢復(fù)

如第一個GIF圖所示,將Fragment的網(wǎng)絡(luò)數(shù)據(jù)保存到內(nèi)存中,然后下次Fragment再次構(gòu)建時使用內(nèi)存中數(shù)據(jù)。

首先有五個Fragment,分別是Fragment0 - Fragment4。

(1)第一步:進入到該首頁后,從左到右滑動到最后一個Fragment,加載網(wǎng)絡(luò)數(shù)據(jù)并填充到對應(yīng)的Fragment中。

(2)第二步:從右到左依次滑動到第一個Fragment,此時可以看到 Fragment3 及 Fragment4 展示的是網(wǎng)絡(luò)數(shù)據(jù),而Fragment0 到 Fragment3 展示的內(nèi)存數(shù)據(jù)。

這是因為在第一步時,當(dāng)界面展示最后一個Fragment的時候,這個時候僅有Fragment3 及 Frgment4 是存活的,其他Fragment均被銷毀掉了。在銷毀Fragment之前干了一件事,將數(shù)據(jù)保存到內(nèi)存中去,當(dāng)銷毀掉的Fragment再次被構(gòu)建時,將之前保存下來的數(shù)據(jù)填充到新構(gòu)建的Fragment中展示。

(3)第三步:再次從左到右滑動到最后一個Fragment,此時可以看到所有的Fragment使用的都是內(nèi)存數(shù)據(jù)了。道理同上

僅在Fragment中做兩件事情即可:

(1)保存Fragment數(shù)據(jù)到內(nèi)存中, 重寫onSaveInstanceState方法

(2)從內(nèi)存中恢復(fù)Fragment數(shù)據(jù),在onCreateView方法中的判斷參數(shù)savedInstanceState不為null,取其參數(shù)中的數(shù)據(jù)

2、FragmentStatePagerAdapter 遇到下拉刷新時,清空內(nèi)存緩存數(shù)據(jù),重新請求最新的網(wǎng)絡(luò)數(shù)據(jù)

如第二個GIF圖所示,下拉刷新后,請求最新的網(wǎng)絡(luò)數(shù)據(jù)填充到Fragment中

(1)在當(dāng)前Fragment4頁面下,進行下拉刷新

(2)下拉刷新的數(shù)據(jù)回來后,刷新當(dāng)前頁面,展示最新的網(wǎng)絡(luò)數(shù)據(jù)

(3)從右滑動到左時,能夠看到所有的Fragment頁面均是請求了最新的數(shù)據(jù)

這里面有幾個注意的點:

(1)下拉刷新后,僅在當(dāng)前頁面顯示時才會進行該頁面的網(wǎng)絡(luò)請求,而不是將所有Fragment頁面都進行網(wǎng)絡(luò)請求

(2)對應(yīng)Fragment4而言,因為下拉刷新后,F(xiàn)ragment4 及 Fragment3 是存活狀態(tài),因此需要單獨地處理它們的刷新(其實這里需要考慮地是當(dāng)前Fragment及其左右的Fragment)

(3)而對應(yīng)非Fragment4、Fragment3的Fragment們,因為已經(jīng)被銷毀掉了,所以只需要在下拉刷新后,將它們的內(nèi)存數(shù)據(jù)清除掉就可以了,這樣下次構(gòu)建的時候,沒有內(nèi)存數(shù)據(jù),則會重新請求網(wǎng)絡(luò)數(shù)據(jù)展示的。

關(guān)于下拉刷新的可以參考這篇文章Android:對現(xiàn)有布局添加自定義的下拉刷新布局(阻尼滑動、懸停、回彈動畫效果)

三、具體實現(xiàn)

1、如何保存Fragment的數(shù)據(jù)到內(nèi)存中

FragmentStatePagerAdapter在銷毀Fragment的時候,會調(diào)用destoryItem方法,從而間接地調(diào)用了Fragment的onSaveInstanceState方法來保存數(shù)據(jù)。

public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    // ...
    mSavedState.set(position, fragment.isAdded()
            ? mFragmentManager.saveFragmentInstanceState(fragment) : null)
    // ...
}

因此,僅需要在Fragment中重寫onSaveInstanceState方法

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    // 這里可以根據(jù)需要保存數(shù)據(jù)
    outState.putString(TAG, DATA_STR);
    super.onSaveInstanceState(outState);
}

2、如何恢復(fù)內(nèi)存中的數(shù)據(jù)填充到Fragment中

在構(gòu)建Fragment的時候,可以充分地利用onCreateView方法中的Bundle類型的savedInstanceState參數(shù):

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mContext = getActivity();
    View view = inflater.inflate(R.layout.demo_fragment_layout, null, false);
    initView(view);
    // savedInstanceState不為空的時候,可以從內(nèi)存中取出數(shù)據(jù)
    if (savedInstanceState != null) {
        mContentTv.setText(((String) savedInstanceState.get(TAG)) + ", 從內(nèi)存中取得數(shù)據(jù)");
        mContentTv.setTextColor(mContext.getResources().getColor(R.color.black));
    } else {
       loadNetData();
    }
    return view;
}

3、如何在下拉刷新后,每個Fragment請求最新的網(wǎng)絡(luò)數(shù)據(jù)并刷新展示

下拉刷新,是將所有Fragment的數(shù)據(jù)都刷新成最新的網(wǎng)絡(luò)數(shù)據(jù),因此在下拉刷新后,要做下面幾件事:

(1)想辦法刷新當(dāng)前還活著的Fragment

如第二個GIF圖所示,當(dāng)在Fragment4的時候,下拉刷新,則此時活著的Fragment就只剩下Fragment4及Fragment3了,那么如何刷新Fragment4及Fragment3呢?

1)刷新Fragment4

因為是在Fragment4頁面進行下拉刷新的,所以可以在下拉刷新回調(diào)事件中調(diào)用:

@Override
public void onComplete() {
    Log.d(TAG, "onComplete... ");
    mMyFragmentAdapter.clearSavedState();
    // 當(dāng)下拉刷新后,分別將當(dāng)前頁面進行刷新、左右頁面在頁面選擇后再進行刷新
    // 刷新當(dāng)前頁面
    int currentPosition = mViewPager.getCurrentItem();
    refreshUI(currentPosition);
    // 記錄左邊位置,待mViewPage切換的時機進行刷新
    mLeftRefresh = currentPosition - 1 >= 0 ? currentPosition - 1 : -1;
    // 記錄右邊位置,待mViewPage切換的時機進行刷新
    mRightRefresh = currentPosition + 1 <= mTabItemList.size() ? currentPosition + 1 : -1;
}
// 刷新當(dāng)前頁面
private void refreshUI(int position) {
    Fragment currentFragment = mMyFragmentAdapter.getCurrentFragment(position);
    if (currentFragment instanceof DemoFragment) {
        ((DemoFragment) currentFragment).loadNetData();
    }
}

在回調(diào)中,刷新當(dāng)前頁面的時候需要獲取當(dāng)前的Fragment,而Fragment都是由Adapter管理的,所以怎么獲取到呢?可以使用反射去?。簠⒖歼@個getCurrentFragment方法

public class MyFragmentAdapter extends FragmentStatePagerAdapter {
    private static final String TAG = "MyFragmentAdapter";
    public static final String POSITION = "POSITION";
    private List<TabItem> mTabItemList = new ArrayList<>();
    public MyFragmentAdapter(FragmentManager fm) {
        super(fm);
    }
    public void setTabItemList(List<TabItem> tabItemList) {
        mTabItemList = tabItemList;
    }
    @NonNull
    @Override
    public Fragment getItem(int position) {
        Bundle bundle = new Bundle();
        bundle.putInt(POSITION, position);
        Fragment demoFragment = new DemoFragment();
        demoFragment.setArguments(bundle);
        return demoFragment;
    }
    @Override
    public int getCount() {
        return mTabItemList.size();
    }
    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return mTabItemList.get(position).getName();
    }
    public void clearSavedState() {
        try {
            Class clazz = getClass().getSuperclass();
            Field savedStateField = clazz.getDeclaredField("mSavedState");
            savedStateField.setAccessible(true);
            Object object = savedStateField.get(this);
            ArrayList<Fragment.SavedState> savedStates = (ArrayList<Fragment.SavedState>) object;
            savedStates.clear();
        } catch (Exception e) {
            Log.e(TAG, "clear saved state is error");
        }
    }
   // 獲取當(dāng)前的Fragment
    @Nullable
    public Fragment getCurrentFragment(int position) {
        try {
            Class clazz = getClass().getSuperclass();
            Field mFragmentsField = clazz.getDeclaredField("mFragments");
            mFragmentsField.setAccessible(true);
            Object object = mFragmentsField.get(this);
            ArrayList<Fragment> fragmentArrayList = (ArrayList<Fragment>) object;
            return fragmentArrayList.get(position);
        } catch (Exception e) {
            Log.e(TAG, "getCurrentFragment is error");
            return null;
        }
    }
}

2)刷新Fragment3

因為Fragment3此時并沒顯示,可以做一個position標(biāo)記,記錄下當(dāng)切換到Fragment3的時候,進行網(wǎng)絡(luò)請求并刷新。因此,在回調(diào)中,看到的是記錄左邊位置(如果左邊位置不存在,則賦值-1)

// 記錄左邊位置,待mViewPage切換的時機進行刷新
mLeftRefresh = currentPosition - 1 &gt;= 0 ? currentPosition - 1 : -1;

在ViewPager中的頁面切換回調(diào)中,拿到position和mLeftRefresh對比,如果相同則請求網(wǎng)絡(luò)數(shù)據(jù)并刷新:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }
    @Override
    public void onPageSelected(int position) {
        if (position == mLeftRefresh) {
            refreshUI(position);
            mLeftRefresh = -1;
        } else if (position == mRightRefresh) {
            refreshUI(position);
            mRightRefresh = -1;
        }
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    }
});

(2)想辦法刷新已經(jīng)銷毀掉的Fragment

已經(jīng)銷毀掉的Fragment,其內(nèi)存數(shù)據(jù)保存在mSavedState列表中

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();

因此,需要做的就是在下拉刷新之后,將這個mSavedState列表數(shù)據(jù)進行清空即可。

到這個,你可能有思路了,就是反射取FragmentStatePagerAdapter的這個mSaveState私有屬性做clear即可

// 下拉刷新的回調(diào)
@Override
public void onComplete() {
    Log.d(TAG, "onComplete... ");
    // 清除內(nèi)存數(shù)據(jù)
    mMyFragmentAdapter.clearSavedState();
}
// MyFragmentAdapter
public void clearSavedState() {
    try {
        Class clazz = getClass().getSuperclass();
        Field savedStateField = clazz.getDeclaredField("mSavedState");
        savedStateField.setAccessible(true);
        Object object = savedStateField.get(this);
        ArrayList<Fragment.SavedState> savedStates = (ArrayList<Fragment.SavedState>) object;
        savedStates.clear();
    } catch (Exception e) {
        Log.e(TAG, "clear saved state is error");
    }
}

關(guān)于“FragmentStatePagerAdapter如何保存恢復(fù)下拉刷新Fragment內(nèi)存數(shù)據(jù)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI