ViewPager+Fragment的模式再常見不過了,以國民應用微信為例,假設微信也是ViewPager+Fragment的實現方式,那表現形式上就是一個ViewPager管理了四個Fragment,左右滑動來回切換。但是ViewPager有一個奇葩的特性叫:預載入,比如開啟微信,首先看到的是第一個Tab(微信),但事實上第二個Tab(通訊錄)已經載入好了。當選擇第二個Tab(通訊錄),第三個Tab(發現)已經載入好了,以此類推。
但上述ViewPager+Fragment的這種組合並不完美,因為我希望使用者選擇了哪個Tab再去載入哪個Tab的資料,而不要去做預載入,假如當前頁面和預載入頁面都有大量的網路請求,可能就會比較慢,有很多請求在排隊。關於這個問題,也有很偏激的做法,比如棄用ViewPager,自己手動管理Fragment,或者直接禁掉ViewPager預載入。有一種比較合適的方案是保持ViewPager預載入的特性,但是隻初始化View,選擇當前Tab的時候再進行網路請求。關於這一方案的實現,也是眾說紛紜,千奇百怪。最後,還是選擇男神Stay的方案。
直接上程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public abstract class BasePageFragment extends Fragment { protected boolean isViewInitiated; protected boolean isVisibleToUser; protected boolean isDataInitiated; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); isViewInitiated = true; prepareFetchData(); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); this.isVisibleToUser = isVisibleToUser; prepareFetchData(); } public abstract void fetchData(); public boolean prepareFetchData() { return prepareFetchData(false); } public boolean prepareFetchData(boolean forceUpdate) { if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) { fetchData(); isDataInitiated = true; return true; } return false; } } |
這是一個父類,看程式碼這裡只有一個setUserVisibleHint需要說下,這是一個相當生僻的方法,我們可以用這個方法來判斷當前UI是否可見,所以在prepareFetchData方法裡我們做如下判斷:就是當前UI可見,並且fragment已經初始化完畢,如果網路資料未載入,那麼請求資料,或者需要強制重新整理頁面,那麼也去請求資料,So easy。子Fragment只需要繼承父類,實現抽象方法,在fetchData()裡做網路請求或者其他耗時操作即可。再在寫個子類吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public class PageFragment extends BasePageFragment { public static PageFragment newInstance(String title){ PageFragment fragment = new PageFragment(); Bundle args = new Bundle(); args.putString("key_fragment_title", title); fragment.setArguments(args); return fragment; } private String title; private TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); title = getArguments().getString("key_fragment_title"); Trace.d(title + ":onCreate"); } @Override public void onResume() { super.onResume(); Trace.d(title + ":onResume"); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Trace.d(title + ":onCreateView"); tv = new TextView(getActivity()); return tv; } @Override public void fetchData() { tv.setText(title); /** * 在這裡請求網路。 */ } } |
如果你也有這樣的需求或者煩惱,保證藥到病除。
有同學說實踐過程中遇到了些問題,比如Fragment只重新整理一次,這個問題只要手動呼叫prepareFetchData(),傳true即可強制重新整理了。還有同學質疑setUserVisibleHint()和onActivityCreated()的執行先後的問題。關於這個請看下圖。