web-dev-qa-db-fra.com

Comment désactiver la pagination en faisant glisser votre doigt dans ViewPager tout en pouvant effectuer un balayage par programme?

J'ai ViewPager et en dessous, j'ai 10 boutons. En cliquant sur le bouton, par exemple # 4, le téléavertisseur passe immédiatement à la page # 4 de mPager.setCurrentItem(3);. Mais, je veux désactiver la pagination en faisant glisser le doigt horizontalement. Ainsi, la recherche est effectuéeUNIQUEMENTen cliquant sur les boutons. Alors, comment puis-je désactiver le balayage avec le doigt?

461
theateist

Vous devez sous-classer ViewPager. onTouchEvent contient beaucoup de bonnes choses que vous ne voulez pas changer, comme permettre aux vues enfants d'obtenir des contacts. onInterceptTouchEvent est ce que vous voulez changer. Si vous regardez le code pour ViewPager, vous verrez le commentaire:

    /*
     * This method JUST determines whether we want to intercept the motion.
     * If we return true, onMotionEvent will be called and we do the actual
     * scrolling there.
     */

Voici une solution complète:

Tout d’abord, ajoutez cette classe à votre dossier src:

import Android.content.Context;
import Android.support.v4.view.ViewPager;
import Android.util.AttributeSet;
import Android.view.MotionEvent;
import Android.view.animation.DecelerateInterpolator;
import Android.widget.Scroller;
import Java.lang.reflect.Field;

public class NonSwipeableViewPager extends ViewPager {

    public NonSwipeableViewPager(Context context) {
        super(context);
        setMyScroller();
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        // Never allow swiping to switch between pages
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Never allow swiping to switch between pages
        return false;
    }

    //down one is added for smooth scrolling

    private void setMyScroller() {
        try {
            Class<?> viewpager = ViewPager.class;
            Field scroller = viewpager.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            scroller.set(this, new MyScroller(getContext()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public class MyScroller extends Scroller {
        public MyScroller(Context context) {
            super(context, new DecelerateInterpolator());
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            super.startScroll(startX, startY, dx, dy, 350 /*1 secs*/);
        }
    }
}

Ensuite, veillez à utiliser cette classe à la place de la valeur normale ViewPager, que vous avez probablement spécifiée sous la forme Android.support.v4.view.ViewPager. Dans votre fichier de mise en page, vous voudrez le spécifier avec quelque chose comme:

<com.yourcompany.NonSwipeableViewPager
    Android:id="@+id/view_pager"
    Android:layout_width="match_parent"
    Android:layout_height="0dp"
    Android:layout_weight="1" />

Cet exemple particulier est dans un LinearLayout et est censé occuper tout l'écran, raison pour laquelle layout_weight est 1 et layout_height est 0dp.

AndsetMyScroller();met en oeuvre une transition en douceur

813
louielouie

L’extension plus générale de ViewPager consisterait à créer une méthode SetPagingEnabled afin que nous puissions activer et désactiver la pagination à la volée . Pour activer/désactiver le balayage, il suffit de remplacer deux méthodes: onTouchEvent et onInterceptTouchEvent. Les deux renverront "faux" si la pagination a été désactivée.

public class CustomViewPager extends ViewPager {

    private boolean enabled;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onTouchEvent(event);
        }

        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onInterceptTouchEvent(event);
        }

        return false;
    }

    public void setPagingEnabled(boolean enabled) {
        this.enabled = enabled;
    } 
}

Ensuite, sélectionnez ceci au lieu du viewpager intégré en XML

<mypackage.CustomViewPager 
    Android:id="@+id/myViewPager" 
    Android:layout_height="match_parent" 
    Android:layout_width="match_parent" />

Il vous suffit d'appeler la méthode setPagingEnabled avec false et les utilisateurs ne pourront pas balayer pour paginer.

432
Rajul

Le moyen le plus simple est de setOnTouchListener et de retourner true pour ViewPager.

mPager.setOnTouchListener(new OnTouchListener()
    {           
        @Override
        public boolean onTouch(View v, MotionEvent event)
        {
            return true;
        }
    });
96
Wayne

Mieux vaut le déclarer stylable pour pouvoir changer sa propriété de xml

private boolean swipeable;

public MyViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyViewPager);
    try {
        swipeable = a.getBoolean(R.styleable.MyViewPager_swipeable, true);
    } finally {
        a.recycle();
    }
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    return swipeable ? super.onInterceptTouchEvent(event) : false;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    return swipeable ? super.onTouchEvent(event) : false;
}

Et dans vos valeurs/attr.xml:

<declare-styleable name="MyViewPager">
  <attr name="swipeable" format="boolean" />
</declare-styleable>

afin que vous puissiez l'utiliser dans votre mise en page XML:

<mypackage.MyViewPager
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/viewPager"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    app:swipeable="false" />

Bien sûr, vous pouvez toujours avoir une propriété get/set.

53
Pietro

Remplacer uniquement onTouchEvent et onInterceptTouchEvent n'est pas suffisant si ViewPager est lui-même dans un autre ViewPager. ViewPager enfant volerait des événements tactiles de défilement horizontal à partir de ViewPager parent à moins qu’il ne soit positionné sur sa première/dernière page.

Pour que cette installation fonctionne correctement, vous devez également remplacer la méthode canScrollHorizontally.

Voir LockableViewPager implémentation ci-dessous.

public class LockableViewPager extends ViewPager {

    private boolean swipeLocked;

    public LockableViewPager(Context context) {
        super(context);
    }

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

    public boolean getSwipeLocked() {
        return swipeLocked;
    }

    public void setSwipeLocked(boolean swipeLocked) {
        this.swipeLocked = swipeLocked;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return !swipeLocked && super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return !swipeLocked && super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean canScrollHorizontally(int direction) {
        return !swipeLocked && super.canScrollHorizontally(direction);
    }

}
45
sidon

Pour désactiver le balayage 

mViewPager.beginFakeDrag();

Pour activer le balayage 

mViewPager.endFakeDrag();
28
KAMAL VERMA

Si vous étendez à partir de ViewPager, vous devez également remplacer executeKeyEvent comme indiqué précédemment par @araks 

@Override
public boolean executeKeyEvent(KeyEvent event)
{
    return isPagingEnabled ? super.executeKeyEvent(event) : false;
}

Certains appareils, comme le Galaxy Tab 4 10 ', affichent ces boutons là où la plupart des appareils ne les affichent jamais:

 Keyboard buttons

25
Grease

Je devais désactiver le balayage sur une page spécifique et lui donner une animation en caoutchouc, voici comment:

    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageScrolled(int position, float positionOffset,
                                   int positionOffsetPixels) {
            if (position == MANDATORY_PAGE_LOCATION && positionOffset > 0.5) {
                mViewPager.setCurrentItem(MANDATORY_PAGE_LOCATION, true);
            }
        }
14
Oded Breiner

Je sais qu'il est très tard pour poster ceci, mais voici un petit bidouillage pour obtenir votre résultat;)

Ajoutez simplement une vue fictive ci-dessous votre viewpager:

<Android.support.v4.view.ViewPager
     Android:layout_width="match_parent"
     Android:layout_height="match_parent">
</Android.support.v4.view.ViewPager>

<RelativeLayout
     Android:id="@+id/dummyView"
     Android:layout_width="match_parent"
     Android:layout_height="match_parent">
</RelativeLayout>

Ajoutez ensuite une OnClickListener à votre RelativeLayout.

dummyView = (RelativeLayout) findViewById(R.id.dummyView);

dummyView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
         //Just leave this empty
    }
});

Soyez créatif avec les mises en page, leurs emplacements et leur comportement et vous apprendrez à trouver le moyen de faire absolument tout ce que vous imaginez :)

Bonne chance!

11
Dinuka

J'ai écrit une CustomViewPager avec un contrôle de balayage:

public class ScrollableViewPager extends ViewPager {
    private boolean canScroll = true;
    public ScrollableViewPager(Context context) {
        super(context);
    }
    public ScrollableViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setCanScroll(boolean canScroll) {
        this.canScroll = canScroll;
    }
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return canScroll && super.onTouchEvent(ev);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return canScroll && super.onInterceptTouchEvent(ev);
    }
}

Si vous définissez canScroll sur true, cette ViewPager peut glisser avec le doigt, false au contraire.

J'utilise cela dans mon projet et cela fonctionne très bien jusqu'à maintenant.

10
L. Swifter

Essayez de remplacer et de renvoyer true à partir de onInterceptTouchEvent () et/ou onTouchEvent () , qui consommera des événements tactiles sur le pageur.

4
AdamK

Dans kotlin, nous pouvons résoudre ce problème en créant un widget personnalisé héritant de la classe ViewPager et en ajoutant une valeur d'indicateur pour contrôler le comportement du balayage.

NoSwipePager.kt

class NoSwipePager(context: Context, attrs: AttributeSet) : ViewPager(context, attrs) {

    var pagingEnabled: Boolean = false

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (this.pagingEnabled) {
            super.onTouchEvent(event)
        } else false
    }

    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        return if (this.pagingEnabled) {
            super.onInterceptTouchEvent(event)
        } else false
    }
}

en xml

<your.package.NoSwipePager
    Android:id="@+id/view_pager"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:layout_below="@id/tab_layout" />

Désactiver le balayage par programmation. Si le plugin kotlin-Android-extensions est appliqué dans build.gradle, le balayage peut être désactivé en écrivant le code ci-dessous.

view_pager.pagingEnabled = false

Sinon, nous pouvons désactiver le balayage en utilisant le code ci-dessous:

val viewPager : ViewPager = findViewById(R.id.view_pager)
viewPager.pagingEnabled = false
3
Sagar Chapagain

Maintenant, nous n’avons plus besoin de créer une ViewPager personnalisée

Une nouvelle vue ViewPager2 name disponible sous Android

Pour désactiver le balayage dans viewpager2, utilisez

viewPager2.setUserInputEnabled(false);

Pour activer le balayage dans viewpager2, utilisez

viewPager2.setUserInputEnabled(true);

pour plus d'informations vérifier ceci

3
Nilesh Rathod

J'ai utilisé cette classe avec succès. Le remplacement de executeKeyEvent est requis pour éviter de glisser à l'aide de flèches sur certains périphériques ou pour des raisons d'accessibilité:

import Android.content.Context;
import Android.support.v4.view.ViewPager;
import Android.util.AttributeSet;
import Android.view.KeyEvent;
import Android.view.MotionEvent;

public class ViewPagerNoSwipe extends ViewPager {
    /**
     * Is swipe enabled
     */
    private boolean enabled;

    public ViewPagerNoSwipe(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = false; // By default swiping is disabled
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return this.enabled ? super.onTouchEvent(event) : false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return this.enabled ? super.onInterceptTouchEvent(event) : false;
    }

    @Override
    public boolean executeKeyEvent(KeyEvent event) {
        return this.enabled ? super.executeKeyEvent(event) : false;
    }

    public void setSwipeEnabled(boolean enabled) {
        this.enabled = enabled;
    }

}

Et dans le xml appelez-le comme ceci:

<package.path.ViewPagerNoSwipe
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />
2
madx

Si vous écrivez une galerie d'images avec ImageViews et ViewPager, qui prend en charge le zoom et le panoramique, voir une solution simple décrite ici: Implémentation d'un ImageView zoomable en étendant le ViewPager par défaut dans Phimpme Android (et exemple Github - PhotoView ). Cette solution ne fonctionne pas avec ViewPager seule.

public class CustomViewPager extends ViewPager {
    public CustomViewPager(Context context) {
        super(context);
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        try {
            return super.onInterceptTouchEvent(event);
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
}
1
CoolMind

Si vous souhaitez implémenter la même chose pour Android dans Xamarin, voici une traduction en C #

J'ai choisi de nommer l'attribut " ScrollEnabled ". Parce que iOS utilise simplement le même nom excat. Ainsi, les noms sont identiques sur les deux plates-formes, ce qui facilite la tâche des développeurs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Support.V4.View;
using Android.Util;

namespace YourNameSpace.ViewPackage {

    // Need to disable swiping for ViewPager, if user performs Pre DSA and the dsa is not completed yet
    // http://stackoverflow.com/questions/9650265/how-do-disable-paging-by-swiping-with-finger-in-viewpager-but-still-be-able-to-s
    public class CustomViewPager: ViewPager {
        public bool ScrollEnabled;

        public CustomViewPager(Context context, IAttributeSet attrs) : base(context, attrs) {
            this.ScrollEnabled = true;
        }

        public override bool OnTouchEvent(MotionEvent e) {
            if (this.ScrollEnabled) {
                return base.OnTouchEvent(e);
            }
            return false;
        }

        public override bool OnInterceptTouchEvent(MotionEvent e) {
            if (this.ScrollEnabled) {
                return base.OnInterceptTouchEvent(e);
            }
            return false;
        }

        // For ViewPager inside another ViewPager
        public override bool CanScrollHorizontally(int direction) {
            return this.ScrollEnabled && base.CanScrollHorizontally(direction);
        }

        // Some devices like the Galaxy Tab 4 10' show swipe buttons where most devices never show them
        // So, you could still swipe through the ViewPager with your keyboard keys
        public override bool ExecuteKeyEvent(KeyEvent evt) {
            return this.ScrollEnabled ? base.ExecuteKeyEvent(evt) : false;
        }
    }
}

En fichier .axml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">
  <YourNameSpace.ViewPackage.CustomViewPager
      Android:id="@+id/pager"
      Android:layout_width="match_parent"
      Android:layout_height="wrap_content"
      Android:background="@Android:color/white"
      Android:layout_alignParentTop="true" />
</LinearLayout>
1
Lepidopteron

Une autre solution facile pour désactiver le balayage sur une page spécifique (dans cet exemple, page 2):

int PAGE = 2;
viewPager.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
        if (viewPager.getCurrentItem() == PAGE) {
                viewPager.setCurrentItem(PAGE-1, false);
                viewPager.setCurrentItem(PAGE, false);
                return  true;
        }
        return false;
}
1
Ofir

Je viens de personnaliser un peu le viewpager et de désactiver facilement le balayage.

public class CustomViewPager extends ViewPager {

private boolean swipeLocked;

public CustomViewPager(Context context) {
    super(context);
}

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

public boolean getSwipeLocked() {
    return swipeLocked;
}

public void setSwipeLocked(boolean swipeLocked) {
    this.swipeLocked = swipeLocked;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    return !swipeLocked && super.onTouchEvent(event);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    return !swipeLocked && super.onInterceptTouchEvent(event);
}

@Override
public boolean canScrollHorizontally(int direction) {
    return !swipeLocked && super.canScrollHorizontally(direction);
}
}
0
Shivam Yadav

voici ma mise en oeuvre
que si vous voulez désactiver l’animation par balayage, vous pouvez utiliser le glissementListener à gauche et à droite et toujours vouloir le défilement par doigt mais sans animation 

1 méthode de substitution Viewpager méthode onInterceptTouchEvent et onTouchEvent 

2- créer votre propre GestureDetector

3- détectez le geste de balayage et utilisez le setCurrentItem(item, false)

ViewPager

public class ViewPagerNoSwipe extends ViewPager {
    private final GestureDetector gestureDetector;
    private OnSwipeListener mOnSwipeListener;

    public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
        mOnSwipeListener = onSwipeListener;
    }

    public ViewPagerNoSwipe(@NonNull Context context) {
        super(context);
        gestureDetector = new GestureDetector(context, new GestureListener());

    }

    public ViewPagerNoSwipe(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        gestureDetector = new GestureDetector(context, new GestureListener());


    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        gestureDetector.onTouchEvent(ev);
        return false;
    }

    public class GestureListener extends GestureDetector.SimpleOnGestureListener {

        private static final int SWIPE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            if(mOnSwipeListener!=null)
                            mOnSwipeListener.onSwipeRight();
                        } else {
                            if(mOnSwipeListener!=null)
                                mOnSwipeListener.onSwipeLeft();
                        }
                        result = true;
                    }
                } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        if(mOnSwipeListener!=null)
                            mOnSwipeListener.onSwipeBottom();
                    } else {
                        if(mOnSwipeListener!=null)
                            mOnSwipeListener.onSwipeTop();
                    }
                    result = true;
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return result;
        }
    }

    public interface OnSwipeListener {

         void onSwipeRight();

        void onSwipeLeft();

        void onSwipeTop();

        void onSwipeBottom();
    }
}

lorsque vous configurez ViewPager, définissez swipeListener 

postsPager.setOnSwipeListener(new ViewPagerNoSwipe.OnSwipeListener() {
            @Override
            public void onSwipeRight() {

              postsPager.setCurrentItem(postsPager.getCurrentItem() + 1,false);

            }

            @Override
            public void onSwipeLeft() {

            postsPager.setCurrentItem(postsPager.getCurrentItem() - 1, false);

            }
             ...
           }
0
tamtom

Dans Kotlin, ma solution, combinant les réponses ci-dessus.

class CustomViewPager(context: Context, attrs: AttributeSet): ViewPager(context, attrs) {
    var swipeEnabled = false

    override fun onTouchEvent(ev: MotionEvent?): Boolean {
        return if (swipeEnabled) super.onTouchEvent(ev) else false
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        return if (swipeEnabled) super.onInterceptTouchEvent(ev) else false
    }

    override fun executeKeyEvent(event: KeyEvent): Boolean {
        return if (swipeEnabled) super.executeKeyEvent(event) else false
    }
}
0
Jeff Padgett
This worked for me

viewPager.setOnTouchListener(new View.OnTouchListener() {
                                         @Override
                                         public boolean onTouch(View v, MotionEvent event) {
                                             if (viewPager.getCurrentItem() == 0) {
                                                 viewPager.setCurrentItem(-1, false);
                                                 return true;
                                             }
                                             else if (viewPager.getCurrentItem() == 1) {
                                                 viewPager.setCurrentItem(1, false);
                                                 return true;
                                             }
                                             else if (viewPager.getCurrentItem() == 2) {
                                                 viewPager.setCurrentItem(2, false);
                                                 return true;
                                             }
                                             return true;
                                         }
                                     });
0
Princewill Iroka

Aucun des codes ci-dessus ne fonctionne correctement. J'ai essayé ceci

<HorizontalScrollView
                    Android:id="@+id/horizontalScrollView"
                    Android:layout_width="match_parent"
                    Android:layout_height="match_parent"
                    Android:fillViewport="true">

                    <Android.support.v4.view.ViewPager
                        Android:id="@+id/pager"
                        Android:layout_width="match_parent"
                        Android:layout_height="match_parent" />
                </HorizontalScrollView>
0
Nandan Singh