检查片段当前是否可见

时间:2017-04-04 12:18:03

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

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

我有嵌套的Fragments层次结构,如下面的结构:

                                  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);
        }
    }

10 个答案:

答案 0 :(得分:20)

您可以检查设备的屏幕上是否显示了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

答案 1 :(得分:4)

试试这个

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

        }
        else
        {       

        }
    }

答案 2 :(得分:4)

您可以在每个片段中使用override setUserVisibleHint()检查其是否可见并带有其他标记。例如

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,只有当它在用户面前时。我解决了上面的问题。

希望它也会对你有帮助。

答案 3 :(得分: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 :(得分:3)

据我所知,我觉得这会有效请尝试让我知道。 在你的活动中做这样的事情。

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;
        }

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

答案 5 :(得分:3)

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

- 对于访问并获得此方法结果,我们有两个选项:

  1. 在活动中制作Static viewPager实例,并在我们的任何地方使用     想
  2. 再次在片段(viewPager
  3. 中制作findViewById()个实例

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

    例如,

    1. 在MainActivity中(用于ViewPager的活动) 定义
    2. public static ViewPager mViewPager;

      • 在片段中,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()获取索引并根据用途查找当前可见的片段。

答案 6 :(得分:2)

最简单的诽谤可能是。 在onCreate商店的DF fragment booleanpreferences。即Key = is_fragment_visible_to_user。并将其更改为true。 在onStoponDestroy(根据您的要求填写)更改时,它的值为false。有了这个,您可以通过访问首选项值轻松检查它。在多个实例的情况下,您可以使用另一个键存储标记。

答案 7 :(得分:0)

当我遇到类似的问题时,我通过使用这种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 内部的私有方法,可随时更改或任何操作系统版本。

答案 8 :(得分:0)

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

  • 如何使用FragmentPagerAdapter或FragmentStatePagerAdapter
  • 如何确定Fragment的可见状态
  • 如何处理嵌套片段

首先,我定义了一个基础适配器帮助我在ViewPager中获取片段。如果您在ViewPager中片段的位置和类型可以更改,则必须使用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;
    }
}

然后,我定义了一个基本片段来处理可见状态。但是有一个小缺陷是你必须为一组片段指定相同的开关类型。

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();

}

最后,如果嵌套使用,请在父片段中传递子片段可见状态。

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。它工作正常。也许我应该做更多的测试,但对我来说就足够了。

答案 9 :(得分:0)

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