Answers:
您需要子类化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();
方法是平稳过渡
0dp
身高/ 1
体重解决方案而不是match_parent
身高来实现全屏效果?这样是否可以提高性能?
executeKeyEvent()
。这是随后调用的方法allowScroll()
。 // Never allow swiping by keyboard LEFT, RIGHT, TAB and SHIFT+TAB keys
@Override public boolean executeKeyEvent(KeyEvent event) { return false; }
更一般的扩展ViewPager
是创建一个SetPagingEnabled
方法,以便我们可以即时启用和禁用分页。要启用/禁用刷卡,只需覆盖两种方法:onTouchEvent
和onInterceptTouchEvent
。如果禁用分页,则两者都将返回“ 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;
}
}
然后选择它而不是XML中的内置viewpager
<mypackage.CustomViewPager
android:id="@+id/myViewPager"
android:layout_height="match_parent"
android:layout_width="match_parent" />
您只需要使用调用该setPagingEnabled
方法,false
用户将无法滑动以进行分页。
onTouchEvent
并onInterceptTouchEvent
与方法return enabled && super.onTouchEvent(event);
最简单的办法就是setOnTouchListener
和回报true
的ViewPager
。
mPager.setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
return true;
}
});
pager.setCurrentItem(pager.getCurrentItem());
在返回之前添加时,它可以工作true
。
public int getItemPosition(Object object) { return POSITION_NONE; }
我的适配器中存在的布局故障,这会迫使页面重新生成并可能还会解决其他问题。
最好声明它为可样式化,因此可以从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;
}
并在您的values / 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本身放在另一个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);
}
}
现在我们无需创建自定义 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" />
使用代码
禁用正在viewpager2
使用的刷卡
viewPager2.setUserInputEnabled(false);
在viewpager2
使用中启用刷卡
viewPager2.setUserInputEnabled(true);
有关更多信息,请检查此
有关完整的示例,请检查此在Android中使用ViewPager2
viewpager2
与RecyclerView.Adapter
如果从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);
}
}
onPageSelected
将被执行。甚至下一页也会切换。因此,不需要此代码。
我知道发布此消息的时间过晚,但这是实现目标的小技巧;)
只需在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>
然后添加OnClickListener
到您的RelativeLayout
。
dummyView = (RelativeLayout) findViewById(R.id.dummyView);
dummyView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Just leave this empty
}
});
借助布局,布局和行为获得一些创意,您将学习找到一种方法来做绝对想像的事情:)
祝好运!
setClickable
代替,这是一种更简单的方法。setOnClickListener
onClick
我写了一个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
。
我在项目中使用了它,直到现在为止效果很好。
我成功地使用了这堂课。需要重写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" />
performClick()
方法不会被覆盖。
在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
}
}
onInterceptTouchEvent()
将阻止子视图接收输入。在大多数情况下,这不是预期的行为。
在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中应用了plugin,则可以通过编写以下代码来禁用滑动。
view_pager.pagingEnabled = false
否则,我们可以使用以下代码禁用刷卡:
val viewPager : ViewPager = findViewById(R.id.view_pager)
viewPager.pagingEnabled = false
onInterceptTouchEvent()
将阻止子视图接收输入。在大多数情况下,这不是预期的行为。
super.onInterceptTouchEvent(event)
导致这些视图不接收任何输入(这是onInterceptTouchEvent
方法的书面用途)。
onInterceptTouchEvent()
方法阻止它(内容)接收输入。
尝试从onInterceptTouchEvent()和/或onTouchEvent()重写并返回true ,这将消耗寻呼机上的触摸事件。
true
暗示触摸事件已处理吗?
禁用特定页面(在此示例中为第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;
}
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;
}
});
如果使用ImageView
s和编写图像库,并ViewPager
支持缩放和平移,请参见此处描述的简单解决方案:通过扩展Phimpme Android中的默认ViewPager (和Github示例-PhotoView)来实现可缩放的ImageView。此解决方案不能ViewPager
单独使用。
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;
}
}
}
如果您想在Xamarin中为Android实施相同的功能,则这里是C#的翻译
我选择将属性命名为“ ScrollEnabled ”。因为iOS仅使用exat相同的命名。因此,您在两个平台上的名称均相同,从而使开发人员更容易。
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;
}
}
}
在.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>
以上代码均无法正常工作。
<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>
这是我的实现
,如果您想禁用滑动动画,则可以向左和向右使用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);
}
...
}
我只是稍微定制了viewpager并轻松禁用了滑动。
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);
}
}
如果您使用的是ViewPager2,则只需使用:
viewpager.setUserInputEnabled(false);
从文档:
启用或禁用用户启动的滚动。这包括触摸输入(滚动和甩动手势)和辅助功能输入。尚不支持禁用键盘输入。禁用用户启动的滚动时,通过setCurrentItem进行程序化滚动仍然有效。默认情况下,启用用户启动的滚动。
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
}
})