【问题标题】:Check if fragment is currently visible or no检查片段当前是否可见
【发布时间】:2017-04-04 12:18:03
【问题描述】:

我知道 StackOverflow 中有很多类似的问题,但我的问题略有不同。

我有嵌套的片段层次结构,如下所示:

                                  Activity
                                     |
                                     |
                                 AFragment
                                     |
                                (ViewPager)
                                 |       |         
                                 |       |         
                         BFragment       BFragment  .....
                            |                       
                       (ViewPager)                       
                        |       |                         
                        |       |                         
                 CFragment     CFragment  ...
                     |
                (ViewPager)                       
                  |     |                              
                  |     |                        
           DFragment   DFragment ...

现在我想知道DFragment 是否向用户显示?

我尝试了很多 StackOverflow 的解决方案,但都没有成功。

我尝试的是:

我尝试了setUserVisibleHint(),但它返回true以上层次结构中的多个DFragment,这是ViewPager的原因

我也从这些链接中尝试过:link1link2link3 等等......但没有得到实际的解决方案。

等待帮助。谢谢。

更新

适配器类

class ViewPagerAdapter extends FragmentPagerAdapter {
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = new ArrayList<>();

        public ViewPagerAdapter(FragmentManager manager) {
            super(manager);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        public void addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
        }
    }

【问题讨论】:

  • 你可以创建一个片段的实例然后检查它
  • 我正在使用 FragmentPagerAdapter 使用 newInstance 显示 Fragments
  • link3 为我工作。你试过吗?
  • 不要创建新实例,让它成为单例
  • @Krish ya 我尝试了 viewpager 更改侦听器但没有运气

标签: java android android-fragments android-viewpager android-view


【解决方案1】:

您可以检查该片段膨胀的View是否在设备的屏幕上可见:

Fragment fragment = ... // retrieve from `ViewPager`'s adapter. View fragmentRootView = fragment.getView(); if (fragmentRootView != null && fragmentRootView.getGlobalVisibleRect(new Rect())) { // fragment is visible } else { // fragment is not visible }

如果视图的一部分在根级别可见,getGlobalVisibleRect() 将返回 true

【讨论】:

  • 在我的情况下,我使用与您建议的相同的代码以其他方式获得片段可见性。当片段可见时,它转到 else 块,当它不可见时,它转到 If 块。
  • 在某些情况下此解决方案不起作用。或者,您可以使用userVisibleHint
  • 我发现这似乎适用于不在 ViewPagers 中的片段,并且只有当您将 getGlobalVisibleRect 调用包装在可运行的帖子中,即 fragmentRootView.post(new Runnable() { public void run() { fragmentRootView.getGlobalVisibleRect(new Rect())) }}) 时。 setUserVisibleHint() 检测方法更适用于 ViewPager 中的片段。
【解决方案2】:

试试这个

@Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {

        }
        else
        {       

        }
    }

【讨论】:

    【解决方案3】:

    试试这个..

    isVisible() 增加了一层额外的可见性检查。

        ViewPager viewPager=(ViewPager)findViewById(R.id.view_pager); 
    
        final Fragment[] fragments={new DFragment(),new MyFragment()};  //add all the other fragment objects present in the view pager......
    
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
            }
    
            @Override
            public void onPageSelected(int position) {
                if(fragments[position].isVisible()){
                    //Cool stuff to do.
                }
            }
    
            @Override
            public void onPageScrollStateChanged(int state) {
    
            }
        });
    

    【讨论】:

      【解决方案4】:

      你可以overridesetUserVisibleHint()在每个你想用附加标志检查它是否可见的片段中。举个例子

      Java:

      boolean isVisited = false;
      
      @Override
      public void setUserVisibleHint(boolean isVisibleToUser) 
      {
          super.setUserVisibleHint(isVisibleToUser);
      
          if (isVisibleToUser && !isVisited ) 
          {
               isVisited = true;
          }
          else if(isVisited)
          {
              // this fragment is already in front of user
          }
      }
      

      我遇到了类似的问题。一旦我需要将数据从服务器加载到fragment,只有当它在用户面前时。我按照上面的方法解决了。

      科特林:

          private var isVisited: Boolean = false
      
      
          override fun setUserVisibleHint(isVisibleToUser: Boolean) {
              super.setUserVisibleHint(isVisibleToUser)
              if (isVisibleToUser && !isVisited!! )
              {
                  isVisited = true;
              }
              else if(isVisited!!)
              {
                  Toast.makeText(activity!!, "Location required", Toast.LENGTH_SHORT).show()
              }
          }
      

      希望对你也有帮助。

      【讨论】:

        【解决方案5】:

        就我所尝试的而言,我认为这会奏效。请尝试让我知道。 在你的活动中做这样的事情。


        public boolean isFragmentVisible(SwipeAdapter swipeAdapter) {
                SwipeAdapter swipeAdapterA = fragmentA.getViewPagerAdapter();
                BFragment bFragment = (BFragment) swipeAdapterA.getFragment(0);
                if (bFragment != null) {
                    SwipeAdapter swipeAdapterB = bFragment.getViewPagerAdapter();
                    CFragment cFragment = (CFragment) swipeAdapterA.getFragment(0);
                    if (cFragment != null) {
                        SwipeAdapter swipeAdapterC = cFragment.getViewPagerAdapter();
                        DFragment dFragment = (DFragment) swipeAdapterA.getFragment(0);
                        if (dFragment != null) {
                            return dFragment.isFragmentVisible();
                        }
                    }
                }
                return false;
            }
        

        使用这个类作为你的 viewpager 适配器

          class SwipeAdapter extends FragmentPagerAdapter {
                private Map<Integer, String> mFragmentTags;
                private FragmentManager mFragmentManager;
        
                private String mPagetile[];
        
                public SwipeAdapter(FragmentManager fm, Context context) {
                    super(fm);
                    mPagetile = context.getResources().getStringArray(R.array.pageTitle);
                    mFragmentManager = fm;
                    mFragmentTags = new HashMap<>();
                }
        
                @Override
                public Fragment getItem(int position) {
                    switch (position) {
                        case 0:
                            return new "Your_fragment";
                        default:
                            break;
                    }
                    return null;
                }
        
                @Override
                public Object instantiateItem(ViewGroup container, int position) {
                    Object obj = super.instantiateItem(container, position);
                    if (obj instanceof Fragment) {
                        Fragment f = (Fragment) obj;
                        String tag = f.getTag();
                        mFragmentTags.put(position, tag);
                    }
                    return obj;
                }
        
                public Fragment getFragment(int position) {
                    String tag = mFragmentTags.get(position);
                    if (tag == null)
                        return null;
                    return mFragmentManager.findFragmentByTag(tag);
                }
        
                @Override
                public CharSequence getPageTitle(int position) {
                    return mPagetile[position];
                }
        
                @Override
                public int getCount() {
                    return "Number of fragments";
                }
            }
        

        现在在您的 DFragment 中执行此操作

         boolean isVisible=false;
        
                @Override
                public void setMenuVisibility(boolean menuVisible) {
                    super.setMenuVisibility(menuVisible);
                    isVisible=menuVisible;
                }
        
                public boolean isFragmentVisible(){
                    return isVisible;
                }
        

        如果不让我知道原因,请告诉我它是否有效。

        【讨论】:

          【解决方案6】:

          我们可以使用mViewPager.getCurrentItem() 方法轻松映射现在可见的片段。该方法给出了int 值编号,我们可以从中找到当前片段。

          -- 对于访问和获取此方法的结果,我们有两个选择:

          1. 在活动中创建Static viewPager 实例并在我们的任何地方使用 想要
          2. 再次在片段中创建 viewPager 实例 (findViewById())

          并在片段中使用上述两种方法中的一种getCurrentItem() 来查找现在可见的片段。

          例如,

          1. 在 MainActivity(用于 ViewPager 的活动) 定义

          public static ViewPager mViewPager;

          • 在 Fragment 中,MainActivity.mViewPager.getCurrentItem() 获取当前片段

          另外,显示哪个片段可见的方法:

          public void whichIsVisible() {
              String msg = "";
              int curFragNo = MainActivity.mViewPager.getCurrentItem();
              switch (curFragNo) {
                  case 0:
                      msg = "AFragment is visible.";
                      break;
                  case 1:
                      msg = "BFragment is visible.";
                      break;
                  case 2:
                      msg = "CFragment is visible.";
                      break;
                  case 3:
                      msg = "DFragment is visible.";
                      break;
              }
              Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
          }
          

          (2) 对于非静态使用,

          • 通过以下代码获取片段中的viewPager实例,

          viewPager=(ViewPager) view.getRootView().findViewById(R.id.container);

          然后

          viewPager.getCurrentItem() 获取索引并根据使用情况查找当前可见片段。

          【讨论】:

          • 这种方法的缺点是,如果您在执行滚动事件时(或在滑动而不松开手指时)尝试检查,您会看到相邻片段的一部分,但 @987654333 @ 将返回上一个片段,而当前有两个可见。
          【解决方案7】:

          最简单的sloution可能是。 在您的DF fragmentonCreate 中,将boolean 存储在preferences 中。即Key = is_fragment_visible_to_user。并将其更改为true。 在onStoponDestroy(无论哪个满足您的要求)中更改,它的值是false。有了这个,您可以通过访问首选项值轻松检查它。在多个实例的情况下,您可以使用另一个键存储标签。

          【讨论】:

          • 这行不通,因为ViewPager 会从左侧和右侧创建片段(默认情况下每侧 1 个),这意味着布尔值将变为 true,但仍然是片段不会显示。
          • 听说过onPageChangeListiner()ViewPager 吗?你知道这个 changeListiner 提供了当前片段的索引吗?例如,如果第一个片段它的活动索引将为 0 等等。因此,也可以在那里应用一个简单的检查。希望你能明白。
          • 在 onCreate 中为真,在 pageChangeListiner 中为假。我希望它可以比这更简单。
          • 是的,那个可能会起作用,但它与 Nitin Patel 提供的解决方案大致相似。不过,我看不到您当前的答案可以按预期工作。
          • 它会为你工作。加上一两次额外检查。但是如果你不喜欢这个也没问题。但这是最简单的。 Nitin 的回答不同,它不会在第一次将片段附加到寻呼机时告诉你。
          【解决方案8】:
          public static boolean isFragmentVisible(Fragment fragment) {
              Activity activity = fragment.getActivity();
              View focusedView = fragment.getView().findFocus();
              return activity != null
                      && focusedView != null
                      && focusedView == activity.getWindow().getDecorView().findFocus();
          }
          

          original post

          【讨论】:

            【解决方案9】:

            当我遇到类似问题时,我通过使用这种 hacky 方法“深入了解”来解决它

            在您的FragmentPagerAdapter(s) 中添加此功能。

            public Fragment getActiveFragment(ViewPager container, int position) {
                    String name = makeFragmentName(container.getId(), position);
                    return fm.findFragmentByTag(name);
                }
            
                private static String makeFragmentName(int viewId, int index) {
                    return "android:switcher:" + viewId + ":" + index;
                }
            

            这不是最优雅的解决方案,但只要它有效

            注意

            这是 ViewPager 内部的私有方法,可以随时更改或针对任何操作系统版本。

            【讨论】:

              【解决方案10】:

              虽然过去这么久了,但我还是想给我的程序,因为我发现得到Fragment的可见状态确实不容易。根据您的要求,您需要解决以下三个问题:

              • 如何使用 FragmentPagerAdapter 或 FragmentStatePagerAdapter
              • 如何判断 Fragment 的可见状态
              • Fragment嵌套时如何处理

              一开始,我定义了一个base Adapter,帮助我在ViewPager中获取Fragment。如果你在ViewPager中Fragment的位置和类型是可变的,你必须使用public int getItemPosition(Object object) methodpublic Fragment getItem(int position)方法来获得正确的位置。

              /**
               * Created by Kilnn on 2017/7/12.
               * A abstract FragmentStatePagerAdapter which hold the fragment reference.
               */
              public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
              
                  private SparseArray<WeakReference<Fragment>> registeredFragments = new SparseArray<>();
              
                  public SmartFragmentStatePagerAdapter(FragmentManager fm) {
                      super(fm);
                  }
              
                  @Override
                  public Object instantiateItem(ViewGroup container, int position) {
                      Fragment fragment = (Fragment) super.instantiateItem(container, position);
                      registeredFragments.put(position, new WeakReference<>(fragment));
                      return fragment;
                  }
              
                  @Override
                  public void destroyItem(ViewGroup container, int position, Object object) {
                      registeredFragments.remove(position);
                      super.destroyItem(container, position, object);
                  }
              
                  public Fragment getRegisteredFragment(int position) {
                      WeakReference<Fragment> reference = registeredFragments.get(position);
                      return reference != null ? reference.get() : null;
                  }
              }
              

              那时,我定义了一个基本的 Fragment 来处理可见状态。但是有一个小缺陷是您必须为一组 Fragment 指定相同的开关类型。

              import android.support.annotation.IntDef;
              import android.support.v4.app.Fragment;
              
              import java.lang.annotation.Retention;
              import java.lang.annotation.RetentionPolicy;
              
              /**
               * Created by Kilnn on 2017/7/12.
               * A smart fragment know itself's visible state.
               */
              public abstract class SmartFragment extends Fragment {
              
                  private boolean isFragmentVisible;
              
                  @Override
                  public void onResume() {
                      super.onResume();
                      int switchType = getSwitchType();
                      if (switchType == ATTACH_DETACH) {
                          notifyOnFragmentVisible();
                      } else if (switchType == SHOW_HIDE) {
                          if (!isHidden()) {
                              notifyOnFragmentVisible();
                          }
                      } else if (switchType == VIEW_PAGER) {
                          //If the parent fragment exist and hidden when activity destroy,
                          //when the activity restore, The parent Fragment  will be restore to hidden state.
                          //And the sub Fragment which in ViewPager is also be restored, and the onResumed() method will callback.
                          //And The sub Fragment's getUserVisibleHint() method will return true  if it is in active position.
                          //So we need to judge the parent Fragment visible state.
                          if (getUserVisibleHint() && isParentFragmentVisible()) {
                              notifyOnFragmentVisible();
                          }
                      }
                  }
              
                  @Override
                  public void setUserVisibleHint(boolean isVisibleToUser) {
                      super.setUserVisibleHint(isVisibleToUser);
                      int switchType = getSwitchType();
                      if (switchType == VIEW_PAGER) {
                          if (isVisibleToUser) {
                              //If ViewPager in ViewPager , the sub ViewPager will call setUserVisibleHint before onResume
                              if (isResumed()) {
                                  notifyOnFragmentVisible();
                              }
                          } else {
                              notifyOnFragmentInvisible();
                          }
                      }
                  }
              
                  @Override
                  public void onHiddenChanged(boolean hidden) {
                      super.onHiddenChanged(hidden);
                      int switchType = getSwitchType();
                      if (switchType == SHOW_HIDE) {
                          if (hidden) {
                              notifyOnFragmentInvisible();
                          } else {
                              //Just judge for safe
                              if (isResumed()) {
                                  notifyOnFragmentVisible();
                              }
                          }
                      }
                  }
              
                  @Override
                  public void onPause() {
                      super.onPause();
                      notifyOnFragmentInvisible();
                  }
              
                  private boolean isParentFragmentVisible() {
                      Fragment parent = getParentFragment();
                      if (parent == null) return true;
                      if (parent instanceof SmartFragment) {
                          return ((SmartFragment) parent).isFragmentVisible();
                      } else {
                          //TODO May be can't get the correct visible state if parent Fragment is not SmartFragment
                          return parent.isVisible();
                      }
                  }
              
                  public boolean isFragmentVisible() {
                      // Don't judge the state of the parent fragment,
                      // because if the parent fragment visible state changes,
                      // you must take the initiative to change the state of the sub fragment
              //        return isFragmentVisible && isParentFragmentVisible();
                      return isFragmentVisible;
                  }
              
                  public void notifyOnFragmentVisible() {
                      if (!isFragmentVisible) {
                          onFragmentVisible();
                          isFragmentVisible = true;
                      }
                  }
              
                  public void notifyOnFragmentInvisible() {
                      if (isFragmentVisible) {
                          onFragmentInvisible();
                          isFragmentVisible = false;
                      }
                  }
              
                  /**
                   * If this method callback, the Fragment must be resumed.
                   */
                  public void onFragmentVisible() {
              
                  }
              
                  /**
                   * If this method callback, the Fragment maybe is resumed or in onPause().
                   */
                  public void onFragmentInvisible() {
              
                  }
              
                  /**
                   * Fragments switch with attach/detach(replace)
                   */
                  public static final int ATTACH_DETACH = 0;
              
                  /**
                   * Fragments switch with show/hide
                   */
                  public static final int SHOW_HIDE = 1;
              
                  /**
                   * Fragments manage by view pager
                   */
                  public static final int VIEW_PAGER = 2;
              
                  @Retention(RetentionPolicy.SOURCE)
                  @IntDef({ATTACH_DETACH, SHOW_HIDE, VIEW_PAGER})
                  public @interface SwitchType {
                  }
              
                  @SwitchType
                  public abstract int getSwitchType();
              
              }
              

              如果嵌套使用,最后将子Fragment的可见状态交给父Fragment。

              public class ParentFragment extends SmartFragment{
              
                  ....
              
                  @Override
                  public void onFragmentVisible() {
                      super.onFragmentVisible();
                      SmartFragment fragment = (SmartFragment) mAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
                      if (fragment != null) {
                          fragment.notifyOnFragmentVisible();
                      }
                  }
              
              
                  @Override
                  public void onFragmentInvisible() {
                      super.onFragmentInvisible();
                      SmartFragment fragment = (SmartFragment) mAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
                      if (fragment != null) {
                          fragment.notifyOnFragmentInvisible();
                      }
                  }
              
                  ...
              
              }
              

              我已经测试了附加/分离、显示/隐藏和三层 ViewPager 嵌套。它工作正常。也许我应该做更多的测试,但对我来说已经足够了。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-04-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多