studio detector android android-viewpager gestures onfling

detector - Android: desplazamiento de ViewPager basado en la velocidad



gesture android studio (5)

La forma en que se desplaza el ViewPager ahora es por un elemento por gesto. Se trata del gesto de lanzamiento de la misma manera, sin importar si se trata de un lanzamiento rápido a pantalla completa o un arrastre lento; en la página final avanza solo un paso.

¿Hay proyectos o ejemplos que podrían agregar un lanzamiento basado en la velocidad que desplaza múltiples elementos según la velocidad del lanzamiento existente (si aún está en progreso) y avanza más si el gesto de lanzamiento es amplio y rápido?

¿Y si no hay por dónde empezar con algo como esto?

PD Se ofrece la recompensa. Por favor, no hay respuestas con referencias a la Galería o HorizontalScrollView



Otra opción es copiar un código fuente completo de implementación de ViewPager desde la biblioteca de soporte y personalizar un método determineTargetPage(...) . Es responsable de determinar a qué página desplazarse en el gesto de lanzamiento. Este enfoque no es muy conveniente, pero funciona bastante bien. Ver código de implementación a continuación:

private int determineTargetPage(int curPage, float pageOffset, int velocity, int dx) { int target; if (Math.abs(dx) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) { target = calculateFinalPage(curPage, velocity); } else { final float truncator = curPage >= mCurItem ? 0.4f : 0.6f; target = (int) (curPage + pageOffset + truncator); } if (mItems.size() > 0) { final ItemInfo first = mItems.get(0); final ItemInfo last = mItems.get(mItems.size() - 1); // Only let the user target pages we have items for target = Math.max(first.position, Math.min(target, last.position)); } return target; } private int calculateFinalPage(int curPage, int velocity) { float distance = Math.abs(velocity) * MAX_SETTLE_DURATION / 1000f; float normalDistance = (float) Math.sqrt(distance / 2) * 25; int step = (int) - Math.signum(velocity); int width = getClientWidth(); int page = curPage; for (int i = curPage; i >= 0 && i < mAdapter.getCount(); i += step) { float pageWidth = mAdapter.getPageWidth(i); float remainingDistance = normalDistance - pageWidth * width; if (remainingDistance >= 0) { normalDistance = remainingDistance; } else { page = i; break; } } return page; }


Puede anular la clase ScrollView u HorizontalScrollView y agregar ese comportamiento. Hay muchos errores en la Galería, y recuerdo que está en desuso desde el nivel 14 de la API.


ViewPager es una clase de la biblioteca de soporte. Descargue el código fuente de la biblioteca de asistencia y cambie aproximadamente 10 líneas de código en el método onTouchEvent para agregar la función deseada.

Utilizo la biblioteca de soporte modificada en mis proyectos durante aproximadamente un año, porque a veces necesito modificar varias líneas de código para hacer un pequeño cambio o agregar un nuevo método y no quiero copiar el código fuente de los componentes. Yo uso la versión modificada de fragmentos y viewpager.

Pero hay un problema que obtendrás: una vez en aproximadamente 6 meses tienes que combinar la biblioteca de soporte personalizada con la nueva versión oficial si necesitas nuevas funciones. Y tenga cuidado con los cambios, no querrá interrumpir la compatibilidad con las clases de biblioteca.


La técnica aquí es extender ViewPager e imitar la mayor parte de lo que el localizador hará internamente, junto con la lógica de desplazamiento desde el widget de la Gallery . La idea general es monitorear el lanzamiento (y la velocidad y los pergaminos que lo acompañan) y luego enviarlos como eventos de arrastre falsos al ViewPager subyacente. Sin embargo, si lo hace solo, no funcionará (aún obtendrá un solo desplazamiento de página). Esto sucede porque el arrastre falso implementa límites en los límites en que el desplazamiento será efectivo. Puede imitar los cálculos en el ViewPager extendido y detectar cuándo sucederá esto, luego simplemente pase la página y continúe como siempre. El beneficio de usar un arrastre falso significa que no tiene que lidiar con el ajuste a las páginas o el manejo de los bordes del ViewPager .

Probé el siguiente código en el ejemplo de demostraciones de animación, descargable desde http://developer.android.com/training/animation/screen-slide.html al reemplazar ViewPager en ScreenSlideActivity con este VelocityViewPager (tanto en el diseño activity_screen_slide como en el campo dentro de la actividad).

/* * Copyright 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: Dororo @ * An extended ViewPager which implements multiple page flinging. * */ package com.example.android.animationsdemo; import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.GestureDetector; import android.widget.Scroller; public class VelocityViewPager extends ViewPager implements GestureDetector.OnGestureListener { private GestureDetector mGestureDetector; private FlingRunnable mFlingRunnable = new FlingRunnable(); private boolean mScrolling = false; public VelocityViewPager(Context context) { super(context); } public VelocityViewPager(Context context, AttributeSet attrs) { super(context, attrs); mGestureDetector = new GestureDetector(context, this); } // We have to intercept this touch event else fakeDrag functions won''t work as it will // be in a real drag when we want to initialise the fake drag. @Override public boolean onInterceptTouchEvent(MotionEvent event) { return true; } @Override public boolean onTouchEvent(MotionEvent event) { // give all the events to the gesture detector. I''m returning true here so the viewpager doesn''t // get any events at all, I''m sure you could adjust this to make that not true. mGestureDetector.onTouchEvent(event); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) { mFlingRunnable.startUsingVelocity((int)velX); return false; } private void trackMotion(float distX) { // The following mimics the underlying calculations in ViewPager float scrollX = getScrollX() - distX; final int width = getWidth(); final int widthWithMargin = width + this.getPageMargin(); final float leftBound = Math.max(0, (this.getCurrentItem() - 1) * widthWithMargin); final float rightBound = Math.min(this.getCurrentItem() + 1, this.getAdapter().getCount() - 1) * widthWithMargin; if (scrollX < leftBound) { scrollX = leftBound; // Now we know that we''ve hit the bound, flip the page if (this.getCurrentItem() > 0) { this.setCurrentItem(this.getCurrentItem() - 1, false); } } else if (scrollX > rightBound) { scrollX = rightBound; // Now we know that we''ve hit the bound, flip the page if (this.getCurrentItem() < (this.getAdapter().getCount() - 1) ) { this.setCurrentItem(this.getCurrentItem() + 1, false); } } // Do the fake dragging if (mScrolling) { this.fakeDragBy(distX); } else { this.beginFakeDrag(); this.fakeDragBy(distX); mScrolling = true; } } private void endFlingMotion() { mScrolling = false; this.endFakeDrag(); } // The fling runnable which moves the view pager and tracks decay private class FlingRunnable implements Runnable { private Scroller mScroller; // use this to store the points which will be used to create the scroll private int mLastFlingX; private FlingRunnable() { mScroller = new Scroller(getContext()); } public void startUsingVelocity(int initialVel) { if (initialVel == 0) { // there is no velocity to fling! return; } removeCallbacks(this); // stop pending flings int initialX = initialVel < 0 ? Integer.MAX_VALUE : 0; mLastFlingX = initialX; // setup the scroller to calulate the new x positions based on the initial velocity. Impose no cap on the min/max x values. mScroller.fling(initialX, 0, initialVel, 0, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE); post(this); } private void endFling() { mScroller.forceFinished(true); endFlingMotion(); } @Override public void run() { final Scroller scroller = mScroller; boolean animationNotFinished = scroller.computeScrollOffset(); final int x = scroller.getCurrX(); int delta = x - mLastFlingX; trackMotion(delta); if (animationNotFinished) { mLastFlingX = x; post(this); } else { endFling(); } } } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distX, float distY) { trackMotion(-distX); return false; } // Unused Gesture Detector functions below @Override public boolean onDown(MotionEvent event) { return false; } @Override public void onLongPress(MotionEvent event) { // we don''t want to do anything on a long press, though you should probably feed this to the page being long-pressed. } @Override public void onShowPress(MotionEvent event) { // we don''t want to show any visual feedback } @Override public boolean onSingleTapUp(MotionEvent event) { // we don''t want to snap to the next page on a tap so ignore this return false; } }

Hay algunos problemas menores con esto, que pueden resolverse fácilmente, pero los dejaré a usted, a saber, cosas como si se desplaza (arrastrando, no lanzando) puede terminar a mitad de camino entre las páginas (querrá ajustarse) el evento ACTION_UP). Además, los eventos táctiles se están anulando por completo para hacer esto, por lo que deberá ViewPager los eventos relevantes al ViewPager subyacente cuando sea apropiado.