Android: Vertical ViewPager

¿Hay alguna manera de hacer una ViewPager que no se desplace horizontalmente, sino verticalmente?

Puede usar ViewPager.PageTransformer para crear la ilusión de una ViewPager vertical. Para lograr desplazarse con un arrastre vertical en lugar de horizontal, deberá anular los eventos táctiles predeterminados de MotionEvent e intercambiar las coordenadas de MotionEvent antes de manipularlas, por ejemplo:

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

Por supuesto, puede ajustar estas configuraciones como mejor le parezca. Termina luciendo así:

Demo VerticalViewPager

Tengo una solución que funciona para mí en dos pasos.

  1. onInstantiateItem() de PagerAdapter , cree la vista y gírela por -90:

     view.setrotation(-90f) 

    Si está utilizando FragmentPagerAdapter , entonces:

     objFragment.getView().setRoration(-90) 
  2. Gire la vista de ViewPager 90 grados:

     objViewPager.setRotation(90) 

Funciona como un encanto, al menos para mi requerimiento.

Implementación de paginador de vista vertical por Antoine Merle:

https://github.com/castorflex/VerticalViewPager

Para alguien que está luchando con la forma de hacer que ViewPager vertical funcione en horizontal, solo haga lo siguiente:

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

ViewPager horizontal

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

Una pequeña extensión de esta respuesta me ayudó a cumplir con mis requisitos (Vertical View Pager para animar similares como en Inshorts – Noticias en 60 palabras )

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

Espero que esto pueda ser útil para otra persona.

Hay una nueva implementación no oficial de código abierto de un ViewPager vertical:

Anuncio: https://plus.google.com/107777704046743444096/posts/1FTJfrvnY8w

Código: https://github.com/LambergaR/VerticalViewPager/

Vertical ViewPager

Las otras respuestas aquí parecían tener algunos problemas con ellas. Incluso los proyectos de código abierto recomendados no fusionaron solicitudes de extracción recientes con correcciones de errores. Luego encontré un VerticalViewPager de Google que usaban alguna aplicación DeskClock. Confío en utilizar la implementación de Google mucho más que las otras respuestas que flotan por aquí. (La versión de Google es similar a la respuesta actual más votadas, pero más completa).

Ejemplo completo

Este ejemplo es casi exactamente el mismo que mi Ejemplo básico de ViewPager , con algunos cambios menores para hacer uso de la clase VerticalViewPager .

enter image description here

XML

Agregue los diseños xml para la actividad principal y para cada página (fragmento). Tenga en cuenta que usamos VerticalViewPager lugar de ViewPager estándar. Incluiré el código para eso a continuación.

activity_main.xml

     

fragment_one.xml

     

Código

El código para VerticalViewPager no es mío. Es solo una versión reducida (sin comentarios) del código fuente de Google. Eche un vistazo a la versión más actualizada. Los comentarios allí también son útiles para comprender lo que está sucediendo.

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

Solo ViewPager VerticalViewPager para 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; } } } 

Terminado

Debería poder ejecutar la aplicación y ver el resultado en la animación de arriba.

Ver también

  • Ejemplo básico de ViewPager
  • ViewPager con FragmentPagerAdapter

Mira esto: https://github.com/JakeWharton/Android-DirectionalViewPager

O la siguiente pregunta puede ayudarlo: Vertical ‘Vista de cuadrícula con páginas’ o ‘Viewpager’

Solo una mejora en las respuestas de @Brett y @Salman666 para transformar correctamente las coordenadas (X, Y) en (Y, X) ya que las pantallas del dispositivo son rectangulares:

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

Sin embargo, todavía queda algo por hacer para mejorar la capacidad de respuesta de la pantalla táctil. El problema podría estar relacionado con lo que @msdark comentó sobre la respuesta de @Salman666.

Hay algunos proyectos de código abierto que dicen hacer esto. Aquí están:

  • kaelaela / VerticalViewPager , último compromiso en octubre de 2015 y un colaborador
  • castorflex / VerticalViewPager , último compromiso en febrero de 2014 y tres colaboradores
  • LambergaR / VerticalViewPager / , último compromiso en agosto de 2013 y un colaborador

Estos han sido marcados como obsoletos por sus autores:

Y una cosa más:

  • También hay un nombre de archivo VerticalViewPager.java en la fuente de Android desde la aplicación deskclock , pero no parece ser oficialmente compatible ya que no puedo encontrar documentación para él.
 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); } } 

De hecho, ya hay uno en la fuente de Android . Lo he modificado para que funcione mejor:

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

Un repository actualmente más activo es lsjwzh / RecyclerViewPager .

  • último compromiso 12 de agosto de 2016, 16 colaboradores, 1840 estrellas
  • basado en RecyclerView

Esta es una implementación de ViewPager verticalmente desplazable. Funciona bien con RecyclerView y ListView. guochong / VerticalViewPager .

utilice ViewPager.PageTransformer para hacer que ViewPager se desplace verticalmente, y también proporcione un View.OnTouchListener para tratar el conflicto de desplazamiento.

 /** * 1.dispatch ACTION_DOWN,ACTION_UP,ACTION_CANCEL to child
* 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; }

Incluso yo estaba enfrentando el mismo problema al hacer que ViewPager se volviera vertical al intercambiar X e Y. No fue nada sencillo.

Esto se debe a que solía funcionar solo cuando el ángulo de deslizamiento era menor que 7.125 grados = arctan (1/8) mientras se interceptaba y 14 grados = arctan (1/4) mientras tocaba; como el ViewPager horizontal uno original funciona cuando el ángulo de deslizamiento es menor que 26.565 grados = arctan (1/2) mientras se intercepta y 45 grados = arctan (1) mientras toca.

Es por eso que copié el código de Android support-core-ui y moví los valores a las variables, que multipliqué utilizando el reflection.

Encuentre el código y README en https://github.com/deepakmishra/verticalviewpager y VerticalViewPager en https://github.com/deepakmishra/verticalviewpager/blob/master/app/src/main/java/com/viewpager/VerticalViewPager .Java

la mejor manera es rotar el visor 90 grados, y usar constraintLayout para ajustar la ubicación.