解决ViewPager嵌套时Fragment的mUserVisibleHint属性不同步的问题
来源:互联网 发布:linux mv 覆盖目录 编辑:程序博客网 时间:2024/06/05 15:03
2017-1-11日更新:setUserVisibleHint中过滤父Fragment未显示的情况
上一篇【Android】友盟统计Fragment页面显示隐藏的完美解决方案 我们讲了通过Fragment的mUserVisibleHint属性可以准确的监听Fragment在ViewPager中的显示与隐藏
现在新的问题又来了,当ViewPager嵌套ViewPager的时候子ViewPager中Fragment的mUserVisibleHint属性却不会同其父Fragment的mUserVisibleHint同步,这样一来子ViewPager中Fragment的状态的统计就不准确了
举个栗子,现有如下结构:
ViewPagerParent Fragment1 ViewPagerChild1 Fragment11 Fragment12 Fragment2 ViewPagerChild2 Fragment21 Fragment22
初始化完成后的状态是
ViewPagerParent Fragment1.mUserVisibleHint=true ViewPagerChild1 Fragment11.mUserVisibleHint=true Fragment12.mUserVisibleHint=false Fragment2.mUserVisibleHint=false ViewPagerChild2 Fragment21.mUserVisibleHint=true Fragment22.mUserVisibleHint=false
可以明确的看出来这时候Fragment11和Fragment21的mUserVisibleHint属性都是true,如果我们通过上一篇中讲述的方法用getUserVisibleHint()&&isResume()来记录页面显示日志的话一下子会记录Fragment11和Fragment21都显示了,
可这时候我们能看到的只有Fragment11,所以我们不希望Fragment21的mUserVisibleHint也是true,可ViewPager并未提供解决此问题的方法,得靠自己来解决
接下来滑动到Fragment2,这时候的状态是
ViewPagerParent Fragment1.mUserVisibleHint=false ViewPagerChild1 Fragment11.mUserVisibleHint=true Fragment12.mUserVisibleHint=false Fragment2.mUserVisibleHint=true ViewPagerChild2 Fragment21.mUserVisibleHint=true Fragment22.mUserVisibleHint=false
我们可以看到Fragment1的mUserVisibleHint属性已经是false了,可是其子Framgnet11依然还是true,并不会跟父Fragment1同步,这时候我们打开新的Activity再回来重走onResume()方法的时候所有Fragment都会重走onResume()方法,那么这时候所有mUserVisibleHint状态是true的Fragment都会记录一次显示事件
我们先来解决初始化时候让Fragment21.mUserVisibleHint为false
解决思路就是在子Fragment onAttach的时候检查其父Fragment的mUserVisibleHint属性的状态,如果是false就强制将当前子Fragment的mUserVisibleHint属性设置为false并设置一个恢复标记(因为接下来还需要恢复)
然后在父Fragment setUserVisibleHint为true的时候检查其所有子Fragment,有恢复标记的子Fragment,设置其mUserVisibleHint属性为true,看代码:
public class BaseFragment extends Fragment{ private boolean waitingShowToUser; @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 如果自己是显示状态,但父Fragment却是隐藏状态,就把自己也改为隐藏状态,并且设置一个等待显示标记 if(getUserVisibleHint()){ Fragment parentFragment = getParentFragment(); if(parentFragment != null && !parentFragment.getUserVisibleHint()){ waitingShowToUser = true; super.setUserVisibleHint(false); } } } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if(getActivity() != null) { List<Fragment> childFragmentList = getChildFragmentManager().getFragments(); if (isVisibleToUser) { // 将所有正等待显示的子Fragment设置为显示状态,并取消等待显示标记 if (childFragmentList != null && childFragmentList.size() > 0) { for (Fragment childFragment : childFragmentList) { if (childFragment instanceof BaseFragment) { BaseFragment childBaseFragment = (BaseFragment) childFragment; if (childBaseFragment.isWaitingShowToUser()) { childBaseFragment.setWaitingShowToUser(false); childFragment.setUserVisibleHint(true); } } } } } } } public boolean isWaitingShowToUser() { return waitingShowToUser; } public void setWaitingShowToUser(boolean waitingShowToUser) { this.waitingShowToUser = waitingShowToUser; }}
接下来解决父ViewPager切换的时候同步其子Fragment的mUserVisibleHint属性
思路就是在父Fragment setUserVisibleHint为false的时候将其所有mUserVisibleHint为true的子Fragment都强制改为false,然后设置一个恢复标记,然后在setUserVisibleHint为true的时候再恢复,看代码:
public class BaseFragment extends Fragment{ private boolean waitingShowToUser; @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 如果自己是显示状态,但父Fragment却是隐藏状态,就把自己也改为隐藏状态,并且设置一个等待显示标记 if(getUserVisibleHint()){ Fragment parentFragment = getParentFragment(); if(parentFragment != null && !parentFragment.getUserVisibleHint()){ waitingShowToUser = true; super.setUserVisibleHint(false); } } } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); // 父Fragment还没显示,你着什么急 if (isVisibleToUser) { Fragment parentFragment = getParentFragment(); if (parentFragment != null && !parentFragment.getUserVisibleHint()) { waitingShowToUser = true; super.setUserVisibleHint(false); return; } } if(getActivity() != null) { List<Fragment> childFragmentList = getChildFragmentManager().getFragments(); if (isVisibleToUser) { // 将所有正等待显示的子Fragment设置为显示状态,并取消等待显示标记 if (childFragmentList != null && childFragmentList.size() > 0) { for (Fragment childFragment : childFragmentList) { if (childFragment instanceof BaseFragment) { BaseFragment childBaseFragment = (BaseFragment) childFragment; if (childBaseFragment.isWaitingShowToUser()) { childBaseFragment.setWaitingShowToUser(false); childFragment.setUserVisibleHint(true); } } } } } else { // 将所有正在显示的子Fragment设置为隐藏状态,并设置一个等待显示标记 if (childFragmentList != null && childFragmentList.size() > 0) { for (Fragment childFragment : childFragmentList) { if (childFragment instanceof BaseFragment) { BaseFragment childBaseFragment = (BaseFragment) childFragment; if (childFragment.getUserVisibleHint()) { childBaseFragment.setWaitingShowToUser(true); childFragment.setUserVisibleHint(false); } } } } } } } public boolean isWaitingShowToUser() { return waitingShowToUser; } public void setWaitingShowToUser(boolean waitingShowToUser) { this.waitingShowToUser = waitingShowToUser; }}
最后我们结合上一篇文章的需求将对Fragment.mUserVisibleHint属性的管理封装成一个FragmentUserVisibleController.java,如下:
/** * Fragment的mUserVisibleHint属性控制器,用于准确的监听Fragment是否对用户可见 * <br> * <br>mUserVisibleHint属性有什么用? * <br>* 使用ViewPager时我们可以通过Fragment的getUserVisibleHint()&&isResume()方法来判断用户是否能够看见某个Fragment * <br>* 利用这个特性我们可以更精确的统计页面的显示事件和标准化页面初始化流程(真正对用户可见的时候才去请求数据) * <br> * <br>解决BUG * <br>* FragmentUserVisibleController还专门解决了在Fragment或ViewPager嵌套ViewPager时子Fragment的mUserVisibleHint属性与父Fragment的mUserVisibleHint属性不同步的问题 * <br>* 例如外面的Fragment的mUserVisibleHint属性变化时,其包含的ViewPager中的Fragment的mUserVisibleHint属性并不会随着改变,这是ViewPager的BUG * <br> * <br>使用方式(假设你的基类Fragment是MyFragment): * <br>1. 在你的MyFragment的构造函数中New一个FragmentUserVisibleController(一定要在构造函数中new) * <br>2. 重写Fragment的onActivityCreated()、onResume()、onPause()、setUserVisibleHint(boolean)方法,分别调用FragmentUserVisibleController的activityCreated()、resume()、pause()、setUserVisibleHint(boolean)方法 * <br>3. 实现FragmentUserVisibleController.UserVisibleCallback接口并实现以下方法 * <br>    * void setWaitingShowToUser(boolean):直接调用FragmentUserVisibleController的setWaitingShowToUser(boolean)即可 * <br>    * void isWaitingShowToUser():直接调用FragmentUserVisibleController的isWaitingShowToUser()即可 * <br>    * void callSuperSetUserVisibleHint(boolean):调用父Fragment的setUserVisibleHint(boolean)方法即可 * <br>    * void onVisibleToUserChanged(boolean, boolean):当Fragment对用户可见或不可见的就会回调此方法,你可以在这个方法里记录页面显示日志或初始化页面 * <br>    * boolean isVisibleToUser():判断当前Fragment是否对用户可见,直接调用FragmentUserVisibleController的isVisibleToUser()即可 */@SuppressLint("LongLogTag")public class FragmentUserVisibleController { private static final String TAG = "FragmentUserVisibleController"; public static boolean DEBUG = false; @SuppressWarnings("FieldCanBeLocal") private String fragmentName; private boolean waitingShowToUser; private Fragment fragment; private UserVisibleCallback userVisibleCallback; private List<OnUserVisibleListener> userVisibleListenerList; 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()); } 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.d(TAG, fragmentName + ": resume, userVisibleHint=" + fragment.getUserVisibleHint()); } if (fragment.getUserVisibleHint()) { userVisibleCallback.onVisibleToUserChanged(true, true); callbackListener(true, true); if (DEBUG) { Log.i(TAG, fragmentName + ": visibleToUser on resume"); } } } public void pause() { if (DEBUG) { Log.d(TAG, fragmentName + ": pause, userVisibleHint=" + fragment.getUserVisibleHint()); } if (fragment.getUserVisibleHint()) { userVisibleCallback.onVisibleToUserChanged(false, true); callbackListener(false, true); if (DEBUG) { Log.w(TAG, fragmentName + ": hiddenToUser on pause"); } } } public void setUserVisibleHint(boolean isVisibleToUser) { Fragment parentFragment = fragment.getParentFragment(); if (DEBUG) { String parent; if (parentFragment != null) { parent = "parent " + parentFragment.getClass().getSimpleName() + " userVisibleHint=" + parentFragment.getUserVisibleHint(); } else { parent = "parent is null"; } Log.d(TAG, fragmentName + ": setUserVisibleHint, userVisibleHint=" + isVisibleToUser + ", " + (fragment.isResumed() ? "resume" : "pause") + ", " + parent); } // 父Fragment还没显示,你着什么急 if (isVisibleToUser) { if (parentFragment != null && !parentFragment.getUserVisibleHint()) { if (DEBUG) { Log.d(TAG, fragmentName + ": setUserVisibleHint, parent " + parentFragment.getClass().getSimpleName() + " is hidden, therefore hidden self"); } userVisibleCallback.setWaitingShowToUser(true); userVisibleCallback.callSuperSetUserVisibleHint(false); return; } } if (fragment.isResumed()) { userVisibleCallback.onVisibleToUserChanged(isVisibleToUser, false); callbackListener(isVisibleToUser, false); if (DEBUG) { if (isVisibleToUser) { Log.i(TAG, fragmentName + ": visibleToUser on setUserVisibleHint"); } else { Log.w(TAG, fragmentName + ": hiddenToUser on setUserVisibleHint"); } } } if (fragment.getActivity() != null) { List<Fragment> 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); } } } } } } } private void callbackListener(boolean isVisibleToUser, boolean invokeInResumeOrPause) { if (userVisibleListenerList != null && userVisibleListenerList.size() > 0) { for (OnUserVisibleListener listener : userVisibleListenerList) { listener.onVisibleToUserChanged(isVisibleToUser, invokeInResumeOrPause); } } } /** * 当前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 void addOnUserVisibleListener(OnUserVisibleListener listener) { if (listener != null) { if (userVisibleListenerList == null) { userVisibleListenerList = new LinkedList<OnUserVisibleListener>(); } userVisibleListenerList.add(listener); } } public void removeOnUserVisibleListener(OnUserVisibleListener listener) { if (listener != null && userVisibleListenerList != null) { userVisibleListenerList.remove(listener); } } public interface UserVisibleCallback { boolean isWaitingShowToUser(); void setWaitingShowToUser(boolean waitingShowToUser); boolean isVisibleToUser(); void callSuperSetUserVisibleHint(boolean isVisibleToUser); void onVisibleToUserChanged(boolean isVisibleToUser, boolean invokeInResumeOrPause); } public interface OnUserVisibleListener { void onVisibleToUserChanged(boolean isVisibleToUser, boolean invokeInResumeOrPause); }}
使用起来非常简单,可快速集成到你的Fragment中,:
- 在你的基类Fragment的构造函数中New一个FragmentUserVisibleController(一定要在构造函数中new)
- 重写Fragment的onActivityCreated()、onResume()、onPause()、setUserVisibleHint(boolean)方法,分别调用FragmentUserVisibleController的activityCreated()、resume()、pause()、setUserVisibleHint(boolean)方法
- 实现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()即可
如下所示:
public class MyFragment extends Fragment implements FragmentUserVisibleController.UserVisibleCallback{ private FragmentUserVisibleController userVisibleController; public MyFragment() { userVisibleController = new FragmentUserVisibleController(this, this); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); userVisibleController.activityCreated(); } @Override public void onResume() { super.onResume(); userVisibleController.resume(); } @Override public void onPause() { super.onPause(); userVisibleController.pause(); } @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) { }}
转载自http://www.jianshu.com/p/e7449278e33d
- 解决ViewPager嵌套时Fragment的mUserVisibleHint属性不同步的问题
- 解决ViewPager嵌套时Fragment的mUserVisibleHint属性不同步的问题
- Viewpager 嵌套fragment的问题
- viewpager的fragment嵌套viewpager的问题
- ViewPager嵌套Fragment时Fragment的生命周期问题
- fragment里面嵌套ViewPager,解决切换fragment了之后ViewPager中的内容消失的问题
- 关于viewpager+fragment中嵌套viewpager+fragment的问题处理:
- 解决viewpager嵌套scrowllview或者viewpager嵌套viewpager的问题
- ViewPager嵌套ViewPager滑动问题的解决
- 关于fragment中嵌套viewpager的问题
- ViewPager的Fragment嵌套使用,getSupportFragmentManager问题
- viewPager+Fragment的多重嵌套问题
- 解决fragment中嵌套viewpager,vierpager中有多个fragment,不显示的问题
- Android 多层fragment 嵌套时,viewPager不显示的问题
- Fragment中ViewPager嵌套Fragment引起的问题
- Slidingmenu 结合fragment,和viewpager嵌套fragment遇到的问题;
- Fragment 嵌套 Tablayout+ViewPager+fragment 遇到的问题
- 关于ViewPager嵌套Fragment,Fragment使用GridView导致的问题
- Spring:基于注解的Spring MVC(上)
- 隐马尔科夫模型的应用实例:中文分词
- HDU4404 Worms(计算多边形和圆的重叠面积)
- Java 30道经典笔试题
- 音频的mixer
- 解决ViewPager嵌套时Fragment的mUserVisibleHint属性不同步的问题
- 机器学习-实战-入门-不同语种的识别
- 01背包+完全背包 HDU
- android AsyncTask介绍
- 浅谈Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
- (C语言)高精度除法
- 用matlab求有约束条件函数的极值
- Python 异常处理 (四)
- JQuery干货篇之处理元素