Как отключить пейджинг, проводя пальцем в ViewPager, но все же иметь возможность программно проводить?
У меня есть ViewPager и под ним у меня есть 10 кнопок. При нажатии на кнопку, например № 4, пейджер сразу переходит на страницу № 4 mPager.setCurrentItem(3);
, Но я хочу отключить пейджинг, проводя пальцем по горизонтали. Таким образом, пейджинг осуществляется ТОЛЬКО нажатием на кнопки. Итак, как я могу отключить смахивание пальцем?
27 ответов
Вам нужно создать подкласс ViewPager. В onTouchEvent есть много хороших вещей, которые вы не хотите менять, например, позволяете дочерним видам касаться друг друга. onInterceptTouchEvent - это то, что вы хотите изменить. Если вы посмотрите на код для ViewPager, вы увидите комментарий:
/*
* 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.
*/
Вот полное решение:
Сначала добавьте этот класс в вашу папку 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*/);
}
}
}
Затем убедитесь, что вы используете этот класс вместо обычного ViewPager, который вы, вероятно, указали как android.support.v4.view.ViewPager. В вашем файле макета вы захотите указать что-то вроде:
<com.yourcompany.NonSwipeableViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
Этот конкретный пример находится в LinearLayout и предназначен для охвата всего экрана, поэтому layout_weight равен 1, а layout_height равен 0dp.
И setMyScroller(); метод для плавного перехода
Более общее расширение ViewPager
будет создавать SetPagingEnabled
метод, чтобы мы могли включать и отключать пейджинг на лету. Чтобы включить / отключить смахивание, просто переопределите два метода: onTouchEvent
а также onInterceptTouchEvent
, Оба вернут "ложь", если подкачка была отключена.
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;
}
}
Затем выберите это вместо встроенного виджета в XML
<mypackage.CustomViewPager
android:id="@+id/myViewPager"
android:layout_height="match_parent"
android:layout_width="match_parent" />
Вам просто нужно позвонить setPagingEnabled
метод с false
и пользователи не смогут сильно ударить по страницам.
Теперь нам не нужно создавать кастомы ViewPager
Новый ViewPager2
Название просмотра доступно в A ndroid
Чтобы отключить считывание в viewpager2
использование
viewPager2.setUserInputEnabled(false);
Чтобы включить пролистывание в viewpager2
использование
viewPager2.setUserInputEnabled(true);
для получения дополнительной информации проверьте это
Самый простой способ это setOnTouchListener
и вернуться true
за ViewPager
,
mPager.setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
return true;
}
});
Только переопределение onTouchEvent
а также onInterceptTouchEvent
недостаточно, если у вас есть сам ViewPager внутри другого ViewPager. Дочерний ViewPager украл бы события касания горизонтальной прокрутки из родительского ViewPager, если он не расположен на своей первой / последней странице.
Чтобы эта настройка работала правильно, вам необходимо переопределить также canScrollHorizontally
метод.
Увидеть LockableViewPager
реализация ниже.
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);
}
}
Лучше объявить его стилизованным, так что вы можете изменить его свойство с 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;
}
И в ваших ценностях /attr.xml:
<declare-styleable name="MyViewPager">
<attr name="swipeable" format="boolean" />
</declare-styleable>
так что вы можете использовать его в макете 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" />
Конечно, у вас все еще может быть свойство get/set.
Если вы выходите из ViewPager, вы также должны переопределитьexecuteKeyEvent
как указано ранее @araks
@Override
public boolean executeKeyEvent(KeyEvent event)
{
return isPagingEnabled ? super.executeKeyEvent(event) : false;
}
Потому что некоторые устройства, такие как Galaxy Tab 4 10', показывают эти кнопки там, где их не показывает большинство устройств:
Чтобы отключить пролистывание
mViewPager.beginFakeDrag();
Чтобы включить пролистывание
mViewPager.endFakeDrag();
Мне нужно было отключить перелистывание на одной конкретной странице и дать ему хорошую анимацию с резинкой, вот как:
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);
}
}
Я написал CustomViewPager
со смахивающим управлением:
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);
}
}
Если вы установите canScroll
в true
, этот ViewPager
можно сильно ударить пальцем, false
напротив.
Я использую это в своем проекте, и он прекрасно работает до сих пор.
Если вы используете ViewPager2, вам просто нужно использовать:
viewpager.setUserInputEnabled(false);
Из документов:
Включение или отключение прокрутки, инициируемой пользователем. Это включает сенсорный ввод (жесты прокрутки и перелистывания) и ввод специальных возможностей. Отключение ввода с клавиатуры пока не поддерживается. Когда прокрутка, инициированная пользователем, отключена, программная прокрутка через setCurrentItem по-прежнему работает. По умолчанию прокрутка, инициированная пользователем, включена.
Я знаю, что это очень поздно, чтобы опубликовать это, но вот крошечный взломать для достижения вашего результата;)
Просто добавьте фиктивный вид под вашим пейджером:
<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>
Затем добавьте OnClickListener
на ваш RelativeLayout
,
dummyView = (RelativeLayout) findViewById(R.id.dummyView);
dummyView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Just leave this empty
}
});
Поэкспериментируйте с макетами, их размещением и поведением, и вы научитесь находить способ делать абсолютно все, что вы себе представляете:)
Удачи!
Я использовал этот класс с успехом. Переопределение executeKeyEvent требуется, чтобы избежать считывания с использованием стрелок на некоторых устройствах или для доступности:
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;
}
}
И в XML это называется так:
<package.path.ViewPagerNoSwipe
android:layout_width="match_parent"
android:layout_height="match_parent" />
В kotlin мы можем решить эту проблему, создав собственный виджет, наследующий класс ViewPager, и добавив значение флага для управления поведением смахивания.
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
}
}
В 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" />
Программно отключите смахивание. Еслиkotlin-android-extensions
плагин применяется в build.gradle, смахивание можно отключить, написав приведенный ниже код.
view_pager.pagingEnabled = false
В противном случае мы можем отключить считывание с помощью кода ниже:
val viewPager : ViewPager = findViewById(R.id.view_pager)
viewPager.pagingEnabled = false
В Kotlin моё решение, сочетающее приведенные выше ответы.
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
}
}
Попробуйте переопределить и вернуть true из onInterceptTouchEvent() и / или onTouchEvent (), которые будут использовать сенсорные события на пейджере.
Ни один из приведенного выше кода не работает гладко. Я попробовал это
<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>
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;
}
});
* NonSwipebleViewPager for kotlin
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.viewpager.widget.ViewPager
import java.lang.reflect.Field
class NonSwipebleViewPager : ViewPager {
constructor(context: Context?) : super(context!!) {
setMyScroller()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {
setMyScroller()
}
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
return false
}
override fun onTouchEvent(event: MotionEvent): Boolean {
return false
}
private fun setMyScroller() {
try {
val viewpager: Class<*> = ViewPager::class.java
val scroller: Field = viewpager.getDeclaredField("mScroller")
scroller.isAccessible = true
scroller.set(this, MyScroller(context))
} catch (e: Exception) {
e.printStackTrace()
}
}
inner class MyScroller(context: Context?) :
Scroller(context, DecelerateInterpolator()) {
override fun startScroll(
startX: Int,
startY: Int,
dx: Int,
dy: Int,
duration: Int
) {
super.startScroll(startX, startY, dx, dy, 350 /*1 secs*/)
}
}
}
Еще одно простое решение - отключить перелистывание на определенной странице (в данном примере на странице 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;
}
Если вам не нужен какой-либо тип прокрутки по горизонтали или вертикали от пальцев пользователя, вы можете попробовать -
yourViewPager.isUserInputEnabled = false
используйте «исусеринпутенаблед».
Здесь описывается простое решение: реализация масштабируемого ImageView путем расширения стандартного ViewPager в Phimpme Android (и в примере Github - PhotoView):
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;
}
}
}
Вот моя реализация
что если вы хотите отключить анимацию смахивания, вы можете использовать swipeListener влево и вправо и по-прежнему хотите прокрутить пальцем, но без анимации
1-Переопределение Viewpager
метод onInterceptTouchEvent
а также onTouchEvent
2- создай свой GestureDetector
3- обнаружить жест смахивания и использовать 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();
}
}
когда вы настраиваете ViewPager установить 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);
}
...
}
Если вы хотите реализовать то же самое для Android в Xamarin, вот перевод на C#
Я решил назвать атрибут "ScrollEnabled". Потому что iOS просто использует одинаковые имена. Таким образом, у вас одинаковое наименование на обеих платформах, это облегчает разработчикам.
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://stackru.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;
}
}
}
В файле.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>
Я просто немного настроил видовой пейджер и легко отключил смахивание.
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);
}
}
shareViewPager?.setOnTouchListener(object :View.OnTouchListener{
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
for (PAGE in 0..shareViewPager.adapter!!.count){
if (shareViewPager.currentItem==PAGE){
shareViewPager.setCurrentItem(PAGE-1,false)
shareViewPager.setCurrentItem(PAGE,false)
}}
return true
}
})