42 votes

ViewPager à l'intérieur de ViewPager

Je voudrais créer un ViewPager (avec trois éléments) où chacune de ses vues est un autre ViewPager (avec deux éléments). L'utilisateur peut ensuite faire glisser les éléments de cette manière :

ViewPager1[0] ViewPager2[0]
ViewPager1[0] ViewPager2[1]
ViewPager1[1] ViewPager2[0]
ViewPager1[1] ViewPager2[1]
ViewPager1[2] ViewPager2[0]
ViewPager1[2] ViewPager2[1]

Comment cela serait-il possible ?

4 votes

Cela est peu probable de fonctionner, pour la même raison pour laquelle vous ne pouvez généralement pas avoir des éléments défilables à l'intérieur d'autres éléments défilables -- ils ont tendance à se disputer les événements tactiles.

2 votes

Oui... c'est pourquoi je demande :).

0 votes

Est-ce que ma réponse vous a aidé en quoi que ce soit ?

58voto

straya Points 1605

Remplacer canScroll dans le parent ViewPager :

@Override
protected boolean canScroll(Vue v, boolean verifierV, int dx, int x, int y) {
   if(v != this && v instanceof ViewPager) {
      return true;
   }
   return super.canScroll(v, verifierV, dx, x, y);
}

0 votes

+1, Merci d'avoir économisé mon temps. J'ai besoin de la même solution pour faire défiler la galerie dans ViewPager. Ça a marché.

0 votes

Merci pour cela. J'ai répondu à ma propre question en utilisant une partie de ceci où j'avais des ScrollViews dans les pages du ViewPager et à l'intérieur des ScrollViews il y a un ViewPager. Au cas où quelqu'un aurait besoin de le faire : stackoverflow.com/questions/17053270/…

10 votes

Cela fonctionne mais je voulais le faire de cette façon. Si le viewpager enfant est à la fin, faites défiler le parent.

28voto

Android Noob Points 801

Essaye ceci :

public class CustomViewPager extends ViewPager {
    private int childId;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
     @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (childId > 0) {          
            ViewPager pager = (ViewPager)findViewById(childId);

            if (pager != null) {           
                pager.requestDisallowInterceptTouchEvent(true);
            }

        }

        return super.onInterceptTouchEvent(event);
    }

    public void setChildId(int id) {
        this.childId = id;
    }
}

8 votes

La maîtrise de onInterceptTouchEvent est le secret de toutes les vues imbriquées scrollables.

1 votes

Ça fonctionne vraiment bien. Si vous construisez votre visionneuse à l'intérieur de votre Xml, assurez-vous de changer android.support.v4.view.ViewPager en -> mypackage.CustomViewPager. Le seul problème est que les premiers événements tactiles ne basculent pas, donc...

0 votes

N'est-ce pas un identifiant de pager parent (et non enfant) qui doit être passé? De plus, pour que cela fonctionne, j'ai dû obtenir le pager de cette manière: ViewPager pager = (ViewPager)((Activity)mContext).findViewById(mParentPagerId)‌​; Sinon, merci beaucoup pour cela, cela m'a fait gagner des heures!

12voto

gtRfnkN Points 115

J'ai cherché longtemps pour faire fonctionner un ViewPager à l'intérieur d'un autre ViewPager et j'ai trouvé la solution par "Android Noob" ici. Merci beaucoup pour cela!

Je voulais aussi partager ma solution. J'ai ajouté la possibilité de passer la gestion du balayage au ViewPager environnant une fois que le dernier élément (le plus à droite) dans le ViewPager interne est atteint. Pour éviter les erreurs graphiques, je sauvegarde également la première direction de balayage pour le dernier élément: c'est-à-dire que si vous faites d'abord un balayage vers la gauche, un balayage minimal vers la droite ne réinitialise pas l'état du défilement.

public class GalleryViewPager extends ViewPager {

    /** la dernière position x */
    private float   lastX;

    /** si le premier balayage était de gauche à droite (->), ne pas écouter les balayages de droite */
    private boolean slidingLeft;

    /** si le premier balayage était de droite à gauche (<-), ne pas écouter les balayages de gauche */
    private boolean slidingRight;

    public GalleryViewPager(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public GalleryViewPager(final Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(final MotionEvent ev) {
        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                // Interdire au parent ViewPager d'intercepter les événements tactiles.
                this.getParent().requestDisallowInterceptTouchEvent(true);

                // sauvegarder la position x actuelle
                this.lastX = ev.getX();

                break;

            case MotionEvent.ACTION_UP:
                // Autoriser au parent ViewPager d'intercepter les événements tactiles.
                this.getParent().requestDisallowInterceptTouchEvent(false);

                // sauvegarder la position x actuelle
                this.lastX = ev.getX();

                // réinitialiser les actions de balayage
                this.slidingLeft = false;
                this.slidingRight = false;

                break;

            case MotionEvent.ACTION_MOVE:
                /*
                 * si c'est le premier élément, un défilement de gauche à
                 * droite devrait naviguer dans le ViewPager environnant
                 */
                if (this.getCurrentItem() == 0) {
                    // un balayage de gauche à droite (->)?
                    if (this.lastX <= ev.getX() && !this.slidingRight) {
                        // activer l'interception tactile du parent -> le parent peut faire un balayage
                        this.getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        /*
                         * si le premier balayage était de droite à gauche, ne pas écouter les balayages
                         * de gauche à droite. cela corrige les erreurs graphiques où l'utilisateur fait d'abord un balayage
                         * à droite, puis à gauche et l'état du défilement est réinitialisé
                         */
                        this.slidingRight = true;

                        // sauvegarder la position x actuelle
                        this.lastX = ev.getX();
                        this.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                } else
                /*
                 * si c'est le dernier élément, un défilement de droite à
                 * gauche devrait naviguer dans le ViewPager environnant
                 */
                if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
                    // un balayage de droite à gauche (<-)?
                    if (this.lastX >= ev.getX() && !this.slidingLeft) {
                        // activer l'interception tactile du parent -> le parent peut faire un balayage
                        this.getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        /*
                         * si le premier balayage était de gauche à droite, ne pas écouter les balayages
                         * de droite à gauche. cela corrige les erreurs graphiques où l'utilisateur fait d'abord un balayage
                         * à gauche, puis à droite et l'état du défilement est réinitialisé
                         */
                        this.slidingLeft = true;

                        // sauvegarder la position x actuelle
                        this.lastX = ev.getX();
                        this.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }

                break;
        }

        super.onTouchEvent(ev);
        return true;
    }

}

J'espère que cela aidera quelqu'un à l'avenir!

12voto

user2749059 Points 31

Si le viewpager enfant est à la fin, faites défiler le parent

protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if(v != this && v instanceof ViewPager) {
        int currentItem = ((ViewPager) v).getCurrentItem();
        int countItem = ((ViewPager) v).getAdapter().getCount();
            if((currentItem==(countItem-1) && dx<0) || (currentItem==0 && dx>0)){
                return false;
            }
        return true;
    }
    return super.canScroll(v, checkV, dx, x, y);
}

9voto

Westy92 Points 842

Pour un ViewPager2, la solution actuelle est d'utiliser un NestedScrollableHost : https://github.com/android/views-widgets-samples/blob/master/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt

Vous pouvez consulter le rapport de bug et les avancées ici : https://issuetracker.google.com/issues/123006042

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X