Добрый день,
Недавно мне понадобилось сделать банерную галерейку с анимацией, проблема была с временем анимации и самой анимацией в 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