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 true
pour 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);
}
}
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.
Vous pouvez override
setUserVisibleHint()
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.
essaye ça
@Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
}
else
{
}
}
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) {
}
});
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.
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é.
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.
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:
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.
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();
}