android viewpager + fragment 等复杂情况的延时用户可见加载

  1. 需求: 当我们的首页有很多viewpager嵌套的fragment 这个时候如果一次性都加载会非常吃内存。就先考虑不一次加载全部,首先想到的是使用setOffscreenPageLimit(0)使得每次都加载一个,但是这样发现如果要多次切换viewpager就会频繁添加fragment,看来就只能想办法让用户可见的时候加载fragment,并且setOffscreenPageLimit(n),这样又有缓存,又能在用的时候加载。 于是想到setUserVisibleHint 这个方法。 但是发现有时候他不会执行 发现

    @Override
     public void setPrimaryItem(ViewGroup container, int position, Object object) {
         Fragment fragment = (Fragment)object;
         if (fragment != mCurrentPrimaryItem) {
             if (mCurrentPrimaryItem != null) {
                 mCurrentPrimaryItem.setMenuVisibility(false);
                 mCurrentPrimaryItem.setUserVisibleHint(false);
             }
             if (fragment != null) {
                 fragment.setMenuVisibility(true);
                 fragment.setUserVisibleHint(true);
             }
             mCurrentPrimaryItem = fragment;
         }
     }
    

发现setPrimaryItem 这个方法被重写,并且没有调用父方法。 又发现如果fragment嵌套viewpager+fragment又出现UserVisibleHint不同步的问题于是又下面的代码

 * Fragment的mUserVisibleHint属性控制器,用于准确的监听Fragment是否对用户可见
 * 
*
mUserVisibleHint属性有什么用? *
* 使用ViewPager时我们可以通过Fragment的getUserVisibleHint()&&isResume()方法来判断用户是否能够看见某个Fragment *
* 利用这个特性我们可以更精确的统计页面的显示事件和标准化页面初始化流程(真正对用户可见的时候才去请求数据) *
*
解决BUG *
* FragmentUserVisibleController还专门解决了在Fragment或ViewPager嵌套ViewPager时子Fragment的mUserVisibleHint属性与父Fragment的mUserVisibleHint属性不同步的问题 *
* 例如外面的Fragment的mUserVisibleHint属性变化时,其包含的ViewPager中的Fragment的mUserVisibleHint属性并不会随着改变,这是ViewPager的BUG *
*
使用方式(假设你的基类Fragment是MyFragment): *
1. 在你的MyFragment的构造函数中New一个FragmentUserVisibleController(一定要在构造函数中new) *
2. 重写Fragment的onActivityCreated()、onResume()、onPause()、setUserVisibleHint(boolean)方法,分别调用FragmentUserVisibleController的activityCreated()、resume()、pause()、setUserVisibleHint(boolean)方法 *
3. 实现FragmentUserVisibleController.UserVisibleCallback接口并实现以下方法 *
    * void setWaitingShowToUser(boolean):直接调用FragmentUserVisibleController的setWaitingShowToUser(boolean)即可 *
    * void isWaitingShowToUser():直接调用FragmentUserVisibleController的isWaitingShowToUser()即可 *
    * void callSuperSetUserVisibleHint(boolean):调用父Fragment的setUserVisibleHint(boolean)方法即可 *
    * void onVisibleToUserChanged(boolean, boolean):当Fragment对用户可见或不可见的就会回调此方法,你可以在这个方法里记录页面显示日志或初始化页面 *
    * boolean isVisibleToUser():判断当前Fragment是否对用户可见,直接调用FragmentUserVisibleController的isVisibleToUser()即可 */ @SuppressLint("LongLogTag") public class FragmentUserVisibleController { private static final boolean DEBUG = false; private static final String TAG = "FragmentUserVisibleController"; @SuppressWarnings("FieldCanBeLocal") private String fragmentName; private boolean waitingShowToUser; private Fragment fragment; private UserVisibleCallback userVisibleCallback; public FragmentUserVisibleController(Fragment fragment, UserVisibleCallback userVisibleCallback) { this.fragment = fragment; this.userVisibleCallback = userVisibleCallback; //noinspection ConstantConditions this.fragmentName = DEBUG ? fragment.getClass().getSimpleName() : null; } public void activityCreated(){ if(DEBUG){ Log.d(TAG, fragmentName + ": activityCreated, userVisibleHint="+fragment.getUserVisibleHint()); } // 如果自己是显示状态,但父Fragment却是隐藏状态,就把自己也改为隐藏状态,并且设置一个等待显示的标记 if(fragment.getUserVisibleHint()){ Fragment parentFragment = fragment.getParentFragment(); if(parentFragment != null && !parentFragment.getUserVisibleHint()){ if(DEBUG){ Log.d(TAG, fragmentName + ": activityCreated, parent " + parentFragment.getClass().getSimpleName() + "is hidden, therefore hidden self"); } userVisibleCallback.setWaitingShowToUser(true); userVisibleCallback.callSuperSetUserVisibleHint(false); } } } public void resume(){ if (DEBUG) { Log.w(TAG, fragmentName + ": resume, userVisibleHint="+fragment.getUserVisibleHint()); } if (fragment.getUserVisibleHint()) { userVisibleCallback.onVisibleToUserChanged(true, true); } } public void pause(){ if (DEBUG) { Log.w(TAG, fragmentName + ": pause, userVisibleHint="+fragment.getUserVisibleHint()); } if (fragment.getUserVisibleHint()) { userVisibleCallback.onVisibleToUserChanged(false, true); } } public void setUserVisibleHint(boolean isVisibleToUser){ if (DEBUG) { Fragment parentFragment = fragment.getParentFragment(); String parent; if(parentFragment != null){ parent = "parent " + parentFragment.getClass().getSimpleName() + " userVisibleHint="+parentFragment.getUserVisibleHint(); }else{ parent = "parent is null"; } Log.w(TAG, fragmentName + ": setUserVisibleHint, userVisibleHint="+isVisibleToUser+", " + (fragment.isResumed() ? "resume" : "pause") + ", "+parent); } if (fragment.isResumed()) { userVisibleCallback.onVisibleToUserChanged(isVisibleToUser, false); } if(fragment.getActivity() != null) { List childFragmentList = fragment.getChildFragmentManager().getFragments(); if (isVisibleToUser) { // 将所有正等待显示的子Fragment设置为显示状态,并取消等待显示标记 if (childFragmentList != null && childFragmentList.size() > 0) { for (Fragment childFragment : childFragmentList) { if (childFragment instanceof UserVisibleCallback) { UserVisibleCallback userVisibleCallback = (UserVisibleCallback) childFragment; if (userVisibleCallback.isWaitingShowToUser()) { if (DEBUG) { Log.d(TAG, fragmentName + ": setUserVisibleHint, show child " + childFragment.getClass().getSimpleName()); } userVisibleCallback.setWaitingShowToUser(false); childFragment.setUserVisibleHint(true); } } } } } else { // 将所有正在显示的子Fragment设置为隐藏状态,并设置一个等待显示标记 if (childFragmentList != null && childFragmentList.size() > 0) { for (Fragment childFragment : childFragmentList) { if (childFragment instanceof UserVisibleCallback) { UserVisibleCallback userVisibleCallback = (UserVisibleCallback) childFragment; if (childFragment.getUserVisibleHint()) { if (DEBUG) { Log.d(TAG, fragmentName + ": setUserVisibleHint, hidden child " + childFragment.getClass().getSimpleName()); } userVisibleCallback.setWaitingShowToUser(true); childFragment.setUserVisibleHint(false); } } } } } } } /** * 当前Fragment是否对用户可见 */ @SuppressWarnings("unused") public boolean isVisibleToUser(){ return fragment.isResumed() && fragment.getUserVisibleHint(); } public boolean isWaitingShowToUser() { return waitingShowToUser; } public void setWaitingShowToUser(boolean waitingShowToUser) { this.waitingShowToUser = waitingShowToUser; } public interface UserVisibleCallback { void setWaitingShowToUser(boolean waitingShowToUser); boolean isWaitingShowToUser(); boolean isVisibleToUser(); void callSuperSetUserVisibleHint(boolean isVisibleToUser); void onVisibleToUserChanged(boolean isVisibleToUser, boolean invokeInResumeOrPause); } }

是时候来一个全部的BaseFragment代码了

注意 重写setPrimaryItem这个方法的时候要调用父方法super.setPrimaryItem(container,position,object);

public abstract class BaseFragment extends Fragment  implements FragmentUserVisibleController.UserVisibleCallback{
    /** Fragment当前状态是否可见 */
    protected boolean isVisible = false;
    /**
     * 是否初始化过
     */
    protected boolean inited = false;
    private static final String TAG = BaseFragment.class.getSimpleName();
    FragmentUserVisibleController userVisibleController = new FragmentUserVisibleController(this, this);
    protected Activity mActivity; 
    protected View mFragmentView;

    //页面名称,用于友盟统计,定义成类名即可,如BaseFragment
    private String mPageName;

    //供子类使用的加载对话框
    protected LoadingView mLoadingView;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
    }


    /**
     * 可见
     */
    protected void onVisible() {
        LogUtil.e("TAG","i am on Visible");
        if (inited){
            init();
            LogUtil.e("TAG"," i am to init");
            inited = false;
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        userVisibleController.setUserVisibleHint(isVisibleToUser);
    }
    @Override
    public void setWaitingShowToUser(boolean waitingShowToUser) {
        userVisibleController.setWaitingShowToUser(waitingShowToUser);
    }

    @Override
    public boolean isWaitingShowToUser() {
        return userVisibleController.isWaitingShowToUser();
    }

    @Override
    public boolean isVisibleToUser() {
        return userVisibleController.isVisibleToUser();
    }

    @Override
    public void callSuperSetUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
    }

    @Override
    public void onVisibleToUserChanged(boolean isVisibleToUser, boolean invokeInResumeOrPause) {
        LogUtil.e("Tag","i am change visible");
        if(isVisibleToUser) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
        }
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        FrameLayout frameLayout=new FrameLayout(container.getContext());

        View viewRoot = inflater.inflate(getLayoutID(),
                frameLayout, false);


        frameLayout.addView(viewRoot);
        mLoadingView=new LoadingView(container.getContext());
        mLoadingView.setFullScreen(true);
        frameLayout.addView(mLoadingView);

        FinalActivity.initInjectedView(this, frameLayout);
        return frameLayout;
    }
    protected boolean needLazyLoad(){
        return false;
    }
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mActivity = getActivity();
        userVisibleController.activityCreated();
        mFragmentView = this.getView();

        //从子类传递PageName进来,用于友盟页面统计
        mPageName = getPageName();
        inited = true;
        if (needLazyLoad()){
            if (isVisible){
                onVisible();
            }
        }else {
             onVisible();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        userVisibleController.resume();
        LogUtil.i(TAG, mPageName + " onResume");

    }

    @Override
    public void onPause() {
        super.onPause();
        userVisibleController.pause();
        LogUtil.i(TAG, mPageName + " onPause");


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LogUtil.i(TAG, mPageName + " onDestroy");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        LogUtil.i(TAG, mPageName + " onDestroyView");
    }

    public abstract void init();
    /**
     * @description: 设置PageName,用于友盟页面统计(Fragment只统计页面,不统计时长)    
     * @return void    
     */
    protected abstract String getPageName();

    public abstract int getLayoutID();

}