Android setUserVisibleHint-- fragment真正的onResume和onPause方法

weixin_34391854發表於2018-07-20

這個情況僅適合與多個fragment之間切換時統計,而非activity和fragment同時互動,因當時專案為首頁4個fargment時長統計,因此適合,經下面網友評論指出,特在這裡寫出此問題,因最近專案較忙,具體情況以後驗證後會發出博文更新。

現在越來越多的應用會使用viewpager+fragment顯示自己的內容頁,fragment和activity有很多共同點,如下圖就是fragment的生命週期

但是fragment和activity不同的是當呼叫本身的onResume和onPause方法的時候可能並不是當前的fragment在顯示,例如當載入下面這張圖時,當我開啟MainActivity時顯示的是第一個fragment 但此時呼叫的方法步驟如下:

 

08-11 11:33:36.158    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onAttach
08-11 11:33:36.158    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onCreate
08-11 11:33:36.159    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onCreateView
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onActivityCreated
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onResume()
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onAttach
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onCreate
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onCreateView
08-11 11:33:36.161    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onActivityCreated

08-11 11:33:36.161    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onResume()   可見此時雖然使用者看到的是第一個fragment 但第二個fragment onAttech onCreate

onCreateView onActivityCreater onStart(“這個我沒油列印log”)onResume 方法已經呼叫,這會導致如果我們要統計使用者更喜歡哪個fragment的內容時,雖然fragment已經建立並且onResume但其實並沒有顯示這一頁,那麼是什麼原因呢,這是因為android v4包下的viewpager,為了讓使用者在切換過程中不會卡頓,谷歌官方預設當建立第一個fragment方法時回建立第二個fragment以確保使用者滑動時第二個view已經被建立,保持viewPager的平滑滑動效果。

翻閱谷歌api發現viewpager有一個方法即 setOffscreenPageLimit。但當在viewpager設定以下程式碼

viewPager.setOffscreenPageLimit(0);

執行時列印的log和上面完全一致,即就算你設定只載入一個fragment還是會載入第二個fragment,原因是setOffscreenPageLimit中的原始碼時這樣寫的

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();
        }
}

這個 DEFAULT_OFFSCREEN_PAGES 定義如下 private static final intDEFAULT_OFFSCREEN_PAGES= 1

就是如果你設定為0 也沒用。!!!

 為了解決判斷是否fragment當前顯示問題 可以在fragment重寫 setUserVisibleHint(boolean isVisibleToUser)

在fragment新增log日誌

public class Fragment1 extends Fragment {
    private static final String TAG = "Fragment1";
 
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        L.v(TAG, "onAttach");
    }
 
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        L.v(TAG, "setUserVisibleHint " + isVisibleToUser);
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        L.v(TAG, "onCreate");
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        L.v(TAG, "onCreateView");
        View view = inflater.inflate(R.layout.fragment_layout,null);
        TextView tv = (TextView) view.findViewById(R.id.tv1);
        tv.setText(TAG);
        return view;
    }
 
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        L.v(TAG,"onActivityCreated");
    }
 
    @Override
    public void onResume() {
        super.onResume();
        L.v(TAG, "onResume()");
    }
 
    @Override
    public void onPause() {
        super.onPause();
        L.v(TAG,"onPause");
    }
 
    @Override
    public void onStop() {
        super.onStop();
        L.v(TAG,"onStop");
}

啟動activity列印日誌如下:

 

08-11 11:33:36.156    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint false
08-11 11:33:36.156    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ setUserVisibleHint false
08-11 11:33:36.157    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint true
08-11 11:33:36.158    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onAttach
08-11 11:33:36.158    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onCreate
08-11 11:33:36.159    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onCreateView
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onActivityCreated
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onResume()
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onAttach
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onCreate
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onCreateView
08-11 11:33:36.161    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onActivityCreated
08-11 11:33:36.161    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onResume()

當切換到第二個fragment時列印日誌:

08-11 11:33:54.084    7162-7162/com.example.yinsgo.myui V/Fragment3﹕ setUserVisibleHint false
08-11 11:33:54.084    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint false
08-11 11:33:54.084    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ setUserVisibleHint true
08-11 11:33:54.084    7162-7162/com.example.yinsgo.myui V/Fragment3﹕ onAttach
08-11 11:33:54.085    7162-7162/com.example.yinsgo.myui V/Fragment3﹕ onCreate
08-11 11:33:54.085    7162-7162/com.example.yinsgo.myui V/Fragment3﹕ onCreateView
08-11 11:33:54.085    7162-7162/com.example.yinsgo.myui V/Fragment3﹕ onActivityCreated
08-11 11:33:54.085    7162-7162/com.example.yinsgo.myui V/Fragment3﹕ onResume()

可見當fragment顯示時回撥用方法 setUserVisibleHint中的isVisibleToUser = true 當fragment被切換隱藏時回 isVisibleToUser = false;

所以當我們要統計是否使用者看到一個fragment時可以執行一下程式碼

@Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        L.v(TAG, "setUserVisibleHint " + isVisibleToUser);
        if (isVisibleToUser) {
            //統計程式碼 或者 fragment顯示操作
        } else {
            
        }
    }

同時根據這個方法還可以進行資料的延遲載入,後面再寫。

今天又看了一下發現單純的執行上面的程式碼是有問題的,因為如果我們在else中認為使用者是離開介面其實是不對的,因為根據啟動第一個Fragment的log日誌

08-11 11:33:36.156    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint false
08-11 11:33:36.156    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ setUserVisibleHint false
08-11 11:33:36.157    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint true
08-11 11:33:36.158    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onAttach
08-11 11:33:36.158    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onCreate
08-11 11:33:36.159    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onCreateView
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onActivityCreated
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment1﹕ onResume()
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onAttach
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onCreate
08-11 11:33:36.160    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onCreateView
08-11 11:33:36.161    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onActivityCreated
08-11 11:33:36.161    7162-7162/com.example.yinsgo.myui V/Fragment2﹕ onResume()

第一次setUserVisibleHint 方法 isVisibleToUser 是false 但其實這個時候只是還未初始化,並不是使用者已經瀏覽介面準備離開,於是這裡我們需要一個輔助標記變數具體程式碼如下:

/**
     * 判斷是否是初始化Fragment
     */
    private boolean hasStarted = false;
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        L.v(TAG, "setUserVisibleHint " + isVisibleToUser);
        if (isVisibleToUser) {
            hasStarted = true;
            L.v(TAG,"開始介面");
        } else {
            if (hasStarted) {
                hasStarted = false;
                L.v(TAG,"結束介面");
            }
        }
    }

當我們啟動MainActivity時 Log列印如下:

08-13 17:55:45.850  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint false
08-13 17:55:45.850  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ setUserVisibleHint false
08-13 17:55:45.850  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onAttach
08-13 17:55:45.850  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onCreate
08-13 17:55:45.850  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint true
08-13 17:55:45.860  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ 開始介面
08-13 17:55:45.860  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onCreateView
08-13 17:55:45.870  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onActivityCreated
08-13 17:55:45.880  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onResume()
08-13 17:55:45.880  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ onAttach
08-13 17:55:45.880  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ onCreate
08-13 17:55:45.880  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ onCreateView
08-13 17:55:45.880  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ onActivityCreated
08-13 17:55:45.880  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ onResume()

切換到第二個fragment時,此時離開第一個fagment  log列印如下

08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ setUserVisibleHint false
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ setUserVisibleHint false
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ 結束介面
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ setUserVisibleHint true
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ 開始介面
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ onAttach
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ onCreate
08-13 17:57:04.310  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ onCreateView
08-13 17:57:04.320  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ onActivityCreated
08-13 17:57:04.320  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ onResume()

切換到第三個fragment時,此時離開第二個fragment log列印如下:

08-13 17:58:15.040  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ setUserVisibleHint false
08-13 17:58:15.040  21467-21467/com.example.yinsgo.myui V/Fragment2﹕ 結束介面
08-13 17:58:15.040  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ setUserVisibleHint true
08-13 17:58:15.040  21467-21467/com.example.yinsgo.myui V/Fragment3﹕ 開始介面
08-13 17:58:15.040  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onPause
08-13 17:58:15.040  21467-21467/com.example.yinsgo.myui V/Fragment1﹕ onStop

可見這樣就可以準確統計使用者是否離開或者開始瀏覽介面了。根據這些開始和離開可以統計使用者停留介面的時間等資料。

相關文章