it-swarm.com.ru

Проверьте, виден ли фрагмент в данный момент или нет

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

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

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

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

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

То, что я пытался это:

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

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

Жду помощи. Спасибо.

ОБНОВЛЕНИЕ

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

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);
        }
    }
13
Vikash Parajuli

Вы можете проверить, видна ли 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, если часть представления видна на корневом уровне.

19
azizbekian

Вы можете overridesetUserVisibleHint() в каждом фрагменте, где вы хотите проверить, является ли он видимым или нет с дополнительным флагом. Для примера

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
Geek

попробуй это

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

        }
        else
        {       

        }
    }
4
faisal iqbal

Дайте это попробовать ..

Функция 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) {

        }
    });
3
Darshan Miskin

Мы можем легко отобразить, какой фрагмент теперь видим, используя метод mViewPager.getCurrentItem(). Этот метод дает int значение, из которого мы можем найти текущий фрагмент.

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

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

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

Например,

  1. В MainActivity (действие, которое используется для ViewPager) Define

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

3
Nitin Patel

Насколько я пытался, я думаю, что это сработает. Пожалуйста, попробуйте и дайте мне знать. Сделайте что-то подобное в своей деятельности.


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

Дайте мне знать, если это сработало, если не дайте мне знать причину.

3
Reyansh Mishra

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

2
Nouman Ghaffar

Когда у меня возникла похожая проблема, я решил ее, используя такой хакерский метод, как «прятаться»

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

0
insomniac

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

  • Как использовать 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'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. Работает нормально. Может быть, я должен был сделать больше испытаний, но для меня этого было достаточно.

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

оригинальный пост

0
Mohammad Reza Norouzi