Вопрос по android, android-viewpager – Изменить продолжительность анимации ViewPager при программном скольжении

65

Я меняю слайд с

viewPager.setCurrentItem(index++, true);

Но это быстро меняется. Есть ли способ установить скорость анимации вручную?

Искал и нашел только кого-то, спрашивающего везде то же самое (кроме StackOverflow, может быть, мне повезло здесь :)), без ответов.

Подобно:

https://groups.google.com/group/android-developers/browse_thread/thread/a13405c0b5833893?fwc=1

Заранее спасибо.

На самом деле это очень похоже на мое решение, хотя я использовал коэффициент вместо абсолютной продолжительности (потому что вViewPager продолжительность может зависеть от того, сколько страниц вы прокручиваете). Oleg Vaskevich
Не уверен, как выглядел ваш поиск, но взгляните наthis answer здесь на ТАК. Это должно быть именно то, что вы после. MH.

Ваш Ответ

6   ответов
30

Я нашел лучшее решение, основанное наответ @ df778899 и Android ValueAnimator API, Он отлично работает без отражения и очень гибкий. Также нет необходимости создавать собственный ViewPager и помещать его в пакет android.support.v4.view. Вот пример:

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();
    }
}
UPDATE:

Только что проверил, можно ли использовать это решение для одновременного считывания нескольких страниц (например, следует ли показывать первую страницу после последней). Это немного измененный код для обработки указанного количества страниц:

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;
}
Я не понимаю. Можете ли вы изменить код, чтобы показать, как это сделать? Может быть, вместо «вперед» есть целое число, которое говорит, сколько страниц идти (и, если отрицательно, идти назад)?
так это фальшивое перетаскивание? кажется разумным способом добиться этого, но что произойдет, если пользователь начнет перетаскивать во время этой операции? Кроме того, что, если я хочу, чтобы он прокрутился до первой страницы (потому что, когда он достигает последней страницы, я бы хотел, чтобы он снова достиг первой страницы)?
@androiddeveloper, я использовал это решение с NonSwipeableViewPager (stackoverflow.com/a/9650884/2923816). Поэтому у меня нет проблем с взаимодействием с пользователем во время фальсификации. Но если кому-то нужно сохранить жест смахивания, это может быть достигнуто путем переопределения ViewPager и управления анимацией, когда пользователь касается ViewPager. Я не пробовал, но я полагаю, что это возможно и должно работать нормально. Для зацикленного пейджера вы должны настроить анимационный код, но эти настройки зависят от конкретной реализации зацикливания.
@androiddeveloper да, вы можете использовать повторяющиеся свойства. В случае, если вы хотите вернуться к первой странице в то же время, что и обычный переход страницы, вам также следует изменить длительность перехода для этой возвращаемой анимации.
Что касается взаимодействия с пользователем, хорошо (поскольку анимация не такая медленная, я не против). Насчет прокрутки на первую страницу я не понимаю. Предположим, что есть только 3 страницы, и я на последней, как я могу сделать так, чтобы она прокручивалась до первой страницы, используя настроенную продолжительность, которую вы установили? поможет ли повторяющееся свойство аниматора?
0

я нашел решение с установленным offscreenPageLimit на общее количество нет. страницы.

Чтобы обеспечить плавную прокрутку ViewPager с постоянной длиной, setOffScreenLimit (page.length) сохранит все представления в памяти. Однако это создает проблему для любой анимации, которая включает в себя вызов функции View.requestLayout (например, для любой анимации, которая включает в себя внесение изменений в поля или границы). Это делает их действительно медленными (согласно Romain Guy), потому что все представления, которые находятся в памяти, будут также признаны недействительными. Поэтому я попробовал несколько разных способов сделать все гладко, но переопределение requestLayout и других недействительных методов вызовет много других проблем.

Хорошим компромиссом является динамическое изменение ограничения за пределами экрана, чтобы большая часть прокрутки между страницами была очень плавной, при этом следя за тем, чтобы все анимации на странице сглаживались, удаляя представления, когда пользователь. Это работает очень хорошо, когда у вас есть только 1 или 2 представления, которые должны сделать другие представления не по памяти.

*** Используйте это, когда никакое решение не работает, потому что, установив предел смещения, вы загрузите все фрагменты одновременно

7

создав класс в пакете поддержки.EDIT Это связано сMAX_SETTLE_DURATION 600 мс, установленныйViewPagerучебный класс.

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

}

Вы можете, конечно, затем добавить пользовательский атрибут, чтобы он мог быть установлен через XML.

setCurrentItemInternal не является частным методом?
@MartyMiller Да, но если библиотека поддержки является частью вашего проекта, вы можете создать класс в его пакете.
@HaggleLad Да, к сожалению, мой подход связан с максимальной продолжительностью анимации 600 мс. Если вам нужно, чтобы он был длиннее, вам нужно использовать другой метод. Я обновлю свой ответ, чтобы отметить это.
Спасибо, похоже, это работает, однако даже при значении VELOCITY, равном 1, прокрутка все еще довольно быстрая, ИМХО, хотелось бы еще больше замедлить ее.
библиотека поддержки редактирования недопустима, если я решил обновить ее, этот код не будет работать, а также в моем личном случае у меня нет исходного кода библиотеки поддержки (и я не буду редактировать его, если он у меня есть)
0

Librera Reader

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

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

однако).I haven't tested it yet but it should work or need minimal modification. Протестировано на Galaxy Nexus JB 4.2.1. Вам нужно использоватьViewPagerCustomDuration в вашем XML вместоViewPagerи тогда вы можете сделать это:

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

}

Надеюсь, это поможет кому-то!

также, как можно использовать var ViewPagerCustomDuration .mScroller? похоже, ни с чем не связано ...
большое спасибо за то, что уделили мне время на тестирование =) Я собираюсь создать отдельный вопрос с подробной информацией о проблемах, которые у меня есть, чтобы вы могли поближе взглянуть и получить «вознаграждение» за ваш ответ. Я опубликую ссылку здесь, как только она будет готова.
Хм, я проверил это, чтобы работать на меня; Я не совсем уверен, почему вы получаетеIllegalArgumentException.
Ваше решение - самое лучшее, что я нашел до сих пор, но когда строка "scroller.set (this, mScroller)"; " выполнено, я получил ошибку "IllegalArgumentException: недопустимое значение для поля". Я думаю, это из-за использования подкласса ViewPager ... какое-либо исправление?
Я немного разбираюсь в этом. Это должно быть возможно. Также mScroller просто используется внутриViewPager.

Похожие вопросы