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