Android: pionowy ViewPager [zamknięty]


133

Czy istnieje sposób, aby ViewPagerprzewijać nie w poziomie, ale w pionie ?!


1
Pojawiła się nowa nieoficjalna implementacja Open Source pionowej ViewPager: Ogłoszenie: plus.google.com/107777704046743444096/posts/1FTJfrvnY8w Kod: github.com/LambergaR/VerticalViewPager
Wasilij Sochinsky

Implementacja pagera w widoku pionowym autorstwa Antoine Merle: github.com/castorflex/VerticalViewPager
micnoy

Jest nowa implementacja oparta na bibliotece wsparcia 19: github.com/castorflex/VerticalViewPager
Wasilij Sochinsky

To nie to samo, co github.com/castorflex/VerticalViewPager , pomimo podobnej nazwy.
Flimm

1
Jest kontrola o nazwie ViewPager2, sprawdź tutaj demo stackoverflow.com/a/54643817/7666442
AskNilesh

Odpowiedzi:


227

Możesz użyć ViewPager.PageTransformer, aby stworzyć iluzję pionu ViewPager. Aby uzyskać przewijanie w pionie zamiast w poziomie, będziesz musiał zastąpić ViewPagerdomyślne zdarzenia dotyku i zamienić współrzędne MotionEvents przed ich obsługą, np .:

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

}

Oczywiście możesz dostosować te ustawienia według własnego uznania. Kończy się tak:

Demo VerticalViewPager


4
@Der Golem, jak użyć układu do tego?
bShah,

Jakie jest dokładne pytanie? Nawiasem mówiąc, nie zamieściłem odpowiedzi, tylko dodałem wspomniany obraz.
Phantômaxx

1
@Brett w tym przykładzie Chcę wykonać jakąś akcję po przesunięciu palcem w lewo i w prawo. jak to zrobić
Prabs

1
@Brett działa zgodnie z oczekiwaniami, przejście odbywa się w pionie. Ale problem polega na tym, że muszę przesunąć palcem w prawo / w lewo, aby dokonać przejścia, a po przesunięciu w górę / w dół nie ma przejścia.
Karan Khurana,

2
@Brett Korzystałem z twojego rozwiązania. ale teraz dostaję problem swipping w urządzeniach andorid pie. Czy ktoś ma ten sam problem?
Jishant,

21

ViewPager w pionie

Wszystkie inne odpowiedzi wydawały się mieć z nimi pewne problemy. Nawet zalecane projekty open source nie łączyły ostatnich żądań ściągnięcia z poprawkami błędów. Potem znalazłem VerticalViewPagerod Google, że używali jakiejś aplikacji DeskClock. Ufam implementacji Google znacznie bardziej niż innym odpowiedziom krążącym tutaj. (Wersja Google jest podobna do aktualnie najczęściej głosowanej odpowiedzi, ale jest dokładniejsza).

Pełny przykład

Ten przykład jest prawie dokładnie taki sam, jak mój przykład Basic ViewPager , z kilkoma drobnymi zmianami w celu wykorzystania tej VerticalViewPagerklasy.

wprowadź opis obrazu tutaj

XML

Dodaj układy xml dla głównego działania i dla każdej strony (fragmentu). Zwróć uwagę, że używamy VerticalViewPagerzamiast standardu ViewPager. Dołączę do tego kod poniżej.

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>

Kod

Kod VerticalViewPagernie jest mój. To tylko okrojona wersja (bez komentarzy) z kodu źródłowego Google. Sprawdź ten, aby uzyskać najbardziej aktualną wersję. Komentarze są również pomocne w zrozumieniu tego, co się dzieje.

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

Zamieniłem się tylko VerticalViewPagerna 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;
        }
    }
}

Skończone

Powinieneś móc uruchomić aplikację i zobaczyć wynik na powyższej animacji.

Zobacz też


3
Kod działa dobrze, potrzebuję tylko 1 małej pomocy. W obecnym kodzie musimy przesuwać palcem od dołu do góry, aby uzyskać następną stronę, ale chcę, aby nawet małe przesunięcie również spowodowało wyświetlenie następnej strony.
AMIT

20

Mam rozwiązanie, które działa dla mnie w dwóch krokach.

  1. onInstantiateItem()z PagerAdapter, utwórz widok i obróć go o -90:

    view.setRotation(-90f)
    

    Jeśli używasz FragmentPagerAdapter, to:

    objFragment.getView().setRotation(-90)
    
  2. Obróć ViewPagerwidok o 90 stopni:

    objViewPager.setRotation(90)
    

Działa jak urok, przynajmniej na moje wymagania.


4
to rozwiązanie działa, ALE nadal musisz przesuwać w poziomie, podczas gdy viewPager teraz przewija się w pionie
leochab

@Kulai Nie jestem pewien, co tu robić, czy mógłbyś trochę wyjaśnić, zgodnie z moim rozumieniem, że mamy publiczny obiekt instantiateItem (kontener ViewGroup, pozycja int) tutaj musimy zrobić container.setRotation (-90f);
varun

1
Takie działanie działa jak urok! Mimo że widok jest przecięty w górnej i dolnej części, i nie mogę zrozumieć, dlaczego: - /
Nivaldo Bondança

2
Działa poprawnie w przypadku obrazu kwadratowego, a nie prostokątnego. Wymiary są nieprawidłowe podczas obracania prostokąta.
Kulai

2
Nie jestem pewien, kto pozytywnie zagłosował na tę odpowiedź. to nie jest VerticalViewPager, to jest ViewPager z obróceniem o 90 stopni, gest i przejście są dokładnie odwrotne. i to wcale nie jest naturalne
droidev

14

Dla kogoś, kto boryka się z tym, jak sprawić, by ViewPager działał w pionie w poziomie, wykonaj następujące czynności:

ViewPager w pionie

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

}

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

}

Jak mogę dodać ten pager z widokiem poziomym i pionowym do mojego widoku?
user1810931


Jak zaimplementować powyższe klasy pagera widoku poziomego i pionowego w moim widoku? Fragment kodu pomaga
user1810931

ViewPager jest już przeglądany, nie rozumiem, o co pytasz.
Divers

Używam biblioteki o nazwie „Material Calendar View” ( github.com/prolificinteractive/material-calendarview ) i ma już w niej poziomy viewpager i chciałem dodać do niej pionową przeglądarkę.
user1810931

8

Niewielkie rozszerzenie tej odpowiedzi pomogło mi spełnić moje wymagania (Pager z widokiem pionowym do animacji podobnej jak w Inshorts - Wiadomości w 60 słowach )

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

Mam nadzieję, że to może być pomocne dla kogoś innego.


1
Idealna wartość za MIN_SCALE?
divyenduz

Nie znalazłeś problemu z szybkim przesuwaniem w górę, przejście nie jest płynne
Praneeth

Dlaczego, u licha, ktoś miałby szybko przesuwać?
Amrut Bidri

Tylko komentarz dla wyjaśnienia: To rozwiązanie zmienia animację w taki sposób, że nowa strona nie pochodzi z dołu, ale raczej zza starej strony. Więc w zasadzie nie masz stron leżących obok siebie, ale raczej talię stron ułożonych jedna na drugiej. Tak więc to rozwiązanie może nie być tym, co próbuje zrobić pierwotne pytanie.
Benjamin Basmaci

4

Istnieje kilka projektów open source, które twierdzą, że to robią. Tutaj są:

Zostały one oznaczone przez ich autorów jako przestarzałe:

I jeszcze jedno:


3

2
Coś, o czym należy pamiętać, to fakt, że DirectionalViewPagernie był on aktualizowany od dłuższego czasu i dlatego brakuje w nim niektórych nowszych funkcji ViewPagerw bibliotece wsparcia; tj setPageMargin(int). Generalnie jednak powinien wystarczyć. A jeśli nie, pobranie kodu źródłowego ViewPageri zamiana całej logiki x / y i szerokości / wysokości nie jest zbyt trudne .
MH.

Już wziąłem pod uwagę, DirectionalViewPagerale, jak powiedział MH, nie jest aktualizowany (jego twórca uważa to za DEPRECATED) !! To niesamowite, że programiści Androida nie zapewnili społeczności pionu ViewPager, a jedynie poziom. Jestem nowicjuszem na Androidzie, rozwijanie mojej pionowej ViewPagerzamiany x / y nie jest dla mnie takie łatwe .. wolałbym już zbudowane rozwiązanie .. w każdym razie, dziękuję
user1709805

2
cześć, czy udało ci się zrobić pionową przeglądarkę? thansk
Paul

3

To tylko ulepszenie odpowiedzi z @Brett i @ Salman666, aby poprawnie przekształcić współrzędne (X, Y) na (Y, X), ponieważ wyświetlacze urządzeń są prostokątne:

...

/**
 * 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;
}

...

Jednak nadal trzeba coś zrobić, aby poprawić reakcję ekranu dotykowego. Problem może być związany z tym, co @msdark skomentował odpowiedź @ Salman666.


Mam pewne ulepszenia, zawsze wraca truew onInterceptTouchEvent, a ja również zmodyfikowała klawiszy, dzięki czemu za pomocą D-pada wywołuje przewijania z górę / w dół zamiast w lewo / w prawo: gist.github.com/zevektor/fbacf4f4f6c39fd6e409
Vektor88

@ Vektor88 zawsze zwracający true spowoduje, że wewnętrzne widoki nie będą odbierać zdarzeń dotykowych. Najlepszym rozwiązaniem jest odpowiedź
Bretta

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

1
Działa to bardzo ładnie, ale gubię zdarzenie dotykowe na elementach lub zdarzeniach left / swipe ...
matiasfha

1

Skończyło się na utworzeniu nowego DirectionalViewPager, który może przewijać w pionie lub w poziomie, ponieważ wszystkie rozwiązania, które tu widziałem, mają wady:

  1. Istniejące implementacje VerticalViewPager (autorstwa Castorflex i LambergaR)

    • Oparte są na bardzo starych wersjach bibliotek wsparcia.
  2. Sztuczka transformacji z zamianą współrzędnych

    • Overscroller jest nadal pokazywany od lewej / prawej krawędzi.
    • Wyrzucanie stron nie działa poprawnie, ponieważ nawet przy zamienionych współrzędnych VelocityTracker.computeCurrentVelocitynadal oblicza prędkość z osią X, prawdopodobnie dlatego, że wewnętrznie używa się wywołania natywnego, które ignoruje zamianę współrzędnych.
  3. Zobacz obrót

    • Hack, który wymaga rotacji widoku każdego dziecka.
    • Jeśli chcesz odczytać współrzędne czegoś innego, musisz zamienić oś.

Ale myślę, że nie może obsługiwać PageTransformera, ponieważ DirectionalViwePager nie nadpisał viewpagera
XueQing

0

Właściwie jest już jeden w źródle Androida . Zmodyfikowałem go, aby działał lepiej:

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

to nie działa poprawnie .. nawet wersja źródłowa Androida nie działa .. jest glitchy i działa tylko dla gestów w prawo / w lewo .. jeśli zrobisz w górę / w dół, nie przewija się .. przynajmniej dla mnie ..
Mohammad Zekrallah


0

Jest to pionowa, przewijalna implementacja ViewPagera, która dobrze współpracuje z RecyclerView i ListView. guochong / VerticalViewPager .

użyj ViewPager.PageTransformer, aby ViewPager przewijał się w pionie, a także zapewnij View.OnTouchListener, aby poradzić sobie z konfliktem przewijania.

/**
 * 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;
}

Witamy w StackOverflow i dziękujemy za pomoc. Możesz ulepszyć swoją odpowiedź, dodając trochę kodu.
Elias MP

0

Nawet ja miałem ten sam problem z ustawieniem ViewPagera w pionie przez zamianę X i Y. Nie było to wcale gładkie.

Dzieje się tak, ponieważ kiedyś działało tylko wtedy, gdy kąt przesuwania był mniejszy niż 7,125 stopnia = arctan (1/8) podczas przecięcia i 14 stopni = arctan (1/4) podczas dotyku; tak jak oryginalny ViewPager poziomy działa, gdy kąt przesuwania jest mniejszy niż 26,565 stopni = arctan (1/2) podczas przecięcia i 45 stopni = arctan (1) podczas dotykania.

Dlatego skopiowałem kod Android support-core-ui i przeniosłem wartości do zmiennych, które pomnożyłem za pomocą odbicia.

Kod i plik README można znaleźć pod adresem https://github.com/deepakmishra/verticalviewpager i VerticalViewPager pod adresem https://github.com/deepakmishra/verticalviewpager/blob/master/app/src/main/java/com/viewpager/VerticalViewPager .Jawa


0

lepszym sposobem jest obrócenie podglądu o 90 stopni i użycie constraintLayout do dostosowania położenia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.