ViewPager управляем временем анимации переходов

в 10:38, , рубрики: android, Разработка под android, метки:

Добрый день,

Недавно мне понадобилось сделать банерную галерейку с анимацией, проблема была с временем анимации и самой анимацией в ViewPager Переход был слишком быстрый, и если переходить с 1 элемента на 5 то не увидишь анимации 3-4 элементов

Приступим

Давайте попробуем разобраться в чем причина такого поведения ViewPager

void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
        if (mAdapter == null || mAdapter.getCount() <= 0) {
            setScrollingCacheEnabled(false);
            return;
        }
        if (!always && mCurItem == item && mItems.size() != 0) {
            setScrollingCacheEnabled(false);
            return;
        }

        if (item < 0) {
            item = 0;
        } else if (item >= mAdapter.getCount()) {
            item = mAdapter.getCount() - 1;
        }
        final int pageLimit = mOffscreenPageLimit; // Из-за данной строчки во время анимации мы не видим n-ые элементы

        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
            for (int i=0; i<mItems.size(); i++) {
                mItems.get(i).scrolling = true;
            }
        }
        final boolean dispatchSelected = mCurItem != item;

        if (mFirstLayout) {
            mCurItem = item;
            if (dispatchSelected && mOnPageChangeListener != null) {
                mOnPageChangeListener.onPageSelected(item);
            }
            if (dispatchSelected && mInternalPageChangeListener != null) {
                mInternalPageChangeListener.onPageSelected(item);
            }
            requestLayout();
        } else {
            populate(item);
            scrollToItem(item, smoothScroll, velocity, dispatchSelected); // Здесь происходит анимация до y позиции и тут же высчитывается время анимации
        }
    }

Мы разобрали причину почему мы невидим анимации некоторых элементов в ViewPager теперь посмотри как же устроена функция скроллинга до определенного элемента.

   // зайдем в SmoothScrolltTo и посмотрим как высчитывается время анимации

        int duration = 0; //время анимации
        velocity = Math.abs(velocity); // скорость
        if (velocity > 0) {  
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
        } else {
            final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
            final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);
            duration = (int) ((pageDelta + 1) * 100);
        }
        duration = Math.min(duration, MAX_SETTLE_DURATION);

        mScroller.startScroll(sx, sy, dx, dy, duration);

Наверное здесь не нужно много объяснений, все предельно просто. Теперь приступим к добавлю нашего метода для скроллинга с произвольным временем за основу возьмем метод setCurrentItemInternal и smoothScrollTo итак преступим

     public void setCurrentItem( int item, int duration){
        if (mAdapter == null || mAdapter.getCount() <= 0) {
            setScrollingCacheEnabled(false);
            return;
        }

        int oldCurrentPos = getCurrentItem(); // запоминаем позицию

        if(oldCurrentPos == item){ // если нынешняя позиция равна элементу к которому нужно прокрутить возвращаемся
            return;
        }else{ // иначе выставляем отображение под элементов равное прокручиваемым элементам
            setOffscreenPageLimit(Math.abs(oldCurrentPos - item));
        }

        if (item < 0) {
            item = 0;
        } else if (item >= mAdapter.getCount()) {
            item = mAdapter.getCount() - 1;
        }

        final int pageLimit = mOffscreenPageLimit;
        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
            // We are doing a jump by more than one page.  To avoid
            // glitches, we want to keep all current pages in the view
            // until the scroll ends.
            for (int i=0; i<mItems.size(); i++) {
                mItems.get(i).scrolling = true;
            }
        }
        final boolean dispatchSelected = mCurItem != item;

        if (mFirstLayout) {
            // We don't have any idea how big we are yet and shouldn't have any pages either.
            // Just set things up and let the pending layout handle things.
            mCurItem = item;
            if (dispatchSelected && mOnPageChangeListener != null) {
                mOnPageChangeListener.onPageSelected(item);
            }
            if (dispatchSelected && mInternalPageChangeListener != null) {
                mInternalPageChangeListener.onPageSelected(item);
            }
            requestLayout();
        } else {
            populate(item);

            final ItemInfo curItem = infoForPosition(item);
            int destX = 0;
            if(curItem != null){
                final int width = getClientWidth();
                destX = (int) (width * Math.max(mFirstOffset, Math.min(curItem.offset, mLastOffset)));
            }

            if(duration > 0){

                int velocity = duration / Math.abs(oldCurrentPos - item);

                smoothScrollToWithDuration(destX, 0,  duration); // скролим
                if(mOnPageChangeListener != null && curItem.position != item){
                    mOnPageChangeListener.onPageSelected(item);
                }

                if (curItem.position != item && mInternalPageChangeListener != null) {
                    mInternalPageChangeListener.onPageSelected(item);
                }

            }
        }
    }


    void smoothScrollToWithDuration(int x, int y, int duration){

            if (getChildCount() == 0) {
                // Nothing to do.
                setScrollingCacheEnabled(false);
                return;
            }
            int sx = getScrollX();
            int sy = getScrollY();
            int dx = x - sx;
            int dy = y - sy;
            if (dx == 0 && dy == 0) {
                completeScroll(true);
                populate();
                setScrollState(SCROLL_STATE_IDLE);
                return;
            }

            setScrollingCacheEnabled(true);
            setScrollState(SCROLL_STATE_SETTLING);

            final int width = getClientWidth();
            final int halfWidth = width / 2;
            final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);

            mScroller.startScroll(sx, sy, dx, dy, duration); // скролим до определенной страницы с произвольным временем
            if(mScroller.isFinished()){
                setOffscreenPageLimit(DEFAULT_OFFSCREEN_PAGES); // восстанавливаем значение подэлементов
            }

            ViewCompat.postInvalidateOnAnimation(this);

    }

Собственно все

Хочу заметить что описание не слишком подробное но хочу улучшить, по возможности пишите что лучше объяснить

Автор: 03uk

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js