ViewPager.setOffscreenPageLimit (0) ne fonctionne pas comme prévu

100

Les fragments que j'utilise dans mon ViewPagerexemple sont assez gourmands en ressources, je voudrais donc n'en charger qu'un à la fois. Quand j'essaye ce qui suit:

mViewPager.setOffscreenPageLimit(0);
mViewPager.setAdapter(mPagerAdapter);

Ma FragmentStatePagerAdapter.getItem(int position)fonction de remplacement est appelée 3 fois, ce qui se produit lorsque j'appelle mViewPager.setOffscreenPageLimit(1). Je m'attendrais à ce qu'il ne soit appelé qu'une seule fois, car j'ai spécifié 0 pages hors écran.

Je crois que j'appelle tout correctement, car si j'appelle mViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position)est appelé 5 fois comme je m'y attendais.

ViewPager nécessite-t-il au moins 1 pages hors écran, ou est-ce que je fais quelque chose de mal ici?

Chris Lacy
la source
9
J'ai ajouté une demande de fonctionnalité: code.google.com/p/android/issues/detail?id=56667
Mark
Je n'avais que 3 onglets et le réglage a ViewPager.setOffscreenPageLimit(2)fonctionné pour moi. EssayezViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

Réponses:

112

ViewPager nécessite-t-il au moins 1 pages hors écran

Oui. Si je lis correctement le code source, vous devriez recevoir un avertissement à ce sujet dans LogCat, quelque chose comme:

Requested offscreen page limit 0 too small; defaulting to 1
CommonsWare
la source
6
Ce n'est pas une solution! Je veux seulement charger 1 fragment pas 2. Cela est sûrement possible d'une manière ou d'une autre?
HGPB
14
@Haraldo: "C'est sûrement possible d'une manière ou d'une autre?" - pas avec ViewPager. ViewPagercrée ces fragments pour que les vues existent, afin que l'utilisateur puisse les glisser entre elles, avec l'effet animé montrant l'ancienne vue glissant hors de l'écran et la nouvelle vue glissant sur l'écran. Vous êtes invités à essayer d'écrire le vôtre ViewPagerqui peut glisser entre des éléments qui n'existent pas. Vous pouvez en lire plus à ce sujet à code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare
2
Oui, j'ai lu ce numéro. Ce n'est tout simplement pas logique. Je m'attends à être capable setOffscreenPageLimit(0)de 0. Ensuite, je peux gérer moi-même les conséquences. Un fragment vide par défaut peut être un espace réservé par exemple jusqu'à ce qu'un fragment soit chargé. Tout cela pourrait avoir lieu hors du fil de l'interface utilisateur. De toute façon, je n'y ai pas beaucoup réfléchi.
HGPB
5
@Haraldo: « Un défaut fragment vide pourrait être un espace réservé , par exemple , jusqu'à ce que fragmentent les a chargé » - Nous vous invitons à avoir un espace réservé dans votre fragment aujourd'hui, avec la mise en œuvre actuelle de ViewPager, celui que vous remplacez le contenu réel lorsque disponible .
CommonsWare
2
onPageChangeListener semble être la solution.
alicanbatur
123

Le meilleur moyen que j'ai trouvé était setUserVisibleHint,
ajoutez ceci à votre fragment

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}
Tyler Davis
la source
1
+++++ pour cette solution !!! merci, après avoir ajouté ce code, j'ai obtenu NPL pour la première fois, je viens d'ajouter try catch block et cela fonctionne parfaitement pour moi !!!
Bhavin Chauhan
1
En utilisant cela, il ne charge pas la date sur le second fragment ViewPager, sur le second fragment ses données de chargement du troisième fragment. Est-ce que quelqu'un peut m'aider?
Arshad
Suis-je manquant quelque chose, setUserVisibleHint appelé avant onCreateView
Anton Makov
La meilleure solution est désormais obsolète
M.Sameer
@ M.Sameer une alternative pour setUserVisibleHint est obsolète?
Yudi karma le
8

Vous pouvez essayer comme ceci:

public abstract class LazyFragment extends Fragment {
    protected boolean isVisible;
    /**
     * 在这里实现Fragment数据的缓加载.
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }
    protected void onVisible(){
        lazyLoad();
    }
    protected abstract void lazyLoad();
    protected void onInvisible(){}

protected abstract void lazyLoad();
protected void onInvisible(){}
Terry
la source
cette meilleure réponse!
Radesh
7

Premier ajout

   boolean isFragmentLoaded = false;

que

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser && !isFragmentLoaded) {
        //Load Your Data Here like.... new GetContacts().execute();

        isFragmentLoaded = true;
    }
else{
     }
}
Arpit J.
la source
6
mais cette méthode appelle avant onCreateView
AndroidGeek
2

ViewPager est par défaut pour charger la page suivante (Fragment) que vous ne pouvez pas modifier par setOffscreenPageLimit (0). Mais vous pouvez faire quelque chose pour pirater. Vous pouvez implémenter la fonction onPageSelected dans Activity contenant le ViewPager. Dans le fragment suivant (que vous ne voulez pas charger), vous écrivez une fonction, disons showViewContent () où vous mettez tout le code d'initialisation consommant des ressources et ne faites rien avant la méthode onResume (). Appelez ensuite la fonction showViewContent () dans onPageSelected. J'espère que cela aidera.

Friends_little_black
la source
1

cela peut être un vieux fil mais cela semble fonctionner pour moi. Remplacez cette fonction:

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

    if ( menuVisible ) {
        /**
         * Load your stuffs here.
         */
    } else  { 
        /**
         * Fragment not currently Visible.
         */
    } 
}

codages heureux ...

ralphgabb
la source
quoi sur webview? Je vous suggère de créer un nouveau fil pour cela.
ralphgabb
1

dans mon cas, je voulais démarrer des animations dans les vues, mais avec setUserVisibleHint j'ai eu quelques problèmes ...
ma solution est:

1 / addOnPageChangeListener pour votre adaptateur:

mViewPager.addOnPageChangeListener(this);

2 / implémenter OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / écraser les 3 méthodes:

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / déclarez et initialisez cette variable sur votre classe

private static int mTabState = 1;

Remarque : j'ai trois fragments dans mon adaptateur et j'utilise mTabState pour setCurrentItem et la position actuelle de l'adaptateur qui reconnaissent quel fragment est montré à l'utilisateur dans le temps ... 5 / dans la méthode onPageSelected, ajoutez ces codes:

if (mTabState == 0 || position == 0)
    {
        Intent intent = new Intent("animation");
        intent.putExtra("current_position", position);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
    }

si la page précédente ou la page actuelle est la page 0 (fragment en position 0), alors faites ceci

6 / maintenant dans votre classe de fragment (fragment en position 0 de l'adaptateur), vous devez créer un récepteur de diffusion et l'enregistrer dans la méthode onResume et le désinscrire onPause methos:

BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (Objects.equals(intent.getAction(), "animation"))
        {
            int currentPosition = intent.getIntExtra("current_position", 0);
            if (currentPosition == 0)
            {
                startAnimation();
                setViewsVisible();
            } else
            {
                setViewsInvisible();
            }
        }
    }
};

@Override
public void onResume()
{
    super.onResume();
    LocalBroadcastManager.getInstance(mContext).registerReceiver(broadcastReceiver, new IntentFilter("animation"));
}

@Override
public void onPause()
{
    super.onPause();
    LocalBroadcastManager.getInstance(mContext).unregisterReceiver(broadcastReceiver);
}

résumé: j'ai un adaptateur de pageur de fragments qui montre trois fragments, je veux montrer quelques animations sur les vues dans le fragment en position 0 de l'adaptateur, pour cela, j'utilise BroadcastReceiver. Lorsque le fragment est sélectionné, je lance la méthode d'animation et montre les vues à l'utilisateur, lorsque le fragment n'est pas affiché à l'utilisateur, j'essaye les vues invisibles ...

AREF
la source
1

Veuillez essayer ce code pour résoudre le problème d'actualisation de la vue dans Viewpager ...

/* DO NOT FORGET! The ViewPager requires at least “1” minimum OffscreenPageLimit */
int limit = (mAdapter.getCount() > 1 ? mAdapter.getCount() - 1 : 1);
mViewPager.setOffscreenPageLimit(limit);
Abhijeet Sharma
la source
0

pour la fonction "instantiateItem", préparez simplement le fragment, mais ne chargez pas le contenu lourd.

Utilisez "onPageChangeListener", de sorte qu'à chaque fois que vous accédez à une page spécifique, vous chargez son contenu lourd et l'affichez.

développeur android
la source
0

J'ai en quelque sorte le même problème. J'ai trouvé du code utile sur ce site et je l'ai transformé.

Le min entier pour mViewPager.setOffscreenPageLimit (...); vaut 1, donc même si vous le changez à 0, vous aurez toujours 2 pages chargées.

La première chose à faire est de créer un int statique, nous appellerons maxPageCount et remplacerons la méthode FragmentStatePagerAdapter getCount () pour renvoyer maxPageCount:

    @Override
    public int getCount() {
        return maxPageCount;
    }

Créez ensuite une méthode statique accessible de n'importe où dans le programme qui vous permettra de modifier ce maxCount:

public static void addPage(){
    maxPageCount++; //or maxPageCount = fragmentPosition+2
    mFragmentStatePagerAdapter.notifyDataSetChanged(); //notifyDataSetChanged is important here.
}

Maintenant, initialisez maxPageCount à 1. Quand vous le souhaitez, vous pouvez ajouter une autre page. Dans mon cas, lorsque j'ai eu besoin de l'utilisateur pour traiter la page actuelle avant de générer l'autre. Il le fait et ensuite, sans problème, peut passer à la page suivante.

J'espère que cela aidera quelqu'un.

K20
la source
0

Utilisez This // create boolean pour récupérer des données private boolean isViewShown = false;

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getView() != null) {
        isViewShown = true;
        // fetchdata() contains logic to show data when page is selected mostly asynctask to fill the data
        fetchData();
    } else {
        isViewShown = false;
    }
} 
Rohit Sharma
la source