Проверьте, отображается ли фрагмент в данный момент или нет.

Я знаю, что в StackOverflow есть много похожих вопросов, но мой вопрос немного отличается.

У меня есть вложенная иерархия фрагментов, как в нижерасположенной структуре:

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

Теперь я хочу знать, показывает ли DFragment пользователю или нет?

Я попробовал много решений из StackOverflow, но не смог добиться успеха.

Что я пробовал:

Я попробовал setUserVisibleHint(), но он возвращает true для нескольких DFragment в иерархии выше, что является причиной ViewPager

Я также пробовал по этим ссылкам: link1, link2, link3 и т.д.... но не получили фактического решения.

Ожидание помощи. Спасибо.

UPDATE

Класс адаптера

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

Ответ 1

Вы можете проверить, отображается ли View этот фрагмент на экране устройства:


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

getGlobalVisibleRect() вернет true, если часть представления будет видна на корневом уровне.

Ответ 2

попробуйте это

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

        }
        else
        {       

        }
    }

Ответ 3

Вы можете 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 только тогда, когда он был перед пользователем. Я решил это, как указано выше.

Надеюсь, это тоже поможет вам.

Ответ 4

Попробуй попробовать.

Добавляет 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) {

        }
    });

Ответ 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 номер значения, из которого мы можем найти текущий фрагмент.

- Для доступа и получения результата этого метода у нас есть два варианта:

  • сделать Static экземпляр viewPager в действии и использовать везде, где мы   хотите
  • снова создайте экземпляр viewPager в фрагменте (findViewById())

и используя один из выше двух методов getCurrentItem() в фрагменте, чтобы найти, какой фрагмент теперь виден.

Например,

  • В MainActivity (активность, которая используется для ViewPager) определить

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(), чтобы получить индекс и найти текущий видимый фрагмент в соответствии с использованием.

Ответ 7

Самый простой вариант может быть. В onCreate вашего DF fragment сохраните boolean в preferences. я Key = is_fragment_visible_to_user. И измените его на true. И в onStop или onDestroy (в зависимости от вашего требования) измените значение на false. При этом вы можете легко проверить это, обратившись к значению предпочтений. В случае нескольких экземпляров вы можете сохранить тег другим ключом.

Ответ 8

Когда у меня была аналогичная проблема, я решил ее, используя этот хакерский метод "под капотом", например

Внутри FragmentPagerAdapter добавить эту функцию.

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, который может быть изменен в любое время или для любой версии ОС.

Ответ 9

Хотя прошлое так долго, но я все еще хочу дать свою программу, потому что я обнаружил, что получить видимое состояние Фрагмента действительно нелегко. По вашему запросу вам необходимо решить следующие три вопроса:

  • Как использовать FragmentPagerAdapter или FragmentStatePagerAdapter
  • Как определить видимое состояние фрагмента
  • Как обращаться с вложенным фрагментом

Сначала я определил базовый адаптер, чтобы помочь мне получить фрагмент в ViewPager. Если позиция и тип фрагмента изменяются в ViewPager, вы должны использовать метод public int getItemPosition(Object object) method и public 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 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 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();
        }
    }

    ...

}

У меня есть тест с подключением/отсоединением, show/hide и тремя слоями ViewPager. Он работает нормально. Может быть, я должен был провести больше тестов, но для меня этого было достаточно.

Ответ 10

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

оригинальное сообщение