web-dev-qa-db-fra.com

Vérifier si le fragment est actuellement visible ou non

Je sais qu'il y a beaucoup de questions similaires dans StackOverflow, mais ma question est un peu différente.

J'ai imbriqué la hiérarchie des fragments comme dans la structure ci-dessous:

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

Maintenant, je veux savoir si DFragment montre à l'utilisateur ou non?

J'ai essayé beaucoup de solutions de StackOverflow mais je n'ai pas réussi. 

Ce que j'ai essayé c'est:

J'ai essayé setUserVisibleHint() mais il retourne truepour plusieurs DFragment dans la hiérarchie ci-dessus, ce qui est une cause de ViewPager

J'ai aussi essayé de ces liens: link1 , link2 , link3 et ainsi de suite ... mais je n'ai pas eu de solution réelle.

En attente d'aide. Je vous remercie.

METTRE À JOUR

Classe d'adaptateur

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

Vous pouvez vérifier si la variable View de ce fragment gonflé est visible sur l'écran du périphérique:


    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() retournera true si une partie de la vue est visible au niveau racine.

19
azizbekian

Vous pouvez overridesetUserVisibleHint() dans chaque fragment où vous souhaitez vérifier s'il est visible ou non avec un indicateur supplémentaire. À titre d'exemple

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

J'avais un problème similaire. Une fois, j’avais besoin de charger les données du serveur dans fragment uniquement quand c’était devant l’utilisateur. Je l'ai résolu comme ci-dessus.

J'espère que cela vous aidera aussi.

4
Geek

essaye ça

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

        }
        else
        {       

        }
    }
4
faisal iqbal

Essayez ceci ..

isVisible() ajoute une couche supplémentaire de contrôle de visibilité.

    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

Pour autant que j'ai essayé, je pense que cela fonctionnera. Essayez de me le faire savoir Faites quelque chose comme ça dans votre activité.


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

Utilisez cette classe comme adaptateur 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";
        }
    }

Maintenant, dans votre DFragment faire ceci

 boolean isVisible=false;

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

        public boolean isFragmentVisible(){
            return isVisible;
        }

Faites-moi savoir si cela a fonctionné sinon laissez-moi savoir la raison.

3
Reyansh Mishra

Le sloution le plus simple pourrait être . Dans onCreate de votre DF fragment, enregistrez une boolean dans preferences. i.e Key = is_fragment_visible_to_user. Et changez-le en true. Et en onStop ou onDestroy (selon ce qui répond à vos besoins) change, sa valeur est false. Avec cela, vous pouvez le vérifier facilement en accédant à la valeur des préférences. Dans le cas d'instances multiples, vous pouvez stocker la balise avec une autre clé. 

2
Nouman Ghaffar

Quand j’ai eu un problème similaire, j’ai résolu le problème en utilisant cette méthode hacky "aller sous le capot"

Dans votre FragmentPagerAdapter (s), ajoutez cette fonction. 

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

Ce n’est pas la solution la plus élégante, mais tant que cela fonctionne

MISE EN GARDE

Il s'agit d'une méthode privée interne à ViewPager qui peut être modifiée à tout moment ou pour toute version du système d'exploitation.

0
insomniac

Bien que le passé pendant si longtemps, mais je veux toujours donner mon programme, parce que j'ai trouvé cet état visible de Fragment n'est en effet pas facile. En fonction de votre demande, vous devez résoudre ces trois questions:

  • Comment utiliser FragmentPagerAdapter ou FragmentStatePagerAdapter
  • Comment déterminer l'état visible de Fragment
  • Comment gérer quand le fragment est imbriqué

Au début, j'ai défini un adaptateur de base m'aidant à obtenir le fragment dans ViewPager. Si la position et le type de fragment sont modifiables dans ViewPager, vous devez utiliser les méthodes public int getItemPosition(Object object) method et public Fragment getItem(int position) pour obtenir la bonne 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;
    }
}

À ce moment-là, je définis un fragment de base pour gérer l'état visible.Mais il existe un petit défaut: vous devez spécifier le même type de commutateur pour un ensemble de fragments.

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

}

Enfin, remettez l'état visible du sous-fragment dans le fragment parent si vous l'utilisez.

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

    ...

}

J'ai tester avec attacher/détacher, afficher/masquer, et trois couches ViewPager imbriquées. Ça marche bien. J'aurais peut-être dû faire plus de tests, mais pour moi c'était suffisant.

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

original post

0