如何通过在ViewPager中用手指滑动来禁用分页,但仍然能够以编程方式滑动?

the*_*ist 521 android android-viewpager

我有ViewPager,在它下面我有10个按钮.通过单击按钮,例如#4,寻呼机立即转到第4页mPager.setCurrentItem(3);.但是,我想通过水平手指滑动来禁用分页.因此,寻呼完成通过点击按钮.那么,我怎么能用手指禁用滑动?

lou*_*uie 864

你需要子类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.
     */
Run Code Online (Sandbox Code Playgroud)

这是一个完整的解决方案:

首先,将此类添加到您的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*/);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,请确保使用此类而不是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" />
Run Code Online (Sandbox Code Playgroud)

这个特定的例子在a中LinearLayout并且意味着占据整个屏幕,这就是为什么layout_weight是1并且layout_height是0dp.

并且setMyScroller();方法是平滑过渡

  • 使用键盘左/右键时,此答案不会阻止页面滑动(在模拟器中尝试).触摸设备可能不是问题,但对于电视和笔记本电脑,它可能是一个问题. (24认同)
  • @MarvinLabs @Thorbear通过键盘禁用页面滚动的正确方法是覆盖`executeKeyEvent()`.这是然后调用`allowScroll()`的方法.`//永远不允许通过键盘LEFT,RIGHT,TAB和SHIFT + TAB键滑动``@Override public boolean executeKeyEvent(KeyEvent event){return false; }` (7认同)
  • 好吧,这也会禁用垂直滚动...如果您使用的是列表视图,这是不好的。 (5认同)
  • 我希望谷歌能够提供一种方法,我们提供了如何处理触摸/滑动的界面方法,因为我相信它不会受到伤害? (5认同)
  • @louielouie在旁注:有什么特别的原因,为什么你选择`0dp`高度/`1`重量超过`match_parent`高度来实现全屏效果?这样做会提高性能吗? (2认同)
  • 仍然滚动页面一点。但是如果您继续滑动它,最终将滚动页面。 (2认同)
  • 依靠将来可以更改的变量名“ mScroller”不是一个好的解决方案。 (2认同)
  • 首先,不要重写`onInterceptTouchEvent()` - 它会阻止子视图接收输入。其次,不要使用反射来访问私有字段(尤其是在生产系统中) - 您将来可能会遇到问题。第三,阅读有关“onTouchEvent()”应返回什么结果的文档。第四,要禁用“一点点滑动”,您可以尝试重写以下方法:`protected void onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {scrollTo(0, 0); }`。 (2认同)

Raj*_*jul 450

更一般的扩展ViewPager是创建一个SetPagingEnabled方法,以便我们可以动态启用和禁用分页.要启用/禁用滑动,只需要两种方法:onTouchEventonInterceptTouchEvent.如果禁用分页,则两者都将返回"false".

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;
    } 
}
Run Code Online (Sandbox Code Playgroud)

然后选择此项而不是XML中的内置viewpager

<mypackage.CustomViewPager 
    android:id="@+id/myViewPager" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent" />
Run Code Online (Sandbox Code Playgroud)

您只需要调用setPagingEnabled方法,false用户将无法滑动到分页.

  • 您可以使用`return enabled && super.onTouchEvent(event);来简化`onTouchEvent`和`onInterceptTouchEvent`方法. (27认同)
  • 我还建议将字段名称从"enabled"更改为"swipePageChangeEnabled"或类似的东西.编写代码一个月后,您几乎不记得"启用"的含义,特别是当您的类被称为"CustomViewPager"时,不清楚自定义是什么.这也是一种风格问题,但如果您不希望最终得到不受支持的项目,则样式很重要,这比完全重写更容易. (6认同)
  • 我在哪里调用setPagingEnabled?._. (3认同)
  • 适用于具有多个子视图的视图,其中一个使用水平滚动(例如,AChartEngine图表).在我的片段中,我使用对我的活动的回调来告诉它打开或关闭分页,具体取决于用户是否触摸(ACTION_MOVE)图表或离开它(ACTION_CANCEL).非常感谢! (2认同)
  • @Ntikki,你可以像这样声明你的`ViewPager`后调用`setPagingEnabled()`方法:`CustomViewPager yourCustomViewPager = (CustomViewPager) findViewById(R.id.yourCustomViewPager); yourCustomViewPager.setPagingEnabled(false)` (2认同)
  • 为什么在`onTouchEvent()`和`onInterceptTouchEvent()`中返回`false`?这种方式触摸寻呼机标题(或图标)将进行滑动.返回`true`而不是禁用它. (2认同)
  • 在我将setPagingEnabled()设置为true之后,布局中的所有视图都变为无法点击...就像布局顶部有一个透明的墙叠加层一样 (2认同)

Way*_*yne 102

最简单的办法就是setOnTouchListener和回报trueViewPager.

mPager.setOnTouchListener(new OnTouchListener()
    {           
        @Override
        public boolean onTouch(View v, MotionEvent event)
        {
            return true;
        }
    });
Run Code Online (Sandbox Code Playgroud)

  • 有时不起作用.我正在使用带有FragmentPagerAdapter和几个Fragment的ViewPager.将OnTouchListener设置为寻呼机后,如果我向左移动,UI仍会向左移动一点. (49认同)
  • 当你在返回`true`之前添加`pager.setCurrentItem(pager.getCurrentItem());`时它会起作用. (14认同)
  • 这是PARTLY工作解决方案.水平滑动以某种奇怪的方式处理.我使用Drag'n Drop Gridview作为ViewPager的一部分进行了测试.使用此解决方案拖动时,从左向右和从右向左拖动失败.就个人而言,我发现Rajul的方法是最好的. (4认同)

Pie*_*tro 57

最好声明它是可设置的,因此您可以从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;
}
Run Code Online (Sandbox Code Playgroud)

在您的values/attr.xml中:

<declare-styleable name="MyViewPager">
  <attr name="swipeable" format="boolean" />
</declare-styleable>
Run Code Online (Sandbox Code Playgroud)

以便您可以在布局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" />
Run Code Online (Sandbox Code Playgroud)

当然,您仍然可以拥有get/set属性.


sid*_*don 56

如果您将ViewPager本身放在另一个ViewPager中onTouchEvent,onInterceptTouchEvent则仅覆盖并且还不够.子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);
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这在另一个ViewPager内的ViewPager上效果很好。 (2认同)

Gre*_*ase 32

如果从ViewPager扩展,则还必须覆盖executeKeyEvent @araks之前指示的内容

@Override
public boolean executeKeyEvent(KeyEvent event)
{
    return isPagingEnabled ? super.executeKeyEvent(event) : false;
}
Run Code Online (Sandbox Code Playgroud)

因为某些设备(如Galaxy Tab 4 10')显示这些按钮,大多数设备从不显示它们:

键盘按钮

  • 三星总是为Android开发者搞砸了. (47认同)
  • @ toobsco42三星在这个例子中并不孤单.SwiftKey(流行的键盘应用程序)允许用户在键盘上放置箭头. (2认同)

小智 27

要禁用滑动

mViewPager.beginFakeDrag();
Run Code Online (Sandbox Code Playgroud)

启用滑动

mViewPager.endFakeDrag();
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个好的解决方案,因为它允许小部分移动,至少在我的应用程序中. (19认同)

Nil*_*hod 22

现在我们无需创建自定义 ViewPager

ViewPager2 android中可用的新名称视图

垂直方向支撑

  • ViewPager2除了传统的水平寻呼外,还支持垂直寻呼。您可以ViewPager2通过设置元素的android:orientation属性来启用元素的垂直分页

使用XML

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:orientation="vertical" />
Run Code Online (Sandbox Code Playgroud)

使用代码

禁用正在viewpager2使用的刷卡

viewPager2.setUserInputEnabled(false);
Run Code Online (Sandbox Code Playgroud)

viewpager2使用中启用刷卡

viewPager2.setUserInputEnabled(true);
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请检查此

更新

请检查:从ViewPager迁移到ViewPager2

  • 很好,但看起来仍然不完整。我用5个不同的片段进行了尝试,在页面之间切换之后,片段中的一种丢失了,即视图既没有还原也没有重新膨胀 (3认同)

Ode*_*ner 13

我需要禁用在一个特定页面上滑动,并给它一个很好的橡皮筋动画,这里是如何:

    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);
            }
        }
Run Code Online (Sandbox Code Playgroud)


L. *_*ter 12

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果设置canScrolltrue,ViewPager则可以用手指轻扫,false相反.

我在我的项目中使用它,直到现在它都运行良好.

  • 如果我在 ViewPager 中有一个按钮并且我想要点击怎么办?此解决方案会停止单击 viewpager 下方的所有内容.. (2认同)

Din*_*uka 11

我知道发布这篇文章的时间太晚了,但这是一个很小的黑客来实现你的结果;)

只需在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>
Run Code Online (Sandbox Code Playgroud)

然后添加一个OnClickListener到你的RelativeLayout.

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

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

通过布局,布局和行为获得一点创意,你将学会找到一种方法来完成你想象的任何事情:)

祝好运!

  • 这不会也阻止 viewpager 中的触摸吗? (2认同)

Tay*_*har 9

如果您使用的是 ViewPager2,则只需使用:

viewpager.setUserInputEnabled(false);

文档

启用或禁用用户启动的滚动。这包括触摸输入(滚动和滑动手势)和辅助功能输入。尚不支持禁用键盘输入。当用户启动的滚动被禁用时,通过 setCurrentItem 的编程滚动仍然有效。默认情况下,启用用户启动的滚动。

感谢:https : //stackoverflow.com/a/61685871/9026710


Sag*_*ain 6

在 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
    }
}
Run Code Online (Sandbox Code Playgroud)

在 xml 中

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
    }
}
Run Code Online (Sandbox Code Playgroud)

以编程方式禁用滑动。如果kotlin-android-extensionsbuild.gradle 中应用了插件,则可以通过编写以下代码来禁用滑动。

view_pager.pagingEnabled = false
Run Code Online (Sandbox Code Playgroud)

否则,我们可以使用以下代码禁用滑动:

val viewPager : ViewPager = findViewById(R.id.view_pager)
viewPager.pagingEnabled = false
Run Code Online (Sandbox Code Playgroud)

  • 覆盖`onInterceptTouchEvent()`将阻止子视图接收输入。在大多数情况下,这不是预期的行为。 (2认同)

Ada*_*amK 5

尝试覆盖onInterceptTouchEvent()和/或onTouchEvent()并返回 true ,这将消耗寻呼机上的触摸事件。


mad*_*adx 5

我成功地使用了这堂课。需要重写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;
    }

}
Run Code Online (Sandbox Code Playgroud)

并在xml中这样调用它:

<package.path.ViewPagerNoSwipe
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Run Code Online (Sandbox Code Playgroud)


Jef*_*ett 5

在 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
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

在科特林

viewPagerNavigation.isUserInputEnabled = false
Run Code Online (Sandbox Code Playgroud)