/**
* Uses a combination of a PageTransformer and swapping X & Y coordinates
* of touch events to create the illusion of a vertically scrolling ViewPager.
*
* Requires API 11+
*
*/
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
@Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // [0,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
package com.crnlgcs.sdk.widgets;
import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
public class VerticalViewPager extends ViewPager {
private static final String TAG = "VerticalViewPager";
private static final boolean DEBUG = true;
private float mLastMotionX;
private float mLastMotionY;
private float mTouchSlop;
private boolean mVerticalDrag;
private boolean mHorizontalDrag;
// Vertical transit page transformer
private final ViewPager.PageTransformer mPageTransformer = new ViewPager.PageTransformer() {
@Override
public void transformPage(View view, float position) {
final int pageWidth = view.getWidth();
final int pageHeight = view.getHeight();
if (position < -1) {
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) {
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// set Y position to swipe in from top
float yPosition = position * pageHeight;
view.setTranslationY(yPosition);
} else {
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
};
public VerticalViewPager(Context context) {
super(context, null);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
init();
}
private void init() {
// Make page transit vertical
setPageTransformer(true, mPageTransformer);
// Get rid of the overscroll drawing that happens on the left and right (the ripple)
setOverScrollMode(View.OVER_SCROLL_NEVER);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
if (DEBUG) Log.v(TAG, "onTouchEvent " + x + ", " + y);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastMotionX = x;
mLastMotionY = y;
if (!super.onTouchEvent(ev))
return false;
return verticalDrag(ev);
}
case MotionEvent.ACTION_MOVE: {
final float xDiff = Math.abs(x - mLastMotionX);
final float yDiff = Math.abs(y - mLastMotionY);
if (!mHorizontalDrag && !mVerticalDrag) {
if (xDiff > mTouchSlop && xDiff > yDiff) { // Swiping left and right
mHorizontalDrag = true;
} else if (yDiff > mTouchSlop && yDiff > xDiff) { //Swiping up and down
mVerticalDrag = true;
}
}
if (mHorizontalDrag) {
return super.onTouchEvent(ev);
} else if (mVerticalDrag) {
return verticalDrag(ev);
}
}
case MotionEvent.ACTION_UP: {
if (mHorizontalDrag) {
mHorizontalDrag = false;
return super.onTouchEvent(ev);
}
if (mVerticalDrag) {
mVerticalDrag = false;
return verticalDrag(ev);
}
}
}
// Set both flags to false in case user lifted finger in the parent view pager
mHorizontalDrag = false;
mVerticalDrag = false;
return false;
}
private boolean verticalDrag(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
ev.setLocation(y, x);
return super.onTouchEvent(ev);
}
}
/**
* Swaps the X and Y coordinates of your touch event
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(swapXY(ev));
}
private MotionEvent swapXY(MotionEvent ev) {
//Get display dimensions
float displayWidth=this.getWidth();
float displayHeight=this.getHeight();
//Get current touch position
float posX=ev.getX();
float posY=ev.getY();
//Transform (X,Y) into (Y,X) taking display dimensions into account
float newPosX=(posY/displayHeight)*displayWidth;
float newPosY=(1-posX/displayWidth)*displayHeight;
//swap the x and y coords of the touch event
ev.setLocation(newPosX, newPosY);
return ev;
}
17 回答
有一个垂直ViewPager的新的非官方开源实现:
Announcement: https://plus.google.com/107777704046743444096/posts/1FTJfrvnY8w
Code: https://github.com/LambergaR/VerticalViewPager/
有一些开源项目声称可以做到这一点 . 他们来了:
kaelaela/VerticalViewPager,2015年10月的最新提交和一位撰稿人
castorflex/VerticalViewPager,2014年2月的最新提交和三个贡献者
LambergaR/VerticalViewPager/,2013年8月的最新提交和一位撰稿人
这些已被作者标记为弃用:
还有一件事:
deskclock
应用程序中还有一个文件名VerticalViewPager.java in the Android source,但它没有找到它的文档 .当前更活跃的存储库是lsjwzh/RecyclerViewPager .
last commit 2016年8月12日,16位贡献者,1840颗星
基于
RecyclerView
我有一个解决方案,分两个步骤为我工作 .
379
onInstantiateItem()
ofPagerAdapter
,创建视图并将其旋转-90:如果您使用的是
FragmentPagerAdapter
,那么:ViewPager
视图旋转90度:至少对我的要求来说,它就像一个魅力 .
看看这个:https://github.com/JakeWharton/Android-DirectionalViewPager
或者以下问题可能对您有所帮助:Vertical 'Gridview with pages' or 'Viewpager'
您可以使用ViewPager.PageTransformer来表示垂直
ViewPager
的错觉 . 要使用垂直拖动而不是水平拖动来实现滚动,您必须覆盖ViewPager
的默认触摸事件并在处理它们之前交换MotionEvent
的坐标,例如:当然,您可以根据需要调整这些设置 . 结尾看起来像这样:
稍微延伸this答案帮助我达到了我的要求(垂直视图寻呼机动画类似于Inshorts - News in 60 words)
我希望这对其他人有帮助 .
更好的方法是将viewpager旋转90度,并使用constraintLayout调整位置 .
这是一个垂直可滚动的ViewPager实现 . 与RecyclerView和ListView一起使用 . guochong/VerticalViewPager .
使用ViewPager.PageTransformer使ViewPager垂直滚动,并提供View.OnTouchListener来处理滚动冲突 .
我最终创建了一个可以垂直或水平滚动的新DirectionalViewPager,因为我在这里看到的所有解决方案都有缺陷:
现有VerticalViewPager实现(由castorflex和LambergaR提供)
它们基于非常旧的支持库版本 .
使用坐标交换的转换技巧
仍然从左/右边缘显示过度滚动 .
Page flinging无法正常工作,因为即使交换坐标,
VelocityTracker.computeCurrentVelocity
仍然使用X轴计算速度,可能是因为它在内部使用忽略坐标交换的本机调用 .查看旋转
需要旋转每个子视图的Hack .
如果你想读取其他东西的坐标,你必须交换轴 .
实际上Android source中已有一个 . 我修改它以更好地工作:
对于正在努力使垂直ViewPager在水平方向上工作的人,只需执行以下操作:
垂直ViewPager
水平ViewPager
即使我通过交换X和Y使ViewPager垂直也面临同样的问题 . 它一点也不顺利 .
那是因为它曾经只在滑动角度小于7.125度时才起作用= arctan(1/8)而拦截时和14度= arctan(1/4)同时触摸;当原始ViewPager水平一个工作时,滑动角度小于26.565度= arctan(1/2),而拦截和45度= arctan(1)触摸时 .
这就是为什么我复制Android support-core-ui的代码并将值移动到变量,我用反射将其乘以 .
请在https://github.com/deepakmishra/verticalviewpager找到代码和README,在https://github.com/deepakmishra/verticalviewpager/blob/master/app/src/main/java/com/viewpager/VerticalViewPager.java找到VerticalViewPager
Antoine Merle的垂直视图寻呼机实现:
https://github.com/castorflex/VerticalViewPager
只是对@Brett和@Salman666的答案进行了改进,以便将坐标(X,Y)正确转换为(Y,X),因为设备显示为矩形:
...
...
但是,仍需要采取措施来提高触摸屏的响应能力 . 这个问题可能与@msdark评论@ Salman666的答案有关 .
垂直ViewPager
这里的其他答案似乎都有一些问题 . 即使是推荐的开源项目也没有合并最近的pull请求和bug修复 . 然后我发现谷歌的VerticalViewPager他们使用了一些DeskClock应用程序 . 我相信使用Google _373506的版本与当前最高投票的答案类似,但更彻底 . )
完整示例
这个例子是几乎与我的Basic ViewPager Example完全相同,只需要进行一些小的改动就可以使用VerticalViewPager类 .
XML
添加主活动和每个页面(片段)的xml布局 . 请注意,我们使用VerticalViewPager而不是标准ViewPager . 我将在下面包含以下代码 .
activity_main.xml中
fragment_one.xml
代码
VerticalViewPager的代码不是我的 . 它只是Google源代码中的精简版(没有评论) . 查看最新版本的一个 . 那里的评论也有助于理解正在发生的事情 .
VerticalViewPager.java
MainActivity.java
我只换了
VerticalViewPager
为ViewPager
.完了
您应该能够运行该应用程序并在上面的动画中查看结果 .
另见
Basic ViewPager Example
ViewPager with FragmentPagerAdapter