web-dev-qa-db-fra.com

android: ViewPager et HorizontalScrollVIew

J'ai un HorizontalScrollView à l'intérieur de mon ViewPager. J'ai défini requestDisallowInterceptTouchEvent(true); pour le HorizontalScrollView mais le ViewPager intercepte toujours parfois les événements tactiles. Existe-t-il une autre commande que je peux utiliser pour empêcher le parent et les ancêtres d'une vue d'intercepter des événements tactiles?

remarque: le HorizontalScrollView n'occupe que la moitié de l'écran.

30
Android Noob

J'ai eu le même problème. Ma solution était:

  1. Créez une sous-classe de ViewPager et ajoutez une propriété appelée childId.
  2. Créez un setter pour la propriété childId et définissez l'ID de HorizontalScrollView.
  3. Remplacez onInterceptTouchEvent() dans la sous-classe de ViewPager et si la propriété childId est supérieure à 0, obtenez cet enfant et si l'événement se trouve dans la zone HorizontalScrollView, retournez false .

Code

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) {
            View scroll = findViewById(childId);
            if (scroll != null) {
                Rect rect = new Rect();
                scroll.getHitRect(rect);
                if (rect.contains((int) event.getX(), (int) event.getY())) {
                    return false;
                }
            }
        }
        return super.onInterceptTouchEvent(event);
    }

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

Dans la méthode onCreate()

viewPager.setChildId(R.id.horizontalScrollViewId);
adapter = new ViewPagerAdapter(this);
viewPager.setAdapter(adapter);

J'espère que cette aide

35
Fede

Merci pour la réponse. J'ai un peu modifié votre solution et réussi à faire fonctionner les ViewPagers imbriqués:

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
Android Noob

Donc, je sais que cette question est assez ancienne, mais je suis venu avec une solution qui, je pense, présente des avantages supplémentaires par rapport à celle publiée ici. Une situation qui pourrait se produire est que je pourrais avoir plus d'un HorizontalScrollView dans mon ViewPager (ce que je fais), puis je devrais fournir des tonnes d'identifiants au ViewPager afin qu'il puisse toujours vérifier les conflits de contact avec l'enfant.

J'ai analysé la source du ViewPager et constaté qu'ils utilisent cette méthode appelée "canScroll" pour déterminer si une vue enfant peut être défilée. Heureusement, il n'a pas été rendu statique ni définitif, donc je pouvais simplement le remplacer. A l'intérieur de cette méthode, le "booléen ViewCompat.canScrollHorizontally (View, int)" a été appelé (celui qui renvoie toujours false pour SDK <14). Ma solution était alors:

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public CustomViewPager(Context context) {
        super(context);
    }
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        return super.canScroll(v, checkV, dx, x, y) || (checkV && customCanScroll(v));
    }
    protected boolean customCanScroll(View v) {
        if (v instanceof HorizontalScrollView) {
            View hsvChild = ((HorizontalScrollView) v).getChildAt(0);
            if (hsvChild.getWidth() > v.getWidth())
                return true;
        }
        return false;
   }
}

Donc, si vous avez une classe différente de View dans votre ViewPager qui devrait également recevoir des traînées horizontales, changez simplement le customCanScroll (View) afin que vous puissiez revenir si le toucher ne doit pas être intercepté ou non.

La vérification booléenne est vraie si la vue actuelle doit également être vérifiée pour la "défilement", c'est pourquoi nous devrions également vérifier cela.

J'espère que cela aidera de futurs problèmes comme celui-ci (ou s'il existe une meilleure solution, ou une solution officielle de la Android, faites-le moi savoir).

7
Victor Elias

Je ne sais pas si vous avez déjà résolu le problème ou non. Mais aucune des réponses ci-dessus ne fonctionne correctement. Je pense donc que c'est nécessaire pour ces gens qui pourraient chercher des solutions avec souffrance.

En fait, la solution est assez simple si vous lisez le document officiel sur la gestion des événements tactiles dans ViewGroup.

Vous devez d'abord comprendre l'utilisation de

ViewParent.requestDisallowInterceptTouchEvent (boolean) , appelez ceci sur une vue parent pour indiquer qu'il ne doit pas intercepter les événements tactiles avec onInterceptTouchEvent (MotionEvent) .

Ensuite, vous devez intercepter les événements et vous assurer que le ViewGroup parent de votre HorizontalScrollView ne distribuera plus les événements.

Alors, définissez un OnToushListener pour vous HorizontalScrollView. Détecter les événements de toucher et de déplacement, puis définissez requestDisallowInterceptTouchEvent (true) sur son parent (ViewPager ou sth else ) pour vous assurer que les événements seront traités par HorizontalScrollView.

Bien sûr, vous pouvez mettre ce code ci-dessous dans vos composants CustomHorizontalScrollView afin qu'il puisse être réutilisé.

En espérant que ça aide. ;-)

horizontalScrollView.setOnTouchListener(new View.OnTouchListener() {
float rawX;
int mTouchSlop =  ViewConfiguration.get(getActivity()).getScaledTouchSlop();

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            v.getParent().requestDisallowInterceptTouchEvent(true);
            rawX = event.getRawX();
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            v.getParent().requestDisallowInterceptTouchEvent(false);
            rawX = 0f;
            break;
        case MotionEvent.ACTION_MOVE:
            if (Math.abs(rawX - event.getRawX()) > mTouchSlop)
                v.getParent().requestDisallowInterceptTouchEvent(true);
            break;
    }
    return false;
}
6
Ryan Hoo

Voici une autre solution qui a fonctionné pour moi,

Créer un ViewPager personnalisé

public class NoScrollViewPager extends ViewPager {

    private boolean scrollDisable = false;


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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(scrollDisable) {
            return false;
        } else {
            return super.onInterceptTouchEvent(ev);
        }
    }

    public void disableScroll() {
        scrollDisable = true;
    }

    public void enableScroll() {
        scrollDisable = false;
    }



}

Ensuite, ajoutez OnTouchListener à votre élément, que ce soit une vue horizontale ou une galerie ou toute autre chose que vous ne souhaitez pas que le viseur fasse défiler lorsqu'il est touché.

gf.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View arg0,
            MotionEvent ev) {
        switch(ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            viewPager.disableScroll();
            break;
        case MotionEvent.ACTION_UP:
            viewPager.enableScroll();
            break;
        }
        return false;
    }

});
5
Ali AlNoaimi

La solution de Fede n'a pas très bien fonctionné pour moi. Mon HorizontalScrollVIew avait des zones où il ne capturait aucune entrée (cliquez, glissez ..). Je suppose que getHitRect () doit faire quelque chose avec ça. Après l'avoir remplacé par le code suivant, tout a bien fonctionné.

Il est possible d'ajouter plusieurs vues HorizontalScrollVIew ou toute autre vue nécessitant une mise au point également.

public class CustomViewPager extends ViewPager {

    private ArrayList<Integer> children; 

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        children = new ArrayList<Integer>();
    }   

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (children.size() > 0) {
            for(int i=0; i<children.size(); i++){
                View scroll = findViewById(children.get(i));
                if (scroll != null & scroll.isPressed()) {
                    return false;
                }
            }
        }
        return super.onInterceptTouchEvent(event);
    }

    public void addChildId(int id) {
       children.add(id);
    }

}

En XML, vous aurez quelque chose comme ceci:

<form.paginator.lib.CustomViewPager
      Android:id="@+id/pager"
      Android:layout_width="fill_parent"
      Android:layout_height="150dp" />

Dans la méthode onCreate ():

mPager = (CustomViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.addChildId(R.id.avatarImageView);

Dans la méthode OnTouchListener () pour la vue que vous avez ajoutée aux enfants CustomViewPager:

case MotionEvent.ACTION_DOWN:
     v.setPressed(true);
     .
     .
     .
case MotionEvent.ACTION_UP:
     v.setPressed(false);
2
milosjovac

Cela fonctionnera également pour le visualiseur dans une vue de défilement: https://stackoverflow.com/a/2655740/969325

1
Warpzit

J'ai découvert que si vous avez un ViewPager dans un ScrollView et que vous souhaitez qu'il reste toujours au point, lorsque vous le touchez et que le ScrollView ne reçoit jamais de focus, cela fait cela.

    mViewPager= (ViewPager) findViewById(R.id.pager);
    mViewPager.setAdapter(adapter);
    mViewPager.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
            return false;
        }
    });
1
Marius Hilarious

J'avais des problèmes avec une petite vue de la galerie au bas de ma première page dans un groupe de viseurs. En faisant glisser la vue de la galerie, le focus basculerait entre le déplacement des éléments de la galerie ou le viseur.

Le remplacement de ViewPager comme indiqué ci-dessus et la définition de requestDisallowInterceptTouchEvent ont résolu mon problème. Les pages ne changent plus lorsque vous faites glisser la vue de la galerie. Pensez que c'est une solution importante pour ceux qui utilisent des vues de galerie avec Viewpager.

1
App8ite

J'ai donc modifié la réponse de Fede car elle désactivait le toucher dans la même zone dans les pages autour de celle que je voulais. De plus, je l'ai rendu capable d'ajouter plusieurs vues parce que j'ai affaire à beaucoup de graphiques et de vues interactives qui utilisent l'interactivité glisser/déposer horizontale. De cette façon, vous pouvez ajouter

public class CustomViewPager extends ViewPager {

private List<ViewFocus> lateralTouchViews = new ArrayList<>();
public CustomViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    for(int i = 0; i< lateralTouchViews.size(); i++){
        View scroll = findViewById(lateralTouchViews.get(i).id);
        if (scroll != null && getCurrentItem() == lateralTouchViews.get(i).itemPosition) {
            Rect rect = new Rect();
            scroll.getHitRect(rect);
            if (rect.contains((int) event.getX(), (int) event.getY())) {
                return false;
            }
        }
    }
    return super.onInterceptTouchEvent(event);
}

public void addConflict(int id, int pageIndex) {
    lateralTouchViews.add(new ViewFocus(id, pageIndex));
}
public class ViewFocus{
    int id;
    int itemPosition;

    public ViewFocus(int id, int itemPosition){
        this.id = id;
        this.itemPosition = itemPosition;
    }
}

}

0
Vitor Hugo Schwaab