web-dev-qa-db-fra.com

Android ViewPager setCurrentItem ne fonctionne pas après onResume

J’ai eu ce problème étrange, setCurrentItem (position, false) de ViewPager fonctionne parfaitement, puis je passe à une autre activité et, une fois revenu à la première activité, ViewPager se termine toujours sur le premier élément. Même si ive a ajouté setCurrentItem à la méthode onResume, il l’ignore toujours . Il ne génère même pas d’exception lorsqu’il essaie de définir l’élément sur out of bounds index. Bien que plus tard, lorsque j'appellerai cette méthode, lorsque le bouton "next" sera cliqué, cela fonctionnera comme prévu . Vérifié mon code 10 fois pour d'éventuels appels à setCurrentItem (0) ou smth mais ce n'est tout simplement pas là du tout.

22
Maciej Boguta

je ne peux pas vraiment répondre POURQUOI cela se produit exactement, mais si vous retardez l'appel de setCurrentItem pendant quelques millisecondes, il devrait fonctionner. Mon hypothèse est que, parce que pendant onResume, il n’ya pas encore eu d’exécution du rendu et que ViewPager en a besoin.

private ViewPager viewPager;

@Override
public void onResume() {
    final int pos = 3;
    viewPager.postDelayed(new Runnable() {

        @Override
        public void run() {
            viewPager.setCurrentItem(pos);
        }
    }, 100);
}

MISE À JOUR: heure du conte

aujourd'hui, j'ai eu le problème que le viewpager a ignoré mon action setCurrentItem et j'ai cherché une solution de stackoverflow. j'ai trouvé quelqu'un avec le même problème et un correctif; J'ai implémenté le correctif et cela n'a pas fonctionné. whoa! Retour à stackoverflow pour réduire ce faux-fix-fournisseur, et ...

c'était moi. J'ai mis en œuvre mon propre non-correctif défectueux, que je suis venu avec la première fois je suis tombé sur le problème (et qui a été oublié plus tard). Je vais maintenant devoir me changer de voix pour avoir fourni de mauvaises informations.


la raison pour laquelle mon "correctif" initial a fonctionné n'était pas à cause d'un "passe rendu"; le problème était que le contenu du téléavertisseur était contrôlé par une centrifugeuse. à la fois l'état des fileurs et celui des pagers ont été restaurés, et à cause de cela, l'écouteur onItemSelected des filateurs a été appelé lors du cycle de propagation d'événement suivant, ce qui a repeuplé le viewpager - cette fois en utilisant une valeur par défaut différente.
La suppression et la réinitialisation de l'écouteur lors de la restauration de l'état initial ont résolu le problème.

le correctif ci-dessus fonctionne un peu, car il définit la position actuelle du pageur après l'événement déclenché onItemSelected. plus tard, il a cessé de fonctionner pour une raison quelconque (probablement, l'application est devenue trop lente - dans mon implémentation, je n'ai pas utilisé 100 ms, mais 10 ms). J'ai ensuite supprimé le postDelayed dans un cycle de nettoyage, car il ne modifiait pas le comportement déjà défectueux.

mise à jour 2: je ne peux pas voter mon propre message. Je suppose, honorable seppuku est la seule option qui reste.

57
stefs

J'ai eu un problème similaire dans le OnCreate de mon activité . L'adaptateur a été configuré avec le nombre correct et j'applique setCurrentItem après avoir défini l'adaptateur sur ViewPager cependant renverrait l'index hors limites. Je pense que ViewPager n'a pas chargé tous mes fragments au moment où j'ai défini l'élément actuel. En postant un exécutable sur ViewPager, j'ai pu contourner ce problème. Voici un exemple avec un peu de contexte.

    // Locate the viewpager in activity_main.xml
    final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);

    // Set the ViewPagerAdapter into ViewPager
    viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));

    viewPager.setOffscreenPageLimit(2);

    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setCurrentItem(ViewPagerAdapter.CENTER_PAGE);
        }
    });
19
Martin Price

J'ai trouvé une solution très simple pour cela:

    if (mViewPager.getAdapter() != null)
        mViewPager.setAdapter(null);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setCurrentItem(desiredPos);

Et, si cela ne fonctionne pas, vous pouvez le mettre dans un gestionnaire, mais vous n'avez pas besoin d'un délai:

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mViewPager.setCurrentItem(desiredPos);
            }
        });
11
android developer

J'ai le même problème et je modifie 

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

Je mets NUM_PAGES être erreur à 1 seulement.

3
Abdulaziz Noor

J'avais un bogue similaire dans le code, le problème était que je définissais la position avant de modifier les données.

La solution consistait simplement à définir la position par la suite et à notifier les données modifiées

notifyDataSetChanged()
setCurrentItem()
2
petrumo

Pour moi, ce paramètre a fonctionné après la configuration de l'adaptateur 

viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));

viewPager.setCurrentItem(idx);

pagerSlidingTabStrip.setViewPager(viewPager);// assign viewpager to tabs
1
arun-r

J'ai utilisé la méthode post () décrite ici et bien sûr, il fonctionnait très bien dans certains scénarios, mais comme mes données proviennent du serveur, ce n'était pas le Saint Graal.

Mon problème était que je veux avoir

notifyDataSetChanged

appelé à un moment arbitraire, puis changez d'onglet sur mon viewPager. Donc, juste après l'appel de notification j'ai cette

ViewUtilities.waitForLayout(myViewPager, new Runnable() {
    @Override
    public void run() {
        myViewPager.setCurrentItem(tabIndex , false);
    }
});

et

public final class ViewUtilities {
    public static void waitForLayout(final View view, final Runnable runnable) {
        view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //noinspection deprecation
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);

                runnable.run();
            }
        });
    }
}

Anecdote: la fin de // noinspection à la fin est due à une erreur d'orthographe dans l'API qui a été corrigée après l'API 16, ce qui devrait indiquer remove On GlobalLayoutListener au lieu de removeGlobal OnLayoutListener

Cela semble couvrir tous les cas pour moi.

1
Thanasis Kapelonis

Ceci est une question de cycle de vie, comme le soulignent plusieurs affiches ici. Cependant, je trouve que les solutions avec la publication d'une Runnable sont imprévisibles et probablement sujettes aux erreurs. Cela semble être un moyen d’ignorer le problème en l’affichant dans le futur.

Je ne dis pas que c'est la meilleure solution, mais cela fonctionne sans utiliser Runnable. Je garde un entier séparé à l'intérieur de la Fragment qui a la ViewPager. Cet entier contiendra la page que nous voulons définir comme page actuelle lorsque onResume sera appelé ensuite. La valeur de l'entier peut être définie n'importe où et peut donc être définie avant une FragmentTransaction ou lors de la reprise d'une activité. Notez également que tous les membres sont configurés dans onResume() et non dans onCreateView().

public class MyFragment extends Fragment
{
    private ViewPager           mViewPager;
    private MyPagerAdapter      mAdapter;
    private TabLayout           mTabLayout;
    private int                 mCurrentItem = 0; // Used to keep the page we want to set in onResume().

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.my_layout, container, false);
        mViewPager = (ViewPager) view.findViewById(R.id.my_viewpager);
        mTabLayout = (TabLayout) view.findViewById(R.id.my_tablayout);
        return view;
    }

    @Override
    public void onResume()
    {
        super.onResume();

        MyActivity myActivity = (MyActivity) getActivity();
        myActivity.getSupportActionBar().setTitle(getString(R.string.my_title));

        mAdapter = new MyPagerAdapter(getChildFragmentManager(), myActivity);
        mViewPager.setAdapter(mAdapter);
        mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
        mViewPager.setCurrentItem(mCurrentItem); // <-- Note the use of mCurrentItem here!
        mTabLayout.setupWithViewPager(mViewPager);
    }

    /**
     * Call this at any point before needed, for example before performing a FragmentTransaction.
     */    
    public void setCurrentItem(int currentItem)
    {
        mCurrentItem = currentItem;

        // This should be called in cases where onResume() is not called later,
        // for example if you only want to change the page in the ViewPager
        // when clicking a Button or whatever. Just omit if not needed.
        mViewPager.setCurrentItem(mCurrentItem); 
    }


}
0
Krøllebølle

Au moment où j'appelle setCurrentItem(), la vue est sur le point d'être recréée. Donc, en fait, j'appelle setCurrentItem() pour le viewpager, puis le système appelle onCreateView() et crée ainsi un nouveau viewpager. 

C'est la raison pour laquelle je ne vois aucun changement. Et c’est la raison pour laquelle postDelayed() peut aider. 

Solution théorique: Reportez l'invocation setCurrentItem() jusqu'à ce que la vue soit recréée. 

Solution pratique: Je n'ai aucune idée d'une solution simple et stable. Nous devrions pouvoir vérifier si la classe est sur le point de recréer sa vue et si c'est le cas, reporter l'invocation de setCurrentItem() à la fin de onCreateView()

0
mikes

CLEAN AND SIMPLE Inutile d’ajouter une méthode post juste setCurrentItem après avoir appelé notifyDataSetChanged ().

0
Vikash Sharma

Je l'ai fait de cette façon pour restaurer l'élément actuel:

@Override
protected void onSaveInstanceState(Bundle outState) {

    if (mViewPager != null) {
        outState.putInt(STATE_PAGE_NO, mViewPager.getCurrentItem());
    }

    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {

    if (savedInstanceState != null) {
        mCurrentPage = savedInstanceState.getInt(STATE_PAGE_NO, 0);
    }

    super.onRestoreInstanceState(savedInstanceState);
}

@Override
protected void onRestart() {
    mViewPager.setCurrentItem(mCurrentPage);
         super.onRestart();
}
0
Szymon

un gars a écrit sur les forums ici. https://code.i-harness.com/fr/q/126bff9 a travaillé pour moi

 if (mViewPager.getAdapter() != null)
    mViewPager.setAdapter(null);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(desiredPos);
0
Andrei Chernyshev