Ich ändere die Folie mit dem folgenden Code:
viewPager.setCurrentItem(index++, true);
Aber es ändert sich zu schnell. Gibt es eine Möglichkeit, die Animationsgeschwindigkeit manuell einzustellen?
Ich ändere die Folie mit dem folgenden Code:
viewPager.setCurrentItem(index++, true);
Aber es ändert sich zu schnell. Gibt es eine Möglichkeit, die Animationsgeschwindigkeit manuell einzustellen?
ViewPager
die Dauer in a davon abhängen kann, wie viele Seiten Sie durchblättern).
Antworten:
Ich wollte es selbst tun und habe eine Lösung gefunden (allerdings mit Reflexion). Ich habe es noch nicht getestet, aber es sollte funktionieren oder nur minimale Änderungen erfordern. Getestet auf Galaxy Nexus JB 4.2.1. Sie müssen ein ViewPagerCustomDuration
in Ihrem XML anstelle von verwenden ViewPager
, und dann können Sie dies tun:
ViewPagerCustomDuration vp = (ViewPagerCustomDuration) findViewById(R.id.myPager);
vp.setScrollDurationFactor(2); // make the animation twice as slow
ViewPagerCustomDuration.java
::
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
import java.lang.reflect.Field;
public class ViewPagerCustomDuration extends ViewPager {
public ViewPagerCustomDuration(Context context) {
super(context);
postInitViewPager();
}
public ViewPagerCustomDuration(Context context, AttributeSet attrs) {
super(context, attrs);
postInitViewPager();
}
private ScrollerCustomDuration mScroller = null;
/**
* Override the Scroller instance with our own class so we can change the
* duration
*/
private void postInitViewPager() {
try {
Field scroller = ViewPager.class.getDeclaredField("mScroller");
scroller.setAccessible(true);
Field interpolator = ViewPager.class.getDeclaredField("sInterpolator");
interpolator.setAccessible(true);
mScroller = new ScrollerCustomDuration(getContext(),
(Interpolator) interpolator.get(null));
scroller.set(this, mScroller);
} catch (Exception e) {
}
}
/**
* Set the factor by which the duration will change
*/
public void setScrollDurationFactor(double scrollFactor) {
mScroller.setScrollDurationFactor(scrollFactor);
}
}
ScrollerCustomDuration.java
::
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.animation.Interpolator;
import android.widget.Scroller;
public class ScrollerCustomDuration extends Scroller {
private double mScrollFactor = 1;
public ScrollerCustomDuration(Context context) {
super(context);
}
public ScrollerCustomDuration(Context context, Interpolator interpolator) {
super(context, interpolator);
}
@SuppressLint("NewApi")
public ScrollerCustomDuration(Context context, Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}
/**
* Set the factor by which the duration will change
*/
public void setScrollDurationFactor(double scrollFactor) {
mScrollFactor = scrollFactor;
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, (int) (duration * mScrollFactor));
}
}
Hoffe das hilft jemandem!
ViewPager
.
IllegalArgumentException
.
Ich habe eine bessere Lösung gefunden, basierend auf der Antwort von @ df778899 und der Android ValueAnimator-API . Es funktioniert gut ohne Reflexion und ist sehr flexibel. Es ist auch nicht erforderlich, einen benutzerdefinierten ViewPager zu erstellen und in das Paket android.support.v4.view einzufügen. Hier ist ein Beispiel:
private void animatePagerTransition(final boolean forward) {
ValueAnimator animator = ValueAnimator.ofInt(0, viewPager.getWidth());
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
viewPager.endFakeDrag();
}
@Override
public void onAnimationCancel(Animator animation) {
viewPager.endFakeDrag();
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.setInterpolator(new AccelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private int oldDragPosition = 0;
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int dragPosition = (Integer) animation.getAnimatedValue();
int dragOffset = dragPosition - oldDragPosition;
oldDragPosition = dragPosition;
viewPager.fakeDragBy(dragOffset * (forward ? -1 : 1));
}
});
animator.setDuration(AppConstants.PAGER_TRANSITION_DURATION_MS);
if (viewPager.beginFakeDrag()) {
animator.start();
}
}
Nur überprüft, ob diese Lösung verwendet werden kann, um mehrere Seiten gleichzeitig zu wischen (zum Beispiel, wenn die erste Seite nach der letzten angezeigt werden soll). Dies ist ein leicht modifizierter Code, um die angegebene Seitenzahl zu verarbeiten:
private int oldDragPosition = 0;
private void animatePagerTransition(final boolean forward, int pageCount) {
// if previous animation have not finished we can get exception
if (pagerAnimation != null) {
pagerAnimation.cancel();
}
pagerAnimation = getPagerTransitionAnimation(forward, pageCount);
if (viewPager.beginFakeDrag()) { // checking that started drag correctly
pagerAnimation.start();
}
}
private Animator getPagerTransitionAnimation(final boolean forward, int pageCount) {
ValueAnimator animator = ValueAnimator.ofInt(0, viewPager.getWidth() - 1);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
viewPager.endFakeDrag();
}
@Override
public void onAnimationCancel(Animator animation) {
viewPager.endFakeDrag();
}
@Override
public void onAnimationRepeat(Animator animation) {
viewPager.endFakeDrag();
oldDragPosition = 0;
viewPager.beginFakeDrag();
}
});
animator.setInterpolator(new AccelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int dragPosition = (Integer) animation.getAnimatedValue();
int dragOffset = dragPosition - oldDragPosition;
oldDragPosition = dragPosition;
viewPager.fakeDragBy(dragOffset * (forward ? -1 : 1));
}
});
animator.setDuration(AppConstants.PAGER_TRANSITION_DURATION_MS / pageCount); // remove divider if you want to make each transition have the same speed as single page transition
animator.setRepeatCount(pageCount);
return animator;
}
public class PresentationViewPager extends ViewPager {
public static final int DEFAULT_SCROLL_DURATION = 250;
public static final int PRESENTATION_MODE_SCROLL_DURATION = 1000;
public PresentationViewPager (Context context) {
super(context);
}
public PresentationViewPager (Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setDurationScroll(int millis) {
try {
Class<?> viewpager = ViewPager.class;
Field scroller = viewpager.getDeclaredField("mScroller");
scroller.setAccessible(true);
scroller.set(this, new OwnScroller(getContext(), millis));
} catch (Exception e) {
e.printStackTrace();
}
}
public class OwnScroller extends Scroller {
private int durationScrollMillis = 1;
public OwnScroller(Context context, int durationScroll) {
super(context, new DecelerateInterpolator());
this.durationScrollMillis = durationScroll;
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, durationScrollMillis);
}
}
}
Eine bessere Lösung besteht darin, einfach auf die privaten Felder zuzugreifen, indem Sie die Klasse im Support Package erstellen. BEARBEITEN Dies ist an die MAX_SETTLE_DURATION
von der ViewPager
Klasse festgelegten 600 ms gebunden .
package android.support.v4.view;
import android.content.Context;
import android.util.AttributeSet;
public class SlowViewPager extends ViewPager {
// The speed of the scroll used by setCurrentItem()
private static final int VELOCITY = 200;
public SlowViewPager(Context context) {
super(context);
}
public SlowViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
setCurrentItemInternal(item, smoothScroll, always, VELOCITY);
}
}
Sie können dann natürlich ein benutzerdefiniertes Attribut hinzufügen, damit dies über XML festgelegt werden kann.
Hier ist mein Code, der in Librera Reader verwendet wird
public class MyViewPager extends ViewPager {
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initMyScroller();
}
private void initMyScroller() {
try {
Class<?> viewpager = ViewPager.class;
Field scroller = viewpager.getDeclaredField("mScroller");
scroller.setAccessible(true);
scroller.set(this, new MyScroller(getContext())); // my liner scroller
Field mFlingDistance = viewpager.getDeclaredField("mFlingDistance");
mFlingDistance.setAccessible(true);
mFlingDistance.set(this, Dips.DP_10);//10 dip
Field mMinimumVelocity = viewpager.getDeclaredField("mMinimumVelocity");
mMinimumVelocity.setAccessible(true);
mMinimumVelocity.set(this, 0); //0 velocity
} catch (Exception e) {
LOG.e(e);
}
}
public class MyScroller extends Scroller {
public MyScroller(Context context) {
super(context, new LinearInterpolator()); // my LinearInterpolator
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, 175);//175 duration
}
}
}
Ich habe die Version von Cicero Moura verwendet, um eine Kotlin-Klasse zu erstellen, die ab Android 10 immer noch perfekt funktioniert.
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.viewpager.widget.ViewPager
class CustomViewPager(context: Context, attrs: AttributeSet) :
ViewPager(context, attrs) {
private companion object {
const val DEFAULT_SPEED = 1000
}
init {
setScrollerSpeed(DEFAULT_SPEED)
}
var scrollDuration = DEFAULT_SPEED
set(millis) {
setScrollerSpeed(millis)
}
private fun setScrollerSpeed(millis: Int) {
try {
ViewPager::class.java.getDeclaredField("mScroller")
.apply {
isAccessible = true
set(this@CustomViewPager, OwnScroller(millis))
}
} catch (e: Exception) {
e.printStackTrace()
}
}
inner class OwnScroller(private val durationScrollMillis: Int) : Scroller(context, AccelerateDecelerateInterpolator()) {
override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, duration: Int) {
super.startScroll(startX, startY, dx, dy, durationScrollMillis)
}
}
}
Initialisierung aus der Aktivitätsklasse:
viewPager.apply {
scrollDuration = 2000
adapter = pagerAdapter
}
Nachdem ich meinen ganzen Tag verschwendet hatte, fand ich eine Lösung, die offscreenPageLimit auf total no setzte. der Seite.
SetOffScreenLimit (page.length) speichert alle Ansichten im Speicher, um eine konstante Länge der ViewPager-Bildläufe zu gewährleisten. Dies stellt jedoch ein Problem für alle Animationen dar, bei denen die Funktion View.requestLayout aufgerufen wird (z. B. für Animationen, bei denen Änderungen am Rand oder an den Grenzen vorgenommen werden). Es macht sie sehr langsam (gemäß Romain Guy), weil alle Ansichten, die sich im Speicher befinden, ebenfalls ungültig werden. Deshalb habe ich verschiedene Methoden ausprobiert, um die Dinge reibungslos zu gestalten, aber das Überschreiben von requestLayout und anderen ungültigen Methoden verursacht viele andere Probleme.
Ein guter Kompromiss besteht darin, das Off-Screen-Limit dynamisch so zu ändern, dass die meisten Bildläufe zwischen den Seiten sehr flüssig sind, während sichergestellt wird, dass alle In-Page-Animationen reibungslos sind, indem die Ansichten beim Benutzer entfernt werden. Dies funktioniert sehr gut, wenn Sie nur 1 oder 2 Ansichten haben, die andere Ansichten aus dem Speicher entfernen müssen.
*** Verwenden Sie diese Option, wenn keine Lösung funktioniert, da durch Festlegen des Offset-Limits alle Fragmente gleichzeitig geladen werden