1. 程式人生 > >Fragment懶載入其實很簡單

Fragment懶載入其實很簡單

前言

記得去年面試的時候, 面了一家小公司, 那個面試官問我, fragment的懶載入做過嗎?我說沒做過(確實沒做過).後來面試快結束了, 又問我, 懶載入沒做過是嗎?後來可想而知也沒收到offer, (ಥ_ಥ)
一直對這個問題耿耿於懷, 不就是fragment的懶載入嗎, 有那麼難?於是這幾天看了一下網上的一些方案, 最終覺得還是自己親手敲一遍好.

原理

懶載入的原理其實挺簡單的, 最主要的就是利用fragment中的setUserVisibleHint(boolean isVisibleToUser)方法中傳進來的那個isVisibleToUser這個引數, 這個引數的字面意思是表示當前fragment是否對使用者可見.注意fragment還有一個getUserVisibleHint()

的方法, 這個方法在我看來其實沒什麼用, 因為我試過列印這個方法的返回值, 返回為true並不能保證使用者切換到了當前fragment.

重寫setUserVisibleHint()

首先定義一個基類, BaseLazyFragment
重寫setUserVisibleHint()這個方法

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.d(TAG, "setUserVisibleHint, isVisibleToUser = " + isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
        // 如果還沒有載入過資料 && 使用者切換到了這個fragment
        // 那就開始載入資料
        if (!mHaveLoadData && isVisibleToUser) {
            loadDataStart();
            mHaveLoadData = true;
        }
    }

用一個布林變數mHaveLoadData來表示該fragment是否載入過資料. 如果沒有載入過資料, 並且isVisibleToUsertrue(表示使用者切換到了這個fragment), 那就開始載入資料, 然後標記該fragment已經載入過資料

loadDataStart()

載入資料這個方法是基類裡的一個抽象方法, 需要子類來重寫, 因為具體的載入過程是需要子類自己來實現的.
寫到這裡我突然想到了那個抽象類和介面有什麼區別的面試題.
在這裡的情景的話, 抽象類就是幫子類統一處理了一些邏輯, 比如判斷什麼時候需要進行載入資料, 這是由父類幫我們做好的, 子類就不需要再寫重複的程式碼了, 而具體的請求過程是由子類自己去實現的.抽象類在這裡的作用就是將重複的邏輯統一處理.
而介面的作用, 就我自己來說用的最多的就是使用一個介面型別的變數來引用一個物件, 這樣就不用去關心這個物件具體是什麼, 而我們只要知道這個物件中一定有介面中的方法, 到時候我們就能呼叫這個物件的方法, 雖然我們並不知道方法中的具體邏輯.
扯遠了.
現在我們寫一個子類繼承這個基類fragment

    @Override
    public void loadDataStart() {
        Log.d(TAG, "loadDataStart");
        // 模擬請求資料
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mData = "這是載入下來的資料";
                // 一旦獲取到資料, 就應該立刻標記資料載入完成
                mLoadDataFinished = true;
                if (mViewInflateFinished) {
                    mTextView.setVisibility(View.VISIBLE);
                    mTextView.setText(mData);
                    mTextView.setText("這是改變後的資料");
                    mPb.setVisibility(View.GONE);
                }
            }
        }, 3000);
    }

在具體的fragment中模擬請求資料, 在請求完成後將mLoadDataFinished這個變數置為true, 這個欄位是繼承自基類fragment的. 然後再判斷是否佈局載入和找控制元件已經完成, 防止將資料設定到控制元件上的時候出現控制元件空指標的錯誤.
那麼我們是在那裡將mViewInflateFinished置為true的呢?在去看基類

onCreateView()

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        if (mRootView != null) {
            return mRootView;
        }
        mRootView = initRootView(inflater, container, savedInstanceState);
        findViewById(mRootView);
        mViewInflateFinished = true;
        return mRootView;
    }

我們回到基類看onCeateView()方法, 在這裡將佈局用一個mRootView的全域性變數儲存起來是因為當viewpager中的fragment比較多的時候, 切換到別的fragment會導致回撥onDestroyView()方法, 再切回來的時候導致onCreateView()onViewCreate()方法又被呼叫, 為了防止fragment重新從layout檔案中載入佈局導致之前設定到控制元件上的變數和狀態丟失, 在佈局初次載入完成之後用mRootView這個變數儲存起來, 當這個變數不為null時就直接複用這個佈局就好了.
initRootView()是初次從layout檔案載入佈局的方法, 是一個抽象方法, 由子類具體去實現, 返回的View表示fragment的佈局.
在初次載入佈局完成之後就是找控制元件的findViewById()方法了, 這也是個抽象方法, 需要子類自己去實現.
findViewById()完成之後就將mViewInflateFinished置為true, 表示載入佈局和找控制元件完成.

findViewById()

我們再去看子類具體實現的findViewById()方法.

    @Override
    protected void findViewById(View view) {
        mTextView = view.findViewById(R.id.section_label);
        mPb = view.findViewById(R.id.pb);
        if (mLoadDataFinished) { // 一般情況下這時候資料請求都還沒完成, 所以不會進這個if
            mTextView.setVisibility(View.VISIBLE);
            mTextView.setText(mData);
            mPb.setVisibility(View.GONE);
        }
    }

在這個方法裡, 如果找控制元件完成之後, 我們立刻將資料設定到控制元件上.但是在這之前我們還是需要判斷一下是否資料已經載入完成, 如果資料已經載入完成, 那就將資料設定到控制元件上.這個mLoadDataFinished標誌位在上面已經提過.

initRootView

這個方法其實沒什麼好說的

    @Override
    protected View initRootView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.d(TAG, "initRootView");
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

完整程式碼

再來貼一下基類和子類的完整程式碼
BaseLazyFragment

public abstract class BaseLazyFragment extends Fragment {

    public final String TAG = getClass().getSimpleName();

    public boolean mHaveLoadData; // 表示是否已經請求過資料

    public boolean mLoadDataFinished; // 表示資料是否已經請求完畢
    private View mRootView;

    // 表示開始載入資料, 但不表示資料載入已經完成
    public abstract void loadDataStart();

    // 表示找控制元件完成, 給控制元件們設定資料不會報空指標了
    public boolean mViewInflateFinished;

    @Override
    public void onAttach(Context context) {
        Log.d(TAG, "onAttach");
        super.onAttach(context);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        if (mRootView != null) {
            return mRootView;
        }
        mRootView = initRootView(inflater, container, savedInstanceState);
        findViewById(mRootView);
        mViewInflateFinished = true;
        return mRootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "onViewCreated");
    }

    protected abstract void findViewById(View view);

    protected abstract View initRootView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState);

    @Override
    public void onDestroyView() {
        Log.d(TAG, "onDestroyView");
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public void onDetach() {
        Log.d(TAG, "onDetach");
        super.onDetach();
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.d(TAG, "setUserVisibleHint, isVisibleToUser = " + isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
        // 如果還沒有載入過資料 && 使用者切換到了這個fragment
        // 那就開始載入資料
        if (!mHaveLoadData && isVisibleToUser) {
            loadDataStart();
            mHaveLoadData = true;
        }
    }

}

子類PlaceholderFragment0

public class PlaceholderFragment0 extends BaseLazyFragment {

    private TextView mTextView;
    private ProgressBar mPb;
    private Handler mHandler = new Handler();
    private String mData;

    public PlaceholderFragment0() {
    }

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static PlaceholderFragment0 newInstance() {
        PlaceholderFragment0 fragment = new PlaceholderFragment0();
        return fragment;
    }

    @Override
    public void loadDataStart() {
        Log.d(TAG, "loadDataStart");
        // 模擬請求資料
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mData = "這是載入下來的資料";
                // 一旦獲取到資料, 就應該立刻標記資料載入完成
                mLoadDataFinished = true;
                if (mViewInflateFinished) { // mViewInflateFinished一般都是true
                    mTextView.setVisibility(View.VISIBLE);
                    mTextView.setText(mData);
                    mTextView.setText("這是改變後的資料");
                    mPb.setVisibility(View.GONE);
                }
            }
        }, 3000);
    }

    @Override
    protected View initRootView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.d(TAG, "initRootView");
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

    @Override
    protected void findViewById(View view) {
        mTextView = view.findViewById(R.id.section_label);
        mPb = view.findViewById(R.id.pb);
        if (mLoadDataFinished) { // 一般情況下這時候資料請求都還沒完成, 所以不會進這個if
            mTextView.setVisibility(View.VISIBLE);
            mTextView.setText(mData);
            mPb.setVisibility(View.GONE);
        }
    }

}

相關推薦

Fragment載入其實簡單

前言 記得去年面試的時候, 面了一家小公司, 那個面試官問我, fragment的懶載入做過嗎?我說沒做過(確實沒做過).後來

將圖片轉換成文字其實簡單

很多朋友們會在各大論壇或者網站上看到一些比較喜歡的文章或者是文字,但是很多文章會受到版權的限制或者其他的各種原因導致無法直接下載,就得不到自己喜歡的那些文字了。 這裏給大家發一個福利,就是我們的捷速OCR文字識別軟件,只要你把你想要的文字用截屏截成圖片,然後在捷速中進行文字識別,立

手把手教你開發chrome擴展一:開發Chrome Extenstion其實簡單

evernote 把手 擴展 data文件夾 現在 效果 界面 nag blog 手把手教你開發chrome擴展一:開發Chrome Extenstion其實很簡單 手把手教你開發Chrome擴展二:為html添加行為 手把手教你開發Chrome擴展三:關於本地存儲數據

這麽說吧,java線程池的實現原理其實簡單

arr nan ads stop shc 線程異常 fixed 響應 submit 好處 : 線程是稀缺資源,如果被無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,合理的使用線程池對線程進行統一分配、調優和監控,有以下好處: 1、降低資源消耗; 2、提高響應速度;

補碼(為什麽按位取反再加一):告訴你一個其實簡單的問題

滿足 所有 我們 進位 數字 樂意 如果 二進制 關系   首先,閱讀這篇文章的你,肯定是一個在網上已經糾結了很久的讀者,因為你查閱了所有你能查到的資料,然後他們都會很耐心的告訴你,補碼:就是按位取反,然後加一。準確無誤,毫無破綻。但是,你搜遍了所有俯拾即是而且準確無誤的答

SQL註入其實簡單,別一不留神就被利用了

SQL註入 sqlmap SQL防註入 SQL註入這個詞相信大家應該都不陌生,而且每年都會有這樣子的事情發生,下面我先帶大家回憶11年兩期起比較經典的案例事件: 1、SONY索尼事件2011年4月,著名的×××組織Anonymous***SONY一個網站,一星期後才被發現7千萬的用戶個人信息,其中

漢諾塔問題其實簡單

推出 class 回溯思想 除了 source 問題 容易 假設 為我 首先上代碼 1 def hanoi_move(n, source, dest, intermediate): 2 if n >= 1: # 遞歸出口,只剩一個盤子 3

面向物件程式設計其實簡單——Python 面向物件(初級篇)

概述 面向過程:根據業務邏輯從上到下寫壘程式碼 函式式:將某功能程式碼封裝到函式中,日後便無需重複編寫,僅呼叫函式即可 面向物件:對函式進行分類和封裝,讓開發“更快更好更強...” 面向過程程式設計最易被初學者接受,其往往用一長段程式碼來實現指定功能,開發過

dubbo其實簡單,就是一個遠端服務呼叫的框架(1)

dubbo專題」dubbo其實很簡單,就是一個遠端服務呼叫的框架(1) 一、dubbo是什麼? 1)本質:一個Jar包,一個分散式框架,,一個遠端服務呼叫的分散式框架。 既然是新手教學,肯定很多同學不明白什麼是分散式和遠端服務呼叫,為什麼要分散式,為什麼要遠端呼叫。我簡單畫個對比圖說明(

初學XPath,其實簡單

XPath 是一門在 XML 文件中查詢資訊的語言。XPath 用於在 XML 文件中通過元素和屬性進行導航。 (我的理解:XPath 就是一個用來查詢xml節點的路徑語言,一個路徑字串語法) XML 例項文件 我們將在下面的例子中使用這個 XML 文件。 <?xml vers

從壹開始前後端分離 [ Vue2.0+.NET Core2.1] 二十三║Vue實戰:Vuex 其實簡單

前言 哈嘍大家週五好,馬上又是一個週末了,下週就是中秋了,下下週就是國慶啦,這裡先祝福大家一個比一個假日嗨皮啦~~轉眼我們的專題已經寫了第 23 篇了,好幾次都堅持不下去想要中斷,不過每當看到群裡的交流,看到部落格下邊好多小夥伴提出問題,我又燃起了鬥志,不過這兩天感冒了,所以更新的比較晚,這裡也提醒大家,節

Android 熱修復其實簡單

一、什麼是熱修復 熱修復說白了就是”打補丁”,比如你們公司上線一個app,使用者反應有重大bug,需要緊急修復。如果按照通  常做法,那就是程式猿加班搞定bug,然後測試,重新打包併發布。這樣帶來的問題就是成本高,效率低。於是,熱  修復就應運而生.一般通過事先設定的介面從

Android使用setUserVisibleHint()實現Fragment載入

Fragment 懶載入使用場景 當使用viewpager+adapter作為應用大的佈局時,viewpager會通過setOffscreenPageLimit來設定預載入的專案,不設定setOffscreenPageLimit,則預設為1(設定0無效,可以檢視該方法原始碼知道),也就是

新手也能看懂,訊息佇列其實簡單

該文已加入開源專案:JavaGuide(一份涵蓋大部分Java程式設計師所需要掌握的核心知識的文件類專案,Star 數接近 16k)。地址:https://github.com/Snailclimb/JavaGuide. 本文內容思維導圖: 訊息佇列其實很簡單   “RabbitMQ?”“Kafk

Spring與Struts2的整合其實簡單

下面是整合步驟 一、複製jar檔案。 把struts2-spring-plugin-..*.jar和spring.jar複製到Web工程的WEB-INF/lib目錄下,並且還需要複製commons-logging.jar。 二、配置Struts.objectFactory

自定義控制元件其實簡單2/3

                尊重原創轉載請註明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵權必究!炮兵鎮樓又要開始雞凍人心的一刻了有木有!有木有雞凍! = = ……通過上一節的講解呢我們大致對Android測量控制元件有個

敏捷其實簡單(4)--初識看板

今天我們來介紹一下敏捷開發中常用的第二個實踐,看板方法。其實,看板方法實際上可以說是精益產品開發的重要實踐,與其他敏捷方法相比,它具有更強的可實施性,提升端到端價值交付能力,更好支援系統的改進。而且它也可以和很多其他敏捷方法無縫連結。 看板方法的起源 中文意思帶來誤解 看

新手也能看懂,挑選伺服器其實簡單

經常在一些技術群裡看到有人聊天,說想買臺伺服器,但是價格太貴了,很猶豫。有人就出主意,說阿里雲伺服器便宜啊,也有人說騰訊云云產品促銷活動也不錯呀,也有人推薦華為雲的。 綜合來說,建議只從上面提到的這三家進行選擇,更小的一些雲廠商就不要考慮了。因為買伺服器圖的是穩定高效,大品牌通常是更值得信賴的,特別是售

敏捷其實簡單(9)Scrum Master的七種武器之離別鉤霸王槍箱子

離別鉤, 七種武器裡面代表的是戒驕。其實對於Scrum Master,這個武器也很重要。 組織在敏捷轉型的時候,運行了一段時間之後,運用了一些敏捷實踐,而且從各方面,貌似反響很大,這個時候往往會產生如下想法: 1.我們已經敏捷了, 看站會,看板都用上了