Android: Vertical ViewPager
Asked Answered
K

16

136

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

Katharina answered 20/11, 2012 at 16:38 Comment(5)
There is a new unofficial open source implementation of a vertical ViewPager: Announcement: plus.google.com/107777704046743444096/posts/1FTJfrvnY8w Code: github.com/LambergaR/VerticalViewPagerCasino
Vertical view pager implementation by Antoine Merle: github.com/castorflex/VerticalViewPagerKurman
There is a new implementation based on the 19 support library: github.com/castorflex/VerticalViewPagerCasino
This is not the same as as github.com/castorflex/VerticalViewPager , despite having a similar name.Hewlett
There is control called ViewPager2 check here for demo https://mcmap.net/q/57213/-proper-implementation-of-viewpager2-in-androidCordi
O
232

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 MotionEvents 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:

VerticalViewPager demo

Ophthalmoscope answered 1/4, 2014 at 21:52 Comment(24)
@Der Golem how to use layout for this?Sartorial
What is the exact question? By the way, I didn't post the answer, just added the referred image.Christianna
Wouldn't this example give trouble when width is not set to the parent width?Identification
Thanks. I got it to work using this. To show different things per page, do i just create a different layout and assign it?Walcoff
And you can use this as <package.name.VerticalViewPager> in your layout xml file.Fly
@Ophthalmoscope in this example I want to perform some action on swipe left and right. how to do thatComfortable
This answer is so much nicer than the other duplicate question I was looking at. I would upvote on the sample animation alone. Thank youAlmswoman
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.Almswoman
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 recomputeScrollPositionmethod, which then leads to copy many other private methods since they are not accessible for overriding..Recreant
@Ophthalmoscope 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.Syllabi
Getting an error with the following statement: boolean intercepted = super.onInterceptTouchEvent(swapXY(ev)); Error: Cannot resolve method onInterceptTouchEvent(android.View.MotionEvent)Sassoon
As no smooth fading in is done here, what's the point of setting alpha to 0 when the screen is offscreen?Incubus
This should be the accepted answerMarcenemarcescent
@Ophthalmoscope 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?Bield
It's work, I just wanted to modify animation how can I do that, I want to give developer.android.com/training/animation/… "DepthPageTransformer" Animation. how that is possible as it is also extends PageTransformer.Nada
Perfect, except that OffscreenPageLimit doesn't seem to work if greater than 1...Afterdamp
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 helpBrooklynese
How would you stop scrolling after certain point? overriding getPageWidth doesn't work...Ancy
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: #50284970Scene
@Ophthalmoscope I was using your solution. but now I am getting the swipping issue in andorid pie devices. Has any one facing same issue?Nikolos
@Ancee try this: https://mcmap.net/q/57214/-vertical-viewpager-and-android-pie-inconsistent-behavior-with-swipe-gesture It isn't nice solution :( but works.Effluence
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 scrollsMckown
pixel android 9 does not workSimone
can we add a TabHost to it as well?Risley
A
27

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.

enter image description here

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.

VerticalViewPager.java

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

Aloud answered 19/10, 2017 at 9:7 Comment(3)
Code 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.Cryohydrate
@Cryohydrate have you found solution for that? Have the same issueTonitonia
can we add a TabHost to it as well?Risley
B
21

I have a solution that works for me in two steps.

  1. onInstantiateItem() of PagerAdapter, create the view and rotate it by -90:

    view.setRotation(-90f)
    

    If you are using FragmentPagerAdapter, then:

    objFragment.getView().setRotation(-90)
    
  2. Rotate ViewPager view by 90 degree:

    objViewPager.setRotation(90)
    

Works like a charm at least for my requirement.

Blinni answered 6/9, 2013 at 12:21 Comment(5)
this solution works, BUT you still have to swipe horizontally whereas the viewPager now scrolls verticallyChic
@Blinni 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);Evan
Doing so works like a charm! Althoug the view gets cut in the top and bottom part, and I can't figure out why :-/Aristotle
It works properly for a square image and not for a rectangular. The dimensions go wrong when a rectangle is rotated.Blinni
I'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 naturalMckown
C
15

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);
    }

}
Cooler answered 16/2, 2016 at 15:44 Comment(9)
How can I add this horizontal and vertical view pager to my view?Interpretation
Start with tutorial - vogella.com/tutorials/AndroidCustomViews/article.htmlCooler
How do I implement the above Horizontal and Vertical view pager classes in my view? Code snippet helpsInterpretation
ViewPager is already view, I don't understand what you asking about.Cooler
I am using a library called "Material Calendar View" (github.com/prolificinteractive/material-calendarview) and it already has horizontal viewpager in it and I wanted to add vertical viewpager in it.Interpretation
Then you should modify source code of that library.Cooler
Thing I don't like is that the vertical uses the pagetransformer to do it, which prevents the use for other effects.Giulietta
is anyone getting problem in swipping vertical view pager for Android Pie devices?Nikolos
its not smooth while scrolling it bounces alotSiobhansion
H
8

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.

Hot answered 1/12, 2015 at 11:46 Comment(4)
Ideal value for MIN_SCALE ?Sacristan
Didn't you find problem of with swiping up fast the transition is not smoothFarny
Why on earth would someone swipe up fast?Hot
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.Olivares
H
4

There are a few open source projects which claim to do this. Here they are:

These have been marked deprecated by their authors:

And one more thing:

Hewlett answered 18/8, 2016 at 13:52 Comment(0)
F
3

Check this out : https://github.com/JakeWharton/Android-DirectionalViewPager

Or the following question may help you: Vertical 'Gridview with pages' or 'Viewpager'

Futch answered 20/11, 2012 at 16:49 Comment(3)
Something 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.Malt
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 youKatharina
hi, did you managed to do the vertical viewpager?? thanskMotel
T
3

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.

Thiazine answered 20/1, 2015 at 1:50 Comment(2)
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: gist.github.com/zevektor/fbacf4f4f6c39fd6e409Edmonton
@Edmonton always returning true will prevent inner views from receiving touch events .. best solution is as in brett answer ..Individualize
B
2
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);
    }
}
Blouson answered 26/8, 2014 at 14:2 Comment(1)
This works very nice, but i lose the touchEvent on elements or the left/swipe events ...3d
K
1

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:

  1. Existing VerticalViewPager implementations (by castorflex and LambergaR)

    • They are based on very old support library versions.
  2. 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.
  3. 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.
Kielce answered 19/9, 2018 at 11:0 Comment(1)
But I think it can't support PageTransformer because DirectionalViwePager didn't override viewpagerVacuity
P
1

In 2023: Use ViewPager2 instead, with android:orientation="vertical" https://developer.android.com/develop/ui/views/animations/vp2-migration#vertical-support

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:orientation="vertical" />
Pelham answered 16/9, 2023 at 17:8 Comment(0)
F
0

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);
    }
}
Fleabag answered 4/2, 2016 at 2:9 Comment(1)
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 ..Individualize
B
0

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;
}
Biphenyl answered 5/9, 2017 at 11:26 Comment(1)
Welcome to StackOverflow and thanks for your help. You might want to make your answer even better by adding some code.Rheumy
S
0

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

Slang answered 30/3, 2018 at 9:2 Comment(0)
B
0

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

Byroad answered 28/6, 2018 at 5:57 Comment(0)
P
0

I am using jvm version 1.8 minSdk 24 using the viewpager2 has the option to scroll pages vertically like following

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"/>
Polymerization answered 31/1 at 10:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.