1.什么是fragment懶加載以及為什么要使用fragment懶加載。
先看下Demo實現(xiàn)的效果吧迈着。大家對這種效果一定不陌生竭望,知乎,掘金等app都用到了這種效果裕菠。
<figcaption></figcaption>
這里探索的懶加載是當viewpager
結合tablayout
時,多個fragment
在進行數(shù)據(jù)加載的時候咬清,當且僅當fragment
對用戶可見的時候,才進行數(shù)據(jù)加載奴潘,這里的數(shù)據(jù)加載可以是網(wǎng)絡請求旧烧,數(shù)據(jù)庫請求,是需要消耗資源的萤彩,如果不使用懶加載粪滤,對于那些用戶未打開的頁面,由于viewpager
的加載機制雀扶,即使用戶未打開的fragment
也有可能進行數(shù)據(jù)加載杖小,造成資源白白的浪費,因此為了良好的用戶體驗肆汹,懶加載是有必要的。
2.fragment懶加載的實現(xiàn)方式以及封裝予权。
fragment
懶加載實現(xiàn)的關鍵在于其的setUserVisibleHint(isVisibleToUser: Boolean)
方法昂勉,該方法在fragment
對用戶由可見變?yōu)椴豢梢娨约坝刹豢梢娮優(yōu)榭梢姇r都會回調。我們創(chuàng)建抽象AbstractLazyInitFrag
扫腺,對其進行封裝岗照。首先我們引入isVisibleToUser
變量,負責保存當前fragment
對用戶的可見狀態(tài)笆环。同時還有幾個值得注意的地方:
-
setUserVisibleHint(isVisibleToUser: Boolean)
方法的回調時機并沒有與fragment
的生命周期有確切的關聯(lián)攒至,比如說,回調時機有可能在onCreateView
方法之后躁劣,也可能在onCreateView
方法之前迫吐。因此,必須引入一個標志位isPrepareView
判斷view
是否創(chuàng)建完成账忘,不然志膀,很容易會造成空指針異常。我們初始化該變量為false,在onViewCreated
中鳖擒,也就是view
創(chuàng)建完成后溉浙,將其賦值為true
。代碼中是這樣:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
isPrepareView = true
}
復制代碼
- 數(shù)據(jù)初始化只應該加載一次蒋荚,因此戳稽,引入第二個標志位,
isInitData
,初始為false,在數(shù)據(jù)加載完成之后期升,將其賦值為true
广鳍。至此,我們的懶加載方法考慮了所有條件吓妆。也就是當isVisibleToUser
為true
赊时,isInitData
為false
,isPrepareView
為true
時,進行數(shù)據(jù)加載行拢,并且加載后為了防止重復調用祖秒,將isInitData
賦值為true
。代碼如下:
private fun lazyInitData() {
if (!isInitData && isVisibleToUser && isPrepareView) {
isInitData = true
initData()
}
}
復制代碼
其中initData()
為抽象方法舟奠,由子類實現(xiàn)竭缝,在這里操作數(shù)據(jù)加載的邏輯。
- 該方法的調用時機沼瘫。首先是
setUserVisibleHint(isVisibleToUser: Boolean)
方法中是必須調用的抬纸。代碼如下:
/*當fragment由可見變?yōu)椴豢梢姾筒豢梢娮優(yōu)榭梢姇r回調*/
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
this.isVisibleToUser = isVisibleToUser //標志位保存fragment對用戶的可見狀態(tài)
lazyInitData()
}
復制代碼
其次,很容易忽略的一點耿戚。對于上圖中第一個fragment
湿故,如果setUserVisibleHint(isVisibleToUser: Boolean)
方法在onCreateView
之前調用的話,如果懶加載方法只在setUserVisibleHint(isVisibleToUser: Boolean)
中調用,那么該fragment將只能在被主動切換一次之后才能加載數(shù)據(jù)阿趁,這肯定是不可能的,因此坛猪,我們需要在view
創(chuàng)建完成之后脖阵,也進行一次調用。思來想去墅茉,在onActivityCreated
方法中是最合適的命黔。我們在繼承的時候,在onViewCreated
方法中進行一些初始化就行了,這樣不會引起沖突就斤。代碼如下:
/*fragment生命周期中onViewCreated之后的方法 在這里調用一次懶加載 避免第一次可見不加載數(shù)據(jù)*/
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lazyInitData()
}
3.完整代碼:
public abstract class BaseLazyFragment extends Fragment {
private Context mContext;
private View mRootView;
private boolean theFragmentVisible;//判斷當前fragment是否可見
private boolean isPrepared;//通過此標記位防止空指針
private boolean isInitData=false;//此標記位是只能初始化一次數(shù)據(jù)
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext=getActivity();
}
//該方法會在不同的Fragment切換時才會調用
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if (isVisibleToUser){//isVisibleToUser用于判斷當前Fragment是否可見
onVisibleDo();
}else{
onUnVisibleDo();
}
super.setUserVisibleHint(isVisibleToUser);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView==null){
mRootView=LayoutInflater.from(mContext).inflate(getLayoutId(),null);
}
return mRootView;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//設置布局可見悍募,且數(shù)據(jù)準備歐克
theFragmentVisible=true;
isPrepared=true;
//如果setUserVisibleHint()在onCreateView()之前調用。
// 那么該fragment將只能在被主動切換一次之后才能加載數(shù)據(jù)洋机,這肯定是不可能的搜立,因此,我們需要在view創(chuàng)建完成之后槐秧,也進行一次調用
lazyLoad();
}
@Override
public boolean getUserVisibleHint() {
return isVisible();
}
private void lazyLoad(){
if (isPrepared && theFragmentVisible && !isInitData){
loadData();
isInitData=true;
}
}
//頁面不可見:
private void onUnVisibleDo(){
theFragmentVisible=false;
}
//頁面可見:
private void onVisibleDo(){
theFragmentVisible=true;
lazyLoad();
}
//獲取子布局id
public abstract int getLayoutId();
//加載數(shù)據(jù)
public abstract void loadData();
}