在 ViewPager 中滑动方向
Posted
技术标签:
【中文标题】在 ViewPager 中滑动方向【英文标题】:Swipe Direction in ViewPager 【发布时间】:2014-09-03 11:37:37 【问题描述】:我有一个使用 ViewPager 和 FragmentStatePagerAdapter 的 android 应用。然而,我需要添加一个新功能,我必须知道滑动方向才能从一个片段视图移动到另一个片段视图,向左或向右移动。 (即,当我向左滑动时,我会做一件事,当我向右滑动时,我会做其他事情)。
请注意,我不需要在滑动结束后发生事件。当滑动发生时,我必须做这些事件。我想过使用setOnPageChangeListener()
,但它并没有告诉我滑动方向。你能告诉我如何确定滑动方向吗?
谢谢。
【问题讨论】:
【参考方案1】:我遇到了这个问题,需要知道滑动的方向因为它正在发生,以防止向某个方向滑动。这是我解决问题的方法,希望对某人有所帮助。我发现前面的所有示例都无法满足我的需要。
如果您将 ViewPager 子类化,则可以响应 ViewPager
的 onInterceptTouchEvent(MotionEvent event)
和 onTouchEvent(MotionEvent event)
方法
@Override
public boolean onInterceptTouchEvent(MotionEvent event)
boolean wasSwipeToRight = this.wasSwipeToRightEvent(event));
// Do what you want here with left/right swipe
return super.onInterceptTouchEvent(event);
@Override
public boolean onTouchEvent(MotionEvent event)
boolean wasSwipeToRight = this.wasSwipeToRightEvent(event));
// Do what you want here with left/right swipe
return super.onTouchEvent(event);
这里是用来确定事件方向的方法。
// Stores the starting X position of the ACTION_DOWN event
float downX;
/**
* Checks the X position value of the event and compares it to
* previous MotionEvents. Returns a true/false value based on if the
* event was an swipe to the right or a swipe to the left.
*
* @param event - Motion Event triggered by the ViewPager
* @return - True if the swipe was from left to right. False otherwise
*/
private boolean wasSwipeToRightEvent(MotionEvent event)
switch (event.getAction())
case MotionEvent.ACTION_DOWN:
downX = event.getX();
return false;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
return downX - event.getX() > 0;
default:
return false;
【讨论】:
downX 对我来说总是返回 0.0 @Roon13 是event.getX()
在您触摸屏幕时返回一个值?【参考方案2】:
这很简单,只需引入一个实例变量来跟踪当前页面。
public class MyActivity extends Activity implements ViewPager.OnPageChangeListener
private ViewPager viewPager;
private int currentPosition;
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
// Do trivial stuff
.
.
.
viewPager.setOnClickListener(this); // doesnt have to be in onCreate
@Override
public void onPageSelected(int position)
if(currentPosition < position)
// handle swipe LEFT
else if(currentPosition > position)
// handle swipe RIGHT
currentPosition = position; // Update current position
【讨论】:
【参考方案3】:看看ViewPager.OnPageChangeListener的onPageSelected(int position)
方法。 position
给出新选择页面的索引。如果您跟踪当前页面索引,则可以将其与position
索引进行比较,以获取滑动的左/右方向。
既然您提到 OnPageChangeListener 没有帮助,请考虑 ViewPager
的 onInterceptTouchEvent (MotionEvent ev)
方法。考虑 MotionEvent 的 ACTION_DOWN、ACTION_MOVE 和 ACTION_UP。希望这会有所帮助。
【讨论】:
【参考方案4】:也许你可以像这样构建一个监听器:
buttonIcons.setOnTouchListener(new OnTouchListener()
@Override
public boolean onTouch(final View v, final MotionEvent event)
switch (event.getAction())
case MotionEvent.ACTION_DOWN:
downX = event.getX();
return true;
case MotionEvent.ACTION_UP:
upX = event.getX();
final float deltaX = downX - upX;
if (deltaX > 0 && deltaX > SWIPE_MIN_DISTANCE)
final Animation loadInAnimation = AnimationUtils
.loadAnimation(
activity.this,
R.anim.slide_in_right);
final Animation loadOutAnimation = AnimationUtils
.loadAnimation(
activity.this,
R.anim.slide_out_left);
viewFlipper.setInAnimation(loadInAnimation);
viewFlipper.setOutAnimation(loadOutAnimation);
viewFlipper.showPrevious();
startSaveButtonAnimation();
if (deltaX < 0 && -deltaX > SWIPE_MIN_DISTANCE)
final Animation loadInAnimation = AnimationUtils
.loadAnimation(
activity.this,
R.anim.slide_in_left);
final Animation loadOutAnimation = AnimationUtils
.loadAnimation(
activity.this,
R.anim.slide_out_right);
viewFlipper.setInAnimation(loadInAnimation);
viewFlipper.setOutAnimation(loadOutAnimation);
viewFlipper.showNext();
startSaveButtonAnimation();
return true;
return false;
);
【讨论】:
非常感谢!有效!。我已经为此苦苦挣扎了很长时间!。【参考方案5】:我使用这个小实现来检测用户在滚动过程中是向右还是向左滚动。在某些我不知道的情况下,我可能会遇到一些错误,但这是我无需添加触摸侦听器就可以解决我需要的问题:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
int SCROLLING_RIGHT = 0;
int SCROLLING_LEFT = 1;
int SCROLLING_UNDETERMINED = 2;
int currentScrollDirection = 2;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
if (isScrollDirectionUndetermined())
setScrollingDirection(positionOffset);
if (isScrollingLeft())
Log.i("TabLayout","Scrolling LEFT");
if (isScrollingRight())
Log.i("TabLayout","Scrolling RIGHT");
private void setScrollingDirection(float positionOffset)
if ((1-positionOffset)>= 0.5)
this.currentScrollDirection = SCROLLING_RIGHT;
else if ((1-positionOffset)<= 0.5)
this.currentScrollDirection = SCROLLING_LEFT;
private boolean isScrollDirectionUndetermined()
return currentScrollDirection == SCROLLING_UNDETERMINED;
private boolean isScrollingRight()
return currentScrollDirection == SCROLLING_RIGHT;
private boolean isScrollingLeft()
return currentScrollDirection == SCROLLING_LEFT;
@Override
public void onPageSelected(int position)
@Override
public void onPageScrollStateChanged(int state)
if (state == ViewPager.SCROLL_STATE_IDLE)
this.currentScrollDirection = SCROLLING_UNDETERMINED;
);
我们可以检测滚动方向,因为当用户从左到右滚动时,positionOffset
从 0 -> 1 变为从右到左滚动时从 1 -> 0。然后我们可以检查该值一次在滚动的最开始,并仅在滚动结束时重置标志。
我们在空闲位置将currentScrollDirection
设置为未确定,而不是onPageSelected
,因为如果用户在滚动过程中释放onPageSelected
可能会被调用,然后您会得到错误的读数。
【讨论】:
【参考方案6】:其实你可以通过onPageScrolled
的参数找到方向。
以下侦听器设置为每个网络片段更改方向显示一次日志消息(如果左侧片段显示为左侧,如果没有片段显示为中心,如果右侧片段显示为右侧)。
类变量,需要跟踪
// -1 - left, 0 - center, 1 - right
private int scroll = 0;
// set only on `onPageSelected` use it in `onPageScrolled`
// if currentPage < page - we swipe from left to right
// if currentPage == page - we swipe from right to left or centered
private int currentPage = 0;
// if currentPage < page offset goes from `screen width` to `0`
// as you reveal right fragment.
// if currentPage == page , offset goes from `0` to `screen width`
// as you reveal right fragment
// You can use it to see
//if user continue to reveal next fragment or moves it back
private int currentOffset = 0;
// behaves similar to offset in range `[0..1)`
private float currentScale = 0;
听者
pager.addOnPageChangeListener(
new OnPageChangeListener()
@Override
public void onPageScrolled(int page, float scale, int offset)
if(scale != 0f && offset > 10)
if(currentOffset > offset && currentScale > scale && (page + 1) == currentPage)
if(scroll != -1)
newImageSet = true;
scroll = -1;
Log.e("scroll", "left");
else if(currentOffset < offset && currentScale < scale && page == currentPage)
if(scroll != 1)
newImageSet = true;
scroll = 1;
Log.e("scroll", "right");
currentOffset = offset;
currentScale = scale;
@Override
public void onPageSelected(int i)
Log.e("scroll","centre");
// we are centerd, reset class variables
currentPage = i;
currentScale = 0;
currentOffset = 0;
scroll = 0;
@Override
public void onPageScrollStateChanged(int i)
);
【讨论】:
【参考方案7】:您可以将 OnPageChangeListner 添加到 ViewPager 并执行以下操作:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
float tempPositionOffset = 0;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
if (position == 0)
if (tempPositionOffset < positionOffset)
Log.d("eric", "scrolling left ...");
else
Log.d("eric", "scrolling right ...");
tempPositionOffset = positionOffset;
Log.d("eric", "position " + position + "; " + " positionOffset " + positionOffset + "; " + " positionOffsetPixels " + positionOffsetPixels + ";");
@Override
public void onPageSelected(int position)
@Override
public void onPageScrollStateChanged(int state)
);
【讨论】:
【参考方案8】:通过使用 Kotlin 和 ViewPager2,这是我的解决方案,您可以将 OnPageChangeCallback 侦听器注册为下面的 sn-p(仅向右滑动)
val pager = rootView.findViewById<ViewPager2>(R.id.viewPager).apply
registerOnPageChangeCallback(object : OnPageChangeCallback()
var oldPosition = 0
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int
)
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
if (position < oldPosition)
setCurrentItem(oldPosition, false)
else
oldPosition = position
)
【讨论】:
【参考方案9】:您可以使用setOnPageChangeListener()
并正确实施onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
。
您可以通过 positionOffset 和位置变量来检测方向:
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
if(position == mCurrentPosition && positionOffset > 0)
//swipe right
else if (positionOffset > 0)
//swipe left
【讨论】:
你在哪里设置 mCurrentPosition ? 您可以将 mCurrentPosition 替换为 yourViewPager.getCurrentItem() 不行,viewPager.getCurrentItem 改变 onRelease。以上是关于在 ViewPager 中滑动方向的主要内容,如果未能解决你的问题,请参考以下文章
ViewPager2嵌套RecyclerView滑动冲突解决办法