Is there a way to make a ViewPager
that does not scroll horizontally, but vertically?!

- 43,104
- 15
- 109
- 137

- 2,028
- 3
- 19
- 26
-
1There is a new unofficial open source implementation of a vertical ViewPager: **Announcement:** https://plus.google.com/107777704046743444096/posts/1FTJfrvnY8w **Code:** https://github.com/LambergaR/VerticalViewPager/ – Vasily Sochinsky Jun 04 '13 at 14:13
-
There is a new implementation based on the 19 support library: https://github.com/castorflex/VerticalViewPager – Vasily Sochinsky Jan 06 '14 at 00:20
-
This is not the same as as https://github.com/castorflex/VerticalViewPager , despite having a similar name. – Flimm Aug 18 '16 at 13:19
-
Vertical view pager implementation by Antoine Merle: https://github.com/castorflex/VerticalViewPager – micnoy Jan 05 '14 at 16:20
-
1There is control called [ViewPager2](https://developer.android.com/jetpack/androidx/releases/viewpager2) check here for demo https://stackoverflow.com/a/54643817/7666442 – AskNilesh Feb 21 '19 at 04:32
14 Answers
You can use a ViewPager.PageTransformer to give the illusion of a vertical ViewPager
. To achieve scrolling with a vertical instead of a horizontal drag you will have to override ViewPager
's default touch events and swap the coordinates of MotionEvent
s prior to handling them, e.g.:
/**
* 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));
}
}
Of course you can tweak these settings as you see fit. Ends up looking like this:
-
4
-
What is the exact question? By the way, I didn't post the answer, just added the referred image. – Phantômaxx Jun 16 '14 at 09:21
-
Wouldn't this example give trouble when width is not set to the parent width? – Matthias Van Parijs Sep 17 '14 at 15:28
-
Thanks. I got it to work using this. To show different things per page, do i just create a different layout and assign it? – kevinl Apr 15 '15 at 16:56
-
-
1@Brett in this example I want to perform some action on swipe left and right. how to do that – Prabs Nov 18 '15 at 07:06
-
This answer is so much nicer than the other duplicate question I was looking at. I would upvote on the sample animation alone. Thank you – Script Kitty Jan 01 '16 at 02:40
-
I was doing something wrong when implementing this and it would unpredictably sometimes scroll and sometimes not. If anyone else has that issue, you can always return true on onInterceptTouchEvent, but I think you'll always let the parent handle it and sacrifice never being able to let the child views handle it. – Script Kitty Jan 01 '16 at 19:57
-
What if you want to add a negative page margin so you can see the adjacents pages? `adapter.pagemargin(-margin)` wouldn't work since it modifies the width: `public void setPageMargin(int marginPixels) { final int oldMargin = mPageMargin; mPageMargin = marginPixels; final int width = getWidth(); recomputeScrollPosition(width, width, marginPixels, oldMargin); requestLayout(); }` Trying to modify this leads to copy the `recomputeScrollPosition`method, which then leads to copy many other private methods since they are not accessible for overriding.. – Javier Mendonça Jan 05 '16 at 09:18
-
1@Brett it works as expected, transition is taking vertically. But the problem is i have to swipe right/left to do transition and on swiping top/bottom no transition take place. – Karan Khurana Feb 12 '16 at 13:15
-
Getting an error with the following statement: boolean intercepted = super.onInterceptTouchEvent(swapXY(ev)); Error: Cannot resolve method onInterceptTouchEvent(android.View.MotionEvent) – webgenius Apr 21 '16 at 12:28
-
As no smooth fading in is done here, what's the point of setting alpha to 0 when the screen is offscreen? – Overclover Jun 04 '16 at 08:01
-
-
@Brett When I use your custom viewpager in another normal viewpager, the normal viewpager won't allow me to scroll to the right anymore (while scrolling to the left still works). Any idea how I could fix this? – Todd Sewell Dec 09 '16 at 21:46
-
It's work, I just wanted to modify animation how can I do that, I want to give https://developer.android.com/training/animation/screen-slide.html#DepthPageTransformer "DepthPageTransformer" Animation. how that is possible as it is also extends PageTransformer. – Hitesh Dhamshaniya Jan 05 '17 at 12:41
-
Perfect, except that `OffscreenPageLimit` doesn't seem to work if greater than 1... – Donkey Jan 12 '17 at 13:06
-
I have added 2 fragments to my adapter but it is showing only one. Unable to swipe down for the other. Why ? Can anyone plz help – Gaurav Arora Mar 06 '17 at 08:05
-
How would you stop scrolling after certain point? overriding `getPageWidth` doesn't work... – Daniil Orekhov May 31 '17 at 21:58
-
I can't understand the logic used in `transformPage()` method here, can somebody go through it step by step and explain it to me? May be in a separate answer? I have posted question here as well: https://stackoverflow.com/questions/50284470/need-a-detailed-explanation-of-the-transformpage-method – Nah May 11 '18 at 03:37
-
3@Brett I was using your solution. but now I am getting the swipping issue in andorid pie devices. Has any one facing same issue? – Jishant Jan 03 '19 at 10:38
-
@Ancee try this: https://stackoverflow.com/a/54896137/3115881 It isn't nice solution :( but works. – Pody01 Feb 27 '19 at 12:12
-
This is not smooth at all, especially on a taller device. also we need to scroll only in 90 degree, any deviation on that blocks the scrolls – droidev May 10 '19 at 05:19
-
-
Vertical ViewPager
The other answers here all seemed to have some problems with them. Even the recommended open source projects weren't merging recent pull requests with bug fixes. Then I found a VerticalViewPager
from Google that they used some DeskClock app. I trust using Google's implementation a lot more than the other answers floating around here. (Google's version is similar to the current top-voted answer, but more thorough.)
Full Example
This example is almost exactly the same as my Basic ViewPager Example, with a few minor changes to make use of the VerticalViewPager
class.
XML
Add the xml layouts for the main activity and for each page (fragment). Note that we use VerticalViewPager
rather than the standard ViewPager
. I'll include the code for that below.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.verticalviewpager.MainActivity">
<com.example.myapp.VerticalViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
fragment_one.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/textview"
android:textSize="30sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
Code
The code for the VerticalViewPager
is not mine. It is just a stripped down version (without comments) from the Google source code. Check out that one for the most up to date version. The comments there are also helpful for understanding what is happening.
import android.support.v4.view.ViewPager;
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
this(context, null);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@Override
public boolean canScrollHorizontally(int direction) {
return false;
}
@Override
public boolean canScrollVertically(int direction) {
return super.canScrollHorizontally(direction);
}
private void init() {
setPageTransformer(true, new VerticalPageTransformer());
setOverScrollMode(View.OVER_SCROLL_NEVER);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final boolean toIntercept = super.onInterceptTouchEvent(flipXY(ev));
flipXY(ev);
return toIntercept;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final boolean toHandle = super.onTouchEvent(flipXY(ev));
flipXY(ev);
return toHandle;
}
private MotionEvent flipXY(MotionEvent ev) {
final float width = getWidth();
final float height = getHeight();
final float x = (ev.getY() / height) * width;
final float y = (ev.getX() / width) * height;
ev.setLocation(x, y);
return ev;
}
private static final class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
final int pageWidth = view.getWidth();
final int pageHeight = view.getHeight();
if (position < -1) {
view.setAlpha(0);
} else if (position <= 1) {
view.setAlpha(1);
view.setTranslationX(pageWidth * -position);
float yPosition = position * pageHeight;
view.setTranslationY(yPosition);
} else {
view.setAlpha(0);
}
}
}
}
MainActivity.java
I only swapped out VerticalViewPager
for ViewPager
.
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
public class MainActivity extends AppCompatActivity {
static final int NUMBER_OF_PAGES = 2;
MyAdapter mAdapter;
VerticalViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = findViewById(R.id.viewpager);
mPager.setAdapter(mAdapter);
}
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUMBER_OF_PAGES;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return FragmentOne.newInstance(0, Color.WHITE);
case 1:
// return a different Fragment class here
// if you want want a completely different layout
return FragmentOne.newInstance(1, Color.CYAN);
default:
return null;
}
}
}
public static class FragmentOne extends Fragment {
private static final String MY_NUM_KEY = "num";
private static final String MY_COLOR_KEY = "color";
private int mNum;
private int mColor;
// You can modify the parameters to pass in whatever you want
static FragmentOne newInstance(int num, int color) {
FragmentOne f = new FragmentOne();
Bundle args = new Bundle();
args.putInt(MY_NUM_KEY, num);
args.putInt(MY_COLOR_KEY, color);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt(MY_NUM_KEY) : 0;
mColor = getArguments() != null ? getArguments().getInt(MY_COLOR_KEY) : Color.BLACK;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_one, container, false);
v.setBackgroundColor(mColor);
TextView textView = v.findViewById(R.id.textview);
textView.setText("Page " + mNum);
return v;
}
}
}
Finished
You should be able to run the app and see the result in the animation above.
See also

- 484,302
- 314
- 1,365
- 1,393
-
3Code is woking fine,just need 1 small help.In current Code we need to swipe from bottom to top for getting next page,but i want even small swipe also should get the next page. – AMIT Mar 26 '19 at 09:21
-
-
I have a solution that works for me in two steps.
onInstantiateItem()
ofPagerAdapter
, create the view and rotate it by -90:view.setRotation(-90f)
If you are using
FragmentPagerAdapter
, then:objFragment.getView().setRotation(-90)
Rotate
ViewPager
view by 90 degree:objViewPager.setRotation(90)
Works like a charm at least for my requirement.

- 12,645
- 6
- 57
- 62

- 630
- 1
- 6
- 12
-
4this solution works, BUT you still have to swipe horizontally whereas the viewPager now scrolls vertically – leochab Jan 09 '14 at 12:48
-
@Kulai i am not clear what to do here can you please explain little bit as per my understanding we have public Object instantiateItem(ViewGroup container, int position) here we need to do container.setRotation(-90f); – varun Jan 29 '14 at 12:03
-
1Doing so works like a charm! Althoug the view gets cut in the top and bottom part, and I can't figure out why :-/ – Nivaldo Bondança Apr 28 '14 at 16:55
-
2It works properly for a square image and not for a rectangular. The dimensions go wrong when a rectangle is rotated. – Kulai May 09 '14 at 05:02
-
2I'm not sure who up-voted this answer. this is not VerticalViewPager, this is ViewPager with 90 degree rotated, the gesture and the transition is just opposite. and it is not at all natural – droidev Jul 12 '19 at 03:19
For someone who is struggling with how to make vertical ViewPager works inside horizontal, just do following stuff:
Vertical ViewPager
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() {
setPageTransformer(true, new VerticalPageTransformer());
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
if (position < -1) {
view.setAlpha(0);
} else if (position <= 1) {
view.setAlpha(1);
view.setTranslationX(view.getWidth() * -position);
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else {
view.setAlpha(0);
}
}
}
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 intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
Horizontal ViewPager
public class HorizontalViewPager extends ViewPager {
private GestureDetector xScrollDetector;
public HorizontalViewPager(Context context) {
super(context);
}
public HorizontalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
xScrollDetector = new GestureDetector(getContext(), new XScrollDetector());
}
class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceX) > Math.abs(distanceY);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (xScrollDetector.onTouchEvent(ev)) {
super.onInterceptTouchEvent(ev);
return true;
}
return super.onInterceptTouchEvent(ev);
}
}

- 9,531
- 7
- 45
- 88
-
-
Start with tutorial - http://www.vogella.com/tutorials/AndroidCustomViews/article.html – Divers Oct 16 '17 at 07:03
-
How do I implement the above Horizontal and Vertical view pager classes in my view? Code snippet helps – user1810931 Oct 16 '17 at 13:33
-
-
I am using a library called "Material Calendar View" (https://github.com/prolificinteractive/material-calendarview) and it already has horizontal viewpager in it and I wanted to add vertical viewpager in it. – user1810931 Oct 16 '17 at 14:47
-
-
Thing I don't like is that the vertical uses the pagetransformer to do it, which prevents the use for other effects. – Jerry F Dec 14 '17 at 18:34
-
is anyone getting problem in swipping vertical view pager for Android Pie devices? – Jishant Jan 03 '19 at 10:31
-
A slight extension this answer helped me to achieve my requirement (Vertical View Pager to animate similar like in Inshorts - News in 60 words)
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));
}
}
I hope this might be helpful to someone else.

- 1
- 1

- 6,276
- 6
- 38
- 80
-
1
-
Didn't you find problem of with swiping up fast the transition is not smooth – Praneeth Feb 26 '16 at 10:31
-
-
Just a comment to clarify: This solution changes the animation in a way that the new page does not come from below but rather from behind the old page. So basically you don't have pages laying next to each other but rather a deck of pages, laying on top of each other. So, this solution might not be what the original question is trying to do. – Benjamin Basmaci May 13 '19 at 15:19
There are a few open source projects which claim to do this. Here they are:
- kaelaela/VerticalViewPager, latest commit in Oct 2015 and one contributor
- castorflex/VerticalViewPager, latest commit in Feb 2014 and three contributors
- LambergaR/VerticalViewPager/, latest commit in Aug 2013 and one contributor
These have been marked deprecated by their authors:
- Deprecated: Android Directional-ViewPager by JakeWharton https://github.com/JakeWharton/Android-DirectionalViewPager
And one more thing:
- There is also a file name VerticalViewPager.java in the Android source from the
deskclock
app, but it doesn't seem to be officially supported as I can't find documentation for it.

- 136,138
- 45
- 251
- 267
Check this out : https://github.com/JakeWharton/Android-DirectionalViewPager
Or the following question may help you: Vertical 'Gridview with pages' or 'Viewpager'
-
2Something to keep in mind is that the `DirectionalViewPager` hasn't been updated for quite a while and hence is missing some of the newer features of the `ViewPager` in the support library; i.e. `setPageMargin(int)`. Generally it should do the job though. And if not, it's not too tricky to grab the source code for `ViewPager` and swap out all x/y and width/height logic. – MH. Nov 20 '12 at 18:29
-
I have already take into consideration the `DirectionalViewPager` but, as MH said, it is not updated (its developer consider it as DEPRECATED)!! It's incredible that Android developers has not provided to community a vertical `ViewPager`, but only horizantal. I'm newer to Android, develop my vertical `ViewPager` swapping out x/y is not so easy for me.. i'd prefer an already built solution.. anyway, thank you – user1709805 Nov 21 '12 at 08:13
-
2
Just an improvement on the answers from @Brett and @Salman666 to correctly transform coordinates (X,Y) into (Y,X) since device displays are rectangular:
...
/**
* 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;
}
...
However, something still needs to be done to better the touchscreen responsiveness. The issue might be related to what @msdark commented on @Salman666's answer.
-
I have some improvements by always returning `true` in `onInterceptTouchEvent` and I've also modified the Key bindings, so that using a DPAD it triggers scroll with UP/DOWN instead of left/right: https://gist.github.com/zevektor/fbacf4f4f6c39fd6e409 – Vektor88 Jul 08 '15 at 08:03
-
@Vektor88 always returning true will prevent inner views from receiving touch events .. best solution is as in brett answer .. – Mohammad Zekrallah Dec 08 '15 at 10:04
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
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() {
setPageTransformer(true, new VerticalPageTransformer());
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements PageTransformer {
@Override
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
if (position < -1) {
view.setAlpha(0);
} else if (position <= 1) {
view.setAlpha(1);
view.setTranslationX(pageWidth * -position);
float yPosition = position * pageHeight;
view.setTranslationY(yPosition);
} else {
view.setAlpha(0);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
ev.setLocation(ev.getY(), ev.getX());
return super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ev.setLocation(ev.getY(), ev.getX());
return super.onInterceptTouchEvent(ev);
}
}

- 81
- 3
-
1This works very nice, but i lose the touchEvent on elements or the left/swipe events ... – matiasfha Aug 31 '14 at 19:20
I ended up creating a new DirectionalViewPager that can scroll either vertically or horizontally because all of the solutions I've seen here have flaws:
Existing VerticalViewPager implementations (by castorflex and LambergaR)
- They are based on very old support library versions.
Transformation trick with coordinate swapping
- The overscroller is still shown from the left/right edges.
- Page flinging doesn't work properly, because even with the coordinates swapped,
VelocityTracker.computeCurrentVelocity
still calculates the velocity with the X axis, probably because this internally uses a native call that ignores the coordinate swap.
View rotation
- Hack that needs every child view to be rotated too.
- If you want to read coordinates for something else you have to swap the axis.

- 406
- 7
- 12
-
But I think it can't support PageTransformer because DirectionalViwePager didn't override viewpager – Gary Chen Sep 09 '20 at 06:19
There's actually already one in the Android source. I've modified it to work better though:
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);
}
}

- 14,721
- 5
- 39
- 40
-
this is not working properly .. even the android source version doesn't.. its glitchy and only works for right/left gestures .. if you do up/down it doesn't scroll .. at least for me .. – Mohammad Zekrallah Feb 07 '16 at 08:26
This is A vertical scrollable ViewPager implementation.Works well with RecyclerView and ListView. guochong/VerticalViewPager.
use ViewPager.PageTransformer to make ViewPager scroll vertically, and also provide a View.OnTouchListener to deal with the scroll conflict.
/**
* 1.dispatch ACTION_DOWN,ACTION_UP,ACTION_CANCEL to child<br>
* 2.hack ACTION_MOVE
*
* @param v
* @param e
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent e) {
Log.i(TAG, "onTouchEvent " + ", action " + e.getAction() + ", e.rawY " + e.getRawY() + ",lastMotionY " + lastMotionY + ",downY " + downY);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (downY == Float.MIN_VALUE && lastMotionY == Float.MIN_VALUE) {
//not start from MOTION_DOWN, the child dispatch this first motion
downY = e.getRawY();
break;
}
float diff = e.getRawY() - (lastMotionY == Float.MIN_VALUE ? downY : lastMotionY);
lastMotionY = e.getRawY();
diff = diff / 2; //slow down viewpager scroll
Log.e(TAG, "scrollX " + dummyViewPager.getScrollX() + ",basescrollX " + dummyViewPager.getBaseScrollX());
if (dummyViewPager.getScrollX() != dummyViewPager.getBaseScrollX()) {
if (fakeDragVp(v, e, diff)) return true;
} else {
if (ViewCompat.canScrollVertically(v, (-diff) > 0 ? 1 : -1)) {
Log.e(TAG, "scroll vertically " + diff + ", move.lastMotionY " + e.getY());
break;
} else {
dummyViewPager.beginFakeDrag();
fakeDragVp(v, e, diff);
adjustDownMotion(v, e);
return true;
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (dummyViewPager.isFakeDragging()) {
try {
dummyViewPager.endFakeDrag();
} catch (Exception e1) {
Log.e(TAG, "", e1);
}
}
reset();
break;
}
return false;
}

- 1
- 2
-
Welcome to StackOverflow and thanks for your help. You might want to make your answer even better by adding some code. – Kenzo_Gilead Sep 05 '17 at 11:45
Even I was facing the same problem with making ViewPager vertical by swapping X and Y. It was not at all smooth.
That's because it used to work only when the angle of swiping was less than 7.125 degrees = arctan (1/8) while intercept and 14 degrees = arctan (1/4) while touch; as original ViewPager horizontal one work when the angle of swiping is less than 26.565 degrees = arctan (1/2) while intercept and 45 degrees = arctan (1) while touch.
That's why I copied the code of Android support-core-ui and moved the values to variables, which I multiplied using reflection.
Please find the code and README at https://github.com/deepakmishra/verticalviewpager and VerticalViewPager at https://github.com/deepakmishra/verticalviewpager/blob/master/app/src/main/java/com/viewpager/VerticalViewPager.java

- 490
- 4
- 11
the better way is to rotate the viewpager by 90 degree, and use constraintLayout to adjust the location.

- 11
- 5