您的浏览器不支持CSS3,建议使用Firfox、Chrome等浏览器,以取得最佳显示效果

Android Fragment可见性的判断与监听完全实现

Android 2,198℃ 0 9个月前 (11-25)

本文仅适用于support包中的Fragment,没有对Android新的接口android.app.Fragment做测试。

实际开发时,常需要在Fragment可见时,做重新加载数据等操作,但系统没有提供可以直接使用的方法。这里通过改造BaseFragment实现Fragment可见性变化的监听。

Fragment可见的定义

  1. Parent可见。ParentActivity处于前台(Parent为Activity);或ParentFragment可见(Parent为Fragment)。
  2. 如果Fragment在ViewPager中,所在Tab被选中。
  3. Fragment被添加到Parent中、Fragment没有被隐藏。
  4. Fragment.View已经AttachToWindow(View被加到Window中),且View可见。

实现机制

1、ParentActivity可见

  • Fragment.onStart/onStop一般在Activity.onStart/onStop时被调用。
  • 但如果在Activity.onStart之后Fragment才被添加,其onStart方法会在添加后才调用。
  1. public class BaseVisibilityFragment extends Fragment {
  2. /**
  3. * ParentActivity是否可见
  4. */
  5. private boolean mParentActivityVisible = false;
  6. @Override
  7. public void onStart() {
  8. info("onStart");
  9. super.onStart();
  10. onActivityVisibilityChanged(true);
  11. }
  12. @Override
  13. public void onStop() {
  14. info("onStop");
  15. super.onStop();
  16. onActivityVisibilityChanged(false);
  17. }
  18. /**
  19. * ParentActivity可见性改变
  20. */
  21. protected void onActivityVisibilityChanged(boolean visible) {
  22. mParentActivityVisible = visible;
  23. }
  24. }

2、如果Fragment在ViewPager中,所在Tab被选中

  • Tab选中态改变事件,通过setUserVisibleHint回调可以监听
  • 通过getUserVisibleHint()可以读取当前所在Tab是否处于选中态
  • 对于没有Tab的页面,getUserVisibleHint()默认为true。
  1. public class BaseVisibilityFragment extends Fragment {
  2. /**
  3. * Tab切换时会回调此方法。对于没有Tab的页面,{@link Fragment#getUserVisibleHint()}默认为true。
  4. */
  5. @Override
  6. public void setUserVisibleHint(boolean isVisibleToUser) {
  7. info("setUserVisibleHint = " + isVisibleToUser);
  8. super.setUserVisibleHint(isVisibleToUser);
  9. }
  10. }

3、Fragment被添加、Fragment没有隐藏

  • 调用FragmentManager.beginTransaction().add()等相关方法,会导致Fragment被添加和移除。
  • 在回调onAttach和onDetach中可以监听Fragment被添加和移除事件。
  • 调用FragmentManager.showFragment/hideFragment会导致Fragment可见性变化,同时还会设置Fragment中顶层View的visibility。
  • 在回调onHiddenChanged中可监听可见性变化。

判断状态:

  1. boolean Fragment.isAdded();
  2. boolean Fragment.isHidden();

监听事件:

  1. public class BaseVisibilityFragment extends Fragment {
  2. @Override
  3. public void onAttach(Activity activity) {
  4. super.onAttach(activity);
  5. }
  6. @Override
  7. public void onDetach() {
  8. super.onDetach();
  9. }
  10. @Override
  11. public void onHiddenChanged(boolean hidden) {
  12. super.onHiddenChanged(hidden);
  13. }
  14. }

4. Fragment.View已经AttachToWindow,且View可见

  • View创建完成时,在onViewCreated回调中给View添加OnAttachStateChangeListener,可以监听其WindowAttach信息的变化。
  • View的可见性监听,可以通过重写View的方式实现。由于开发时一般很少直接调用Fragment.getView().setVisibility(),可以不考虑这种情况的监听。

判断状态:

  1. View view = Fragment.getView();
  2. view != null && view.isAttachedToWindow() && view.getVisibility() == View.VISIBLE;

监听事件:

  1. public class BaseVisibilityFragment extends Fragment implements View.OnAttachStateChangeListener {
  2. @Override
  3. public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
  4. super.onViewCreated(view, savedInstanceState);
  5. view.addOnAttachStateChangeListener(this);
  6. }
  7. @Override
  8. public void onViewAttachedToWindow(View v) {
  9. LogUtils.i(getClass().getSimpleName(), "onViewAttachedToWindow");
  10. }
  11. @Override
  12. public void onViewDetachedFromWindow(View v) {
  13. LogUtils.i(getClass().getSimpleName(), "onViewDetachedFromWindow");
  14. v.removeOnAttachStateChangeListener(this);
  15. }
  16. }

5、ParentFragment可见

  • 定义一个接口,当Fragment可见性改变时,回调Listener。
  • Fragment在onAttach时检查是否有ParentFragment,如果有,则设置Listener监听ParentFragment的可见性。
  1. public interface OnFragmentVisibilityChangedListener {
  2. void onFragmentVisibilityChanged(boolean visible);
  3. }
  4. public class BaseVisibilityFragment extends Fragment {
  5. private OnFragmentVisibilityChangedListener mListener;
  6. public void setOnVisibilityChangedListener(OnFragmentVisibilityChangedListener listener) {
  7. mListener = listener;
  8. }
  9. @Override
  10. public void onAttach(Context context) {
  11. info("onAttach");
  12. super.onAttach(context);
  13. final Fragment parentFragment = getParentFragment();
  14. if (parentFragment != null && parentFragment instanceof BaseVisibilityFragment) {
  15. mParentFragment = ((BaseVisibilityFragment) parentFragment);
  16. mParentFragment.setOnVisibilityChangedListener(this);
  17. }
  18. }
  19. /**
  20. * 可见性改变
  21. */
  22. protected void onVisibilityChanged(boolean visible) {
  23. info("==> onFragmentVisibilityChanged = " + visible);
  24. if (mListener != null) {
  25. mListener.onFragmentVisibilityChanged(visible);
  26. }
  27. }
  28. }

完整方案

系统提供了一个Fragment.isVisible(),用于判断可见性,源码如下:

  1. /**
  2. * Return true if the fragment is currently visible to the user. This means
  3. * it: (1) has been added, (2) has its view attached to the window, and
  4. * (3) is not hidden.
  5. */
  6. final public boolean isVisible() {
  7. return isAdded() && !isHidden() && mView != null
  8. && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
  9. }

下面是判断和监听Fragment可见性完整的代码(不考虑直接调用Fragment.getView().setVisibility时的监听,因为不容易实现且必要性不大)。要求所有Fragment继承BaseVisibilityFragment基类。

完整的Demo可在此下载 https://github.com/jzj1993/FragmentLifeCycle

  1. public interface OnFragmentVisibilityChangedListener {
  2. void onFragmentVisibilityChanged(boolean visible);
  3. }
  1. import android.content.Context;
  2. import android.os.Bundle;
  3. import android.support.annotation.Nullable;
  4. import android.support.v4.app.Fragment;
  5. import android.util.Log;
  6. import android.view.View;
  7. /**
  8. * Created by jzj on 16/9/5.
  9. */
  10. public class BaseVisibilityFragment extends Fragment implements View.OnAttachStateChangeListener, OnFragmentVisibilityChangedListener {
  11. /**
  12. * ParentActivity是否可见
  13. */
  14. private boolean mParentActivityVisible = false;
  15. /**
  16. * 是否可见(Activity处于前台、Tab被选中、Fragment被添加、Fragment没有隐藏、Fragment.View已经Attach)
  17. */
  18. private boolean mVisible = false;
  19. private BaseVisibilityFragment mParentFragment;
  20. private OnFragmentVisibilityChangedListener mListener;
  21. public void setOnVisibilityChangedListener(OnFragmentVisibilityChangedListener listener) {
  22. mListener = listener;
  23. }
  24. @Override
  25. public void onAttach(Context context) {
  26. info("onAttach");
  27. super.onAttach(context);
  28. final Fragment parentFragment = getParentFragment();
  29. if (parentFragment != null && parentFragment instanceof BaseVisibilityFragment) {
  30. mParentFragment = ((BaseVisibilityFragment) parentFragment);
  31. mParentFragment.setOnVisibilityChangedListener(this);
  32. }
  33. checkVisibility(true);
  34. }
  35. @Override
  36. public void onDetach() {
  37. info("onDetach");
  38. if (mParentFragment != null) {
  39. mParentFragment.setOnVisibilityChangedListener(null);
  40. }
  41. super.onDetach();
  42. checkVisibility(false);
  43. mParentFragment = null;
  44. }
  45. @Override
  46. public void onStart() {
  47. info("onStart");
  48. super.onStart();
  49. onActivityVisibilityChanged(true);
  50. }
  51. @Override
  52. public void onStop() {
  53. info("onStop");
  54. super.onStop();
  55. onActivityVisibilityChanged(false);
  56. }
  57. /**
  58. * ParentActivity可见性改变
  59. */
  60. protected void onActivityVisibilityChanged(boolean visible) {
  61. mParentActivityVisible = visible;
  62. checkVisibility(visible);
  63. }
  64. /**
  65. * ParentFragment可见性改变
  66. */
  67. @Override
  68. public void onFragmentVisibilityChanged(boolean visible) {
  69. checkVisibility(visible);
  70. }
  71. @Override
  72. public void onCreate(@Nullable Bundle savedInstanceState) {
  73. info("onCreate");
  74. super.onCreate(savedInstanceState);
  75. }
  76. @Override
  77. public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
  78. super.onViewCreated(view, savedInstanceState);
  79. view.addOnAttachStateChangeListener(this);
  80. }
  81. @Override
  82. public void onHiddenChanged(boolean hidden) {
  83. super.onHiddenChanged(hidden);
  84. checkVisibility(hidden);
  85. }
  86. /**
  87. * Tab切换时会回调此方法。对于没有Tab的页面,{@link Fragment#getUserVisibleHint()}默认为true。
  88. */
  89. @Override
  90. public void setUserVisibleHint(boolean isVisibleToUser) {
  91. info("setUserVisibleHint = " + isVisibleToUser);
  92. super.setUserVisibleHint(isVisibleToUser);
  93. checkVisibility(isVisibleToUser);
  94. }
  95. @Override
  96. public void onViewAttachedToWindow(View v) {
  97. info("onViewAttachedToWindow");
  98. checkVisibility(true);
  99. }
  100. @Override
  101. public void onViewDetachedFromWindow(View v) {
  102. info("onViewDetachedFromWindow");
  103. v.removeOnAttachStateChangeListener(this);
  104. checkVisibility(false);
  105. }
  106. /**
  107. * 检查可见性是否变化
  108. *
  109. * @param expected 可见性期望的值。只有当前值和expected不同,才需要做判断
  110. */
  111. private void checkVisibility(boolean expected) {
  112. if (expected == mVisible) return;
  113. final boolean parentVisible = mParentFragment == null ? mParentActivityVisible : mParentFragment.isFragmentVisible();
  114. final boolean superVisible = super.isVisible();
  115. final boolean hintVisible = getUserVisibleHint();
  116. final boolean visible = parentVisible && superVisible && hintVisible;
  117. info(String.format("==> checkVisibility = %s ( parent = %s, super = %s, hint = %s )",
  118. visible, parentVisible, superVisible, hintVisible));
  119. if (visible != mVisible) {
  120. mVisible = visible;
  121. onVisibilityChanged(mVisible);
  122. }
  123. }
  124. /**
  125. * 可见性改变
  126. */
  127. protected void onVisibilityChanged(boolean visible) {
  128. info("==> onFragmentVisibilityChanged = " + visible);
  129. if (mListener != null) {
  130. mListener.onFragmentVisibilityChanged(visible);
  131. }
  132. }
  133. /**
  134. * 是否可见(Activity处于前台、Tab被选中、Fragment被添加、Fragment没有隐藏、Fragment.View已经Attach)
  135. */
  136. public boolean isFragmentVisible() {
  137. return mVisible;
  138. }
  139. private void info(String s) {
  140. if (BuildConfig.DEBUG) {
  141. Log.i(getClass().getSimpleName() + " (" + hashCode() + ")", s);
  142. }
  143. }
  144. }
来自为知笔记(Wiz)

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

0

暂无评论

评论前:需填写以下信息,或 登录

用户登录

忘记密码?