判斷Fragment是否對使用者可見

S丶Mask發表於2017-12-17

如需轉載,請標明原文出處: https://juejin.im/post/5a30c5016fb9a0450a6755b2 ,謝謝。

背景

最近在開發過程中遇到了若干問題,比如

  1. 頁面統計
  2. Banner輪播的啟動與暫停
  3. 等等其他自定義需求

在Activity中這些可以很簡單的達到目的,但是如果頁面中有Fragment,那就無法像Activity那樣簡單的實現,下面針對Fragment不同的使用方法進行分析。

各種使用方法

1. 在Activity中直接使用

這種情況是最簡單的,也就是在Activity使用XML引入,或者使用FragmentManager 的addFragment或者replaceFrament 動態載入。在這種情況下,只要監聽Fragment的onResume和onPause方法就能夠判斷其顯隱。在onResume和onPause中間是對使用者可見的。

@Override
public void onResume() {
    super.onResume();
    // TODO now it's visible to user
}

@Override
public void onPause() {
    super.onPause();
    // TODO now it's invisible to user
}
複製程式碼

2. 使用show和hide來顯隱的Fragment

FragmentManager除了addFragment和replaceFragment之外還有showFragment和hideFragment來做Fragment的顯隱,這樣可以提生頁面切換的速度,是一種用空間換時間的方式。但是這樣使用的Fragment在被hide的時候是不會呼叫onPause方法的。因為它只是在螢幕中不可見了,但是沒有Pause。這時候我們需要監聽onHiddenChanged方法。

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
	if (!hidden) {
		// TODO now it's visible to user
	} else {
		// TODO now it's invisible to user
	}
}
複製程式碼

3. 在ViewPager中的Fragment

現在很多app都會做tab頁,而tab頁基本都是通過android自帶的ViewPager實現的。ViewPager有這樣一個特點,當滑到某一個Tab時,它會同時載入這個tab的左右兩個tab頁,比如我從1頁面切換到了2頁面,那麼3頁面的onResume也被呼叫了,但是3頁面其實對使用者是不可見的。這時候我們就需要監聽setUserVisibleHint來判斷到底對使用者是否可見。

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
	if (isVisibleToUser) {
		// TODO now it's visible to user
	} else {
		// TODO now it's invisible to user
	}
}
複製程式碼

注意!!!注意!!!注意!!!

在以下幾種操作情況下:

  1. startActivity頁面跳轉
  2. 按Home鍵返回主屏
  3. 從其他頁面或主屏返回
  4. 等等和上面類似的操作

只要使用者進行了以上操作,那麼上述列出的所有使用方法中,只要是處於同一個Activity中的Fragment,那麼不管是否顯示,都會且只會呼叫onResume或onPause方法。

比如:

在一個Activity中分別有Fragment A、B,其中在Fragment A中內嵌了Fragment a、b。

  1. 當返回主屏或者跳轉新頁面時,Fragment A、B、a、b四個均會執行onPause方法,而onHiddenChanged和setUserVisibleHint兩個方法不會被執行。
  2. 同樣,當返回Activity頁面時,Fragment A、B、a、b四個均會執行onResume方法,而onHiddenChanged和setUserVisibleHint兩個方法不會被執行。

正是因為上述問題的存在,所以需要onHiddenChanged、setUserVisibleHint、onResume、onPause四個方法相互結合一起來判斷。

程式碼如下

public abstract class BaseFrameFragment extends Fragment {

    public BaseFrameActivity activity;

    private boolean isFirstCreate;// 是否是第一次建立

    private boolean isVisibleToUser;// 當前可見狀態
    private boolean isLastVisibleToUser;// 最後一次可見狀態(onPause前的最後一次狀態)

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        LogUtil.i("onAttach:" + getFragmentName());
        FragmentActivity activity = getActivity();
        if (activity instanceof BaseFrameActivity) {
            this.activity = (BaseFrameActivity) activity;
        } else {
            throw new IllegalArgumentException("Activity 必須繼承 BaseFrameActivity");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        LogUtil.i("onCreateView:" + getFragmentName());
        View layout_root = View.inflate(activity, getContentViewResourceId(), null);
        initData(getArguments());
        initView(layout_root);
        loadData();
        setListener();
        isFirstCreate = true;
        return layout_root;
    }

    /**
     * 使用showFragment和hideFragment來做Fragment的顯隱時判斷Fragment是否可見
     *
     * @param hidden 是否隱藏
     */
    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        LogUtil.i("onHiddenChanged:" + getFragmentName() + " " + hidden);
        isLastVisibleToUser = !hidden;
        onVisibleToUser(isLastVisibleToUser);
        setChildVisibleToUser(isLastVisibleToUser);
    }

    /**
     * 使用ViewPager來做Fragment的顯隱時判斷Fragment是否可見
     *
     * @param isVisibleToUser 是否可見
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (activity == null) {
            LogUtil.i("setUserVisibleHint:" + isVisibleToUser);
            return;
        }
        LogUtil.i("setUserVisibleHint:" + getFragmentName() + " " + isVisibleToUser);
        isLastVisibleToUser = isVisibleToUser;
        onVisibleToUser(isLastVisibleToUser);
        setChildVisibleToUser(isLastVisibleToUser);
    }

    /**
     * 在Activity使用XML引入,或者使用FragmentManager 的addFragment或者replaceFragment 動態載入時判斷Fragment是否可見
     * startActivity切換頁面或者Home鍵切入後臺後回到原頁面也會呼叫此方法
     */
    @Override
    public void onResume() {
        super.onResume();
        LogUtil.i("onResume:" + getFragmentName());
        // 第一次建立且顯示給使用者(ViewPager) 或 回到原頁面前可見狀態且ParentFragment isLastVisibleToUser狀態
        if ((isFirstCreate && getUserVisibleHint()) || (isLastVisibleToUser && getParentFragmentLastVisibleToUser())) {
            onVisibleToUser(true);
        }
    }

    /**
     * 在Activity使用XML引入,或者使用FragmentManager 的addFragment或者replaceFragment 動態載入時判斷Fragment是否可見
     * startActivity切換頁面或者Home鍵切入後臺也會呼叫此方法
     */
    @Override
    public void onPause() {
        super.onPause();
        LogUtil.i("onPause:" + getFragmentName());
        if (isLastVisibleToUser) {
            onVisibleToUser(false);
        }
    }

    /**
     * 獲取ParentFragment isLastVisibleToUser狀態(沒有則為true)
     *
     * @return ParentFragment isLastVisibleToUser
     */
    private boolean getParentFragmentLastVisibleToUser() {
        Fragment parentFragment = getParentFragment();
        if (parentFragment != null) {
            if (parentFragment instanceof BaseFrameFragment) {
                BaseFrameFragment frameFragment = (BaseFrameFragment) parentFragment;
                return frameFragment.isLastVisibleToUser;
            }
        }
        return true;
    }

    /**
     * 設定子Fragment顯示狀態
     *
     * @param isVisibleToUser isVisibleToUser
     */
    private void setChildVisibleToUser(boolean isVisibleToUser) {
        List<Fragment> fragmentList = getChildFragmentManager().getFragments();
        if (!BaseUtils.isEmptyList(fragmentList)) {
            for (Fragment fragment : fragmentList) {
                if (fragment instanceof BaseFrameFragment) {
                    BaseFrameFragment frameFragment = (BaseFrameFragment) fragment;
                    if (frameFragment.isLastVisibleToUser) {
                        frameFragment.onVisibleToUser(isVisibleToUser);
                    }
                }
            }
        }
    }

    public abstract String getFragmentName();

    /**
     * 獲取根佈局ResourceId
     *
     * @return 根佈局ResourceId
     */
    public abstract int getContentViewResourceId();

    /**
     * 初始化資料,主要包括拆包從activity傳遞過來的引數,介面卡初始化,集合初始化等,不可進行view的操作
     *
     * @param arguments 接收到的從其他地方傳遞過來的引數
     */
    public abstract void initData(Bundle arguments);

    /**
     * 初始化View
     *
     * @param layout_root Fragment View
     */
    public abstract void initView(View layout_root);

    /**
     * 載入資料,在onCreateView方法中呼叫可以直接理解為載入資料,方法中可以進行view的操作
     */
    public abstract void loadData();

    /**
     * 設定監聽
     */
    public abstract void setListener();

    /**
     * 對使用者可見/不可見時執行的操作(類似Activity的onResume/onPause方法)
     *
     * @param isVisibleToUser 是否對使用者顯示可見
     */
    public void onVisibleToUser(boolean isVisibleToUser) {
        if (this.isVisibleToUser == isVisibleToUser) {
            return;
        }
        LogUtil.i("onVisibleToUser:" + getFragmentName() + " " + isVisibleToUser);
        this.isVisibleToUser = isVisibleToUser;
        isFirstCreate = false;
        if (isVisibleToUser) {
            isLastVisibleToUser = true;
        }
    }

}

複製程式碼

具體使用

public class A_TempFragment extends BaseFrameFragment {

    @Override
    public String getFragmentName() {
        return getString(R.string.app_name);
    }

    @Override
    public int getContentViewResourceId() {
        return R.layout.activity_base;
    }

    @Override
    public void initData(Bundle arguments) {

    }

    @Override
    public void initView(View layout_root) {
        ButterKnife.bind(this, layout_root);
    }

    @Override
    public void loadData() {

    }

    @Override
    public void setListener() {

    }

    @Override
    public void onVisibleToUser(boolean isVisibleToUser) {
        super.onVisibleToUser(isVisibleToUser);
        if (isVisibleToUser) {
            // TODO now it's visible to user
        } else {
            // TODO now it's invisible to user
        }
    }
    
}
複製程式碼

目前在Activity嵌入Fragment,Fragment內嵌ViewPager+Fragment使用中,一切正常 歡迎一起討論

參考資料

www.cnblogs.com/dongweiq/p/…

相關文章