Files
EhViewer/app/src/main/java/com/hippo/glgallery/ScrollLayoutManager.java
T
tarsin 0ced054631 Drop edgeEffect
* This md2 but not md3

* We have no reason to reinvent the wheel
2022-06-23 08:16:40 +08:00

1135 lines
36 KiB
Java

/*
* Copyright 2016 Hippo Seven
*
* 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.
*/
package com.hippo.glgallery;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import androidx.annotation.NonNull;
import com.hippo.glview.anim.Animation;
import com.hippo.glview.view.GLView;
import com.hippo.glview.widget.GLProgressView;
import com.hippo.glview.widget.GLTextureView;
import com.hippo.yorozuya.AnimationUtils;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.MathUtils;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
class ScrollLayoutManager extends GalleryView.LayoutManager {
private static final String TAG = ScrollLayoutManager.class.getSimpleName();
private static final float RESERVATION = 1f;
private static final float MAX_SCALE = 2.0f;
private static final float MIN_SCALE = 1.0f;
private static final float SCALE_ERROR = 0.01f;
private static final int INVALID_TOP = Integer.MAX_VALUE;
private final LinkedList<GalleryPageView> mPages = new LinkedList<>();
private final LinkedList<GalleryPageView> mTempPages = new LinkedList<>();
private final PageFling mPageFling;
private final SmoothScaler mSmoothScaler;
private GalleryView.Adapter mAdapter;
private GLProgressView mProgress;
private String mErrorStr;
private GLTextureView mErrorView;
private float mScale = 0.5f;
private int mOffsetX;
private int mOffsetY;
private int mDeltaX;
private int mDeltaY;
private int mKeepTopPageIndex = GalleryPageView.INVALID_INDEX;
private int mKeepTop = INVALID_TOP;
private int mFirstShownPageIndex = GalleryPageView.INVALID_INDEX;
private boolean mScrollUp;
private boolean mFlingUp;
private boolean mStopAnimationFinger;
private int mInterval;
// Current index
private int mIndex;
private int mBottomStateBottom;
private boolean mBottomStateHasNext;
public ScrollLayoutManager(Context context, @NonNull GalleryView galleryView, int interval) {
super(galleryView);
mInterval = interval;
mPageFling = new PageFling(context);
mSmoothScaler = new SmoothScaler();
}
public void setInterval(int interval) {
if (mInterval == interval) {
return;
}
if (mAdapter != null) {
int index = getInternalCurrentIndex();
GalleryView.Adapter adapter = onDetach();
mInterval = interval;
onAttach(adapter);
setCurrentIndex(index);
mGalleryView.requestFill();
} else {
mInterval = interval;
}
}
private void resetParameters() {
mScale = 1.0f;
mOffsetX = 0;
mOffsetY = 0;
mDeltaX = 0;
mDeltaY = 0;
mKeepTopPageIndex = GalleryPageView.INVALID_INDEX;
mKeepTop = INVALID_TOP;
mFirstShownPageIndex = GalleryPageView.INVALID_INDEX;
mScrollUp = false;
mFlingUp = false;
mStopAnimationFinger = false;
}
// Return true for animations are running
private boolean cancelAllAnimations() {
boolean running = mPageFling.isRunning() ||
mSmoothScaler.isRunning();
mPageFling.cancel();
mSmoothScaler.cancel();
return running;
}
@Override
public void onAttach(GalleryView.Adapter adapter) {
AssertUtils.assertNull("The ScrollLayoutManager is attached", mAdapter);
AssertUtils.assertNotNull("The iterator is null", adapter);
mAdapter = adapter;
// Reset parameters
resetParameters();
}
private void removeProgress() {
if (mProgress != null) {
mGalleryView.removeComponent(mProgress);
mGalleryView.releaseProgress(mProgress);
mProgress = null;
}
}
private void removeErrorView() {
if (mErrorView != null) {
mGalleryView.removeComponent(mErrorView);
mGalleryView.releaseErrorView(mErrorView);
mErrorView = null;
mErrorStr = null;
}
}
private void removePage(@NonNull GalleryPageView page) {
mGalleryView.removeComponent(page);
mAdapter.unbind(page);
mGalleryView.releasePage(page);
}
private void removeAllPages() {
for (GalleryPageView page : mPages) {
removePage(page);
}
mPages.clear();
}
@Override
public GalleryView.Adapter onDetach() {
AssertUtils.assertNotNull("The PagerLayoutManager is not attached", mAdapter);
// Cancel all animations
cancelAllAnimations();
// Remove all view
removeProgress();
removeErrorView();
removeAllPages();
// Clear iterator
GalleryView.Adapter iterator = mAdapter;
mAdapter = null;
return iterator;
}
private GalleryPageView obtainPage() {
GalleryPageView page = mGalleryView.obtainPage();
page.getImageView().setScaleOffset(ImageView.SCALE_FIT, ImageView.START_POSITION_TOP_RIGHT, 1.0f);
return page;
}
private GalleryPageView getPageForIndex(List<GalleryPageView> pages, int index, boolean remove) {
for (Iterator<GalleryPageView> iterator = pages.iterator(); iterator.hasNext(); ) {
GalleryPageView page = iterator.next();
if (index == page.getIndex()) {
if (remove) {
iterator.remove();
}
return page;
}
}
return null;
}
private boolean isInScreen(GalleryPageView page) {
int height = mGalleryView.getHeight();
Rect bound = page.bounds();
int pageTop = bound.top;
int pageBottom = bound.bottom;
return (pageTop >= 0 && pageTop < height) || (pageBottom > 0 && pageBottom <= height) ||
(pageTop < 0 && pageBottom > height);
}
private float getReservation() {
return Math.max(RESERVATION, (((1 + 2 * RESERVATION) * mScale) - 1) / 2);
}
private void fillPages(int startIndex, int startOffset) {
final GalleryView.Adapter adapter = mAdapter;
final GalleryView galleryView = mGalleryView;
final LinkedList<GalleryPageView> pages = mPages;
final LinkedList<GalleryPageView> tempPages = mTempPages;
final int width = galleryView.getWidth();
final int height = galleryView.getHeight();
final int pageWidth = (int) (width * mScale);
final int size = adapter.size();
final int interval = mInterval;
final float reservation = getReservation();
final int minY = (int) (-height * reservation);
final int maxY = (int) (height * (1 + reservation));
final int widthSpec = GLView.MeasureSpec.makeMeasureSpec(pageWidth, GLView.MeasureSpec.EXACTLY);
final int heightSpec = GLView.MeasureSpec.makeMeasureSpec(height, GLView.MeasureSpec.UNSPECIFIED);
// Fix start index and start offset
if (startIndex < 0) {
startIndex = 0;
startOffset = 0;
} else if (startIndex >= size) {
startIndex = size - 1;
startOffset = 0;
} else if (startOffset < minY) {
while (true) {
GalleryPageView page = getPageForIndex(pages, startIndex, false);
if (null == page) {
startOffset = minY;
break;
} else {
page.measure(widthSpec, heightSpec);
if (startOffset + page.getHeight() > minY) {
break;
} else if (size - 1 == startIndex) {
startOffset = 0;
break;
} else {
++startIndex;
startOffset += page.getHeight() + interval;
if (startOffset >= minY) {
break;
}
}
}
}
} else if (startOffset >= maxY) {
if (0 == startIndex) {
startOffset = 0;
} else {
--startIndex;
int startBottomOffset = startOffset - interval;
while (true) {
GalleryPageView page = getPageForIndex(pages, startIndex, false);
if (null == page) {
startOffset = maxY - 1;
break;
} else {
page.measure(widthSpec, heightSpec);
startOffset = startBottomOffset - page.getHeight();
if (startOffset < maxY) {
break;
} else if (0 == startIndex) {
startOffset = 0;
break;
} else {
--startIndex;
startBottomOffset = startOffset - interval;
}
}
}
}
}
// Put page to temp list
tempPages.addAll(pages);
pages.clear();
// Sanitize offsetX
int margin = pageWidth - width;
if (margin >= 0) {
mOffsetX = MathUtils.clamp(mOffsetX, -margin, 0);
} else {
mOffsetX = -margin / 2;
}
// Layout start page
GalleryPageView page = getPageForIndex(tempPages, startIndex, true);
if (null == page) {
page = obtainPage();
galleryView.addComponent(page);
adapter.bind(page, startIndex);
}
pages.add(page);
page.measure(widthSpec, heightSpec);
page.layout(mOffsetX, startOffset, mOffsetX + pageWidth, startOffset + page.getMeasuredHeight());
// Prepare for check up and down
int bottomOffset = startOffset - interval;
int topOffset = startOffset + page.getMeasuredHeight() + interval;
// Check up
int index = startIndex - 1;
while (bottomOffset > minY && index >= 0) {
page = getPageForIndex(tempPages, index, true);
if (null == page) {
page = obtainPage();
galleryView.addComponent(page);
adapter.bind(page, index);
}
pages.addFirst(page);
page.measure(widthSpec, heightSpec);
page.layout(mOffsetX, bottomOffset - page.getMeasuredHeight(), mOffsetX + pageWidth, bottomOffset);
// Update
bottomOffset -= page.getMeasuredHeight() + interval;
index--;
}
// Avoid space in top
page = pages.getFirst();
if (0 == page.getIndex() && page.bounds().top > 0) {
int offset = -page.bounds().top;
for (GalleryPageView p : pages) {
p.offsetTopAndBottom(offset);
}
topOffset += offset;
}
// Check down
index = startIndex + 1;
while (topOffset < maxY && index < size) {
page = getPageForIndex(tempPages, index, true);
if (null == page) {
page = obtainPage();
galleryView.addComponent(page);
adapter.bind(page, index);
}
pages.addLast(page);
page.measure(widthSpec, heightSpec);
page.layout(mOffsetX, topOffset, mOffsetX + pageWidth, topOffset + page.getMeasuredHeight());
// Update
topOffset += page.getMeasuredHeight() + interval;
index++;
}
// Avoid space in bottom
if (size - 1 == pages.getLast().getIndex()) {
while (true) {
page = pages.getLast();
int pagesBottom = page.bounds().bottom;
if (pagesBottom >= height) {
break;
}
page = pages.getFirst();
index = page.getIndex();
if (0 == index) {
break;
}
--index;
int pagesTop = page.bounds().top;
page = getPageForIndex(tempPages, index, true);
if (null == page) {
page = obtainPage();
galleryView.addComponent(page);
adapter.bind(page, index);
}
pages.addFirst(page);
page.measure(widthSpec, heightSpec);
int offset = Math.min(height - pagesBottom, page.getMeasuredHeight());
for (GalleryPageView p : pages) {
p.offsetTopAndBottom(offset);
}
int bottom = pagesTop - interval + offset;
page.layout(mOffsetX, bottom - page.getMeasuredHeight(), mOffsetX + pageWidth, bottom);
}
}
// Remove remain page
for (GalleryPageView p : tempPages) {
removePage(p);
}
tempPages.clear();
// Update state
if (!pages.isEmpty()) {
page = pages.getFirst();
mIndex = page.getIndex();
mOffsetY = page.bounds().top;
}
}
@Override
public void onFill() {
GalleryView.Adapter adapter = mAdapter;
GalleryView galleryView = mGalleryView;
AssertUtils.assertNotNull("The PagerLayoutManager is not attached", adapter);
int size = adapter.size();
String errorStr = adapter.getError();
if (size == GalleryProvider.STATE_WAIT) { // Wait here, show progress bar
// Remove error view and all pages
removeErrorView();
removeAllPages();
// Ensure progress
if (mProgress == null) {
mProgress = galleryView.obtainProgress();
galleryView.addComponent(mProgress);
}
// Place progress center
placeCenter(mProgress);
} else if (size <= GalleryProvider.STATE_ERROR || size == 0) { // Get error or empty, show error text
// Ensure error is not null
if (0 == size) {
errorStr = galleryView.getEmptyStr();
} else if (null == errorStr) {
errorStr = galleryView.getDefaultErrorStr();
}
// Remove progress and all pages
removeProgress();
removeAllPages();
// Ensure error view
if (mErrorView == null) {
mErrorView = galleryView.obtainErrorView();
galleryView.addComponent(mErrorView);
}
// Update error string
if (!errorStr.equals(mErrorStr)) {
mErrorStr = errorStr;
galleryView.bindErrorView(mErrorView, errorStr);
}
// Place error view center
placeCenter(mErrorView);
} else {
LinkedList<GalleryPageView> pages = mPages;
// Remove progress and error view
removeProgress();
removeErrorView();
// Ensure index in range
int index = mIndex;
if (index < 0) {
Log.e(TAG, "index < 0, index = " + index);
index = 0;
mIndex = index;
removeAllPages();
} else if (index >= size) {
Log.e(TAG, "index >= size, index = " + index + ", size = " + size);
index = size - 1;
mIndex = index;
removeAllPages();
}
// Find keep index and keep top
int keepTop = INVALID_TOP;
int keepTopIndex;
if (GalleryPageView.INVALID_INDEX != mKeepTopPageIndex) {
keepTopIndex = mKeepTopPageIndex;
keepTop = mKeepTop;
} else if (GalleryPageView.INVALID_INDEX != mFirstShownPageIndex) {
keepTopIndex = mFirstShownPageIndex;
} else {
keepTopIndex = GalleryPageView.INVALID_INDEX;
}
if (GalleryPageView.INVALID_INDEX != keepTopIndex && INVALID_TOP == keepTop) {
keepTop = mOffsetY;
for (GalleryPageView page : pages) {
// Check keep page
if (keepTopIndex == page.getIndex()) {
break;
}
keepTop += page.getHeight() + mInterval;
}
}
int startIndex;
int startOffset;
if (GalleryPageView.INVALID_INDEX != keepTopIndex) {
startIndex = keepTopIndex;
startOffset = keepTop;
} else {
startIndex = mIndex;
startOffset = mOffsetY;
}
fillPages(startIndex, startOffset);
// Get first shown image
mFirstShownPageIndex = GalleryPageView.INVALID_INDEX;
for (GalleryPageView page : mPages) {
// Check first shown loaded page
if ((mScrollUp || mFlingUp) && !page.isLoaded()) {
continue;
}
if (isInScreen(page)) {
mFirstShownPageIndex = page.getIndex();
break;
}
}
}
}
@Override
public void onDown() {
mDeltaX = 0;
mDeltaY = 0;
mScrollUp = false;
mStopAnimationFinger = cancelAllAnimations();
}
@Override
public void onUp() {
mScrollUp = false;
}
@Override
public void onDoubleTapConfirmed(float x, float y) {
if (mPages.size() <= 0) {
return;
}
float startScale = mScale;
float endScale;
if (startScale < MAX_SCALE - SCALE_ERROR) {
endScale = MAX_SCALE;
} else {
endScale = MIN_SCALE;
}
mSmoothScaler.startSmoothScaler(x, y, startScale, endScale, 300);
}
@Override
public void onLongPress(float x, float y) {
}
private void getBottomState() {
List<GalleryPageView> pages = mPages;
int bottom = mOffsetY;
int i = 0;
for (GalleryPageView page : pages) {
if (i != 0) {
bottom += mInterval;
}
bottom += page.getHeight();
i++;
}
boolean hasNext = mIndex + pages.size() < mAdapter.size();
mBottomStateBottom = bottom;
mBottomStateHasNext = hasNext;
}
// True for get top or bottom
private boolean scrollInternal(float dx, float dy, boolean fling, float x, float y) {
if (mPages.size() <= 0) {
return false;
}
GalleryView galleryView = mGalleryView;
int width = galleryView.getWidth();
int height = galleryView.getHeight();
int pageWidth = (int) (width * mScale);
final float reservation = getReservation();
boolean requestFill = false;
boolean result = false;
int margin = pageWidth - width;
int dxInt = (int) dx;
if (margin > 0 && 0 != dxInt) {
int oldOffsetX = mOffsetX;
int exceptOffsetX = oldOffsetX - dxInt;
mOffsetX = MathUtils.clamp(exceptOffsetX, -margin, 0);
if (mOffsetX != oldOffsetX) {
requestFill = true;
}
// Do not show over scroll effect for left and right
/*
int extraOffsetX = mOffsetX - exceptOffsetX;
if (0 != extraOffsetX) {
overScrollEdge(extraOffsetX, 0, x, y);
}
*/
}
int remainY = (int) dy;
while (remainY != 0) {
if (remainY < 0) { // Try to show top
int limit;
if (mIndex > 0) {
limit = (int) (-height * reservation) + mInterval;
} else {
limit = 0;
}
if (mOffsetY - remainY <= limit) {
mOffsetY -= remainY;
remainY = 0;
requestFill = true;
mDeltaX = 0;
mDeltaY = 0;
} else {
if (mIndex > 0) {
mOffsetY = limit;
remainY = remainY + limit - mOffsetY;
// Offset one pixel to avoid infinite loop
++mOffsetY;
++remainY;
galleryView.forceFill();
requestFill = false;
mDeltaX = 0;
mDeltaY = 0;
} else {
if (mOffsetY != limit) {
mOffsetY = limit;
requestFill = true;
}
remainY = 0;
result = true;
}
}
} else { // Try to show bottom
getBottomState();
int bottom = mBottomStateBottom;
boolean hasNext = mBottomStateHasNext;
int limit;
if (hasNext) {
limit = (int) (height * (1 + reservation)) - mInterval;
} else {
limit = height;
}
// Fix limit for page not fill screen
limit = Math.min(bottom, limit);
if (bottom - remainY >= limit) {
mOffsetY -= remainY;
remainY = 0;
requestFill = true;
mDeltaX = 0;
mDeltaY = 0;
} else {
if (hasNext) {
mOffsetY -= bottom - limit;
remainY = remainY + limit - bottom;
// Offset one pixel to avoid infinite loop
--mOffsetY;
--remainY;
galleryView.forceFill();
requestFill = false;
mDeltaX = 0;
mDeltaY = 0;
} else {
if (mOffsetY != limit) {
mOffsetY -= bottom - limit;
requestFill = true;
}
remainY = 0;
result = true;
}
}
}
}
if (requestFill) {
mGalleryView.requestFill();
}
return result;
}
@Override
public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) {
mKeepTopPageIndex = GalleryPageView.INVALID_INDEX;
mKeepTop = INVALID_TOP;
mScrollUp = dy < 0;
scrollInternal(dx, dy, false, x, y);
}
@Override
public void onFling(float velocityX, float velocityY) {
if (mPages.size() <= 0) {
return;
}
mKeepTopPageIndex = GalleryPageView.INVALID_INDEX;
mKeepTop = INVALID_TOP;
mFlingUp = velocityY > 0;
int maxX;
int minX;
int width = mGalleryView.getWidth();
int pageWidth = (int) (width * mScale);
int margin = pageWidth - width;
if (margin > 0) {
maxX = -mOffsetX;
minX = -margin + mOffsetX;
} else {
maxX = 0;
minX = 0;
}
int maxY;
if (mIndex > 0) {
maxY = Integer.MAX_VALUE;
} else {
maxY = -mOffsetY;
}
getBottomState();
int bottom = mBottomStateBottom;
boolean hasNext = mBottomStateHasNext;
int minY;
if (hasNext) {
minY = Integer.MIN_VALUE;
} else {
minY = mGalleryView.getHeight() - bottom;
}
mPageFling.startFling((int) velocityX, minX, maxX,
(int) velocityY, minY, maxY);
}
@Override
public boolean canScale() {
return mPages.size() > 0;
}
@Override
public void onScale(float focusX, float focusY, float scale) {
if (mPages.isEmpty()) {
return;
}
float oldScale = mScale;
mScale = MathUtils.clamp(oldScale * scale, MIN_SCALE, MAX_SCALE);
scale = mScale / oldScale;
if (oldScale != mScale) {
GalleryPageView page = null;
// Keep scale page origin position
for (GalleryPageView p : mPages) {
if (p.bounds().top < focusY) {
page = p;
} else {
break;
}
}
if (null != page) {
mKeepTopPageIndex = page.getIndex();
mKeepTop = page.bounds().top;
mGalleryView.forceFill();
int oldKeepTop = mKeepTop;
mKeepTop = INVALID_TOP;
// Apply scroll
int newOffsetX = (int) (focusX - ((focusX - mOffsetX) * scale));
int newKeepTop;
if (page.isLoaded()) {
newKeepTop = (int) (focusY - ((focusY - oldKeepTop) * scale));
} else {
newKeepTop = oldKeepTop;
}
scrollInternal(mOffsetX - newOffsetX, oldKeepTop - newKeepTop, false, focusX, focusY);
} else {
Log.e(TAG, "Can't find target page");
mKeepTopPageIndex = GalleryPageView.INVALID_INDEX;
mKeepTop = INVALID_TOP;
mGalleryView.forceFill();
}
}
}
@Override
public boolean onUpdateAnimation(long time) {
boolean invalidate = mPageFling.calculate(time);
invalidate |= mSmoothScaler.calculate(time);
return invalidate;
}
@Override
public void onDataChanged() {
AssertUtils.assertNotNull("The PagerLayoutManager is not attached", mAdapter);
// Cancel all animations
cancelAllAnimations();
// Remove all views
removeProgress();
removeErrorView();
removeAllPages();
// Reset parameters
resetParameters();
mGalleryView.requestFill();
}
@Override
public void onPageLeft() {
int size = mAdapter.size();
if (size <= 0 || mPages.isEmpty()) {
return;
}
///////
// UP
///////
GalleryView galleryView = mGalleryView;
if (mIndex == 0 && mOffsetY >= 0) {
} else {
// Cancel all animations
cancelAllAnimations();
// Get first shown page
GalleryPageView previousPage = null;
GalleryPageView firstShownPage = null;
for (GalleryPageView p : mPages) {
if (isInScreen(p)) {
firstShownPage = p;
break;
}
previousPage = p;
}
int height = galleryView.getHeight();
int maxOffset = height - mInterval;
if (null == firstShownPage) {
Log.e(TAG, "Can't find first shown page when paging left");
mOffsetY += height / 2;
} else {
int firstShownTop = firstShownPage.bounds().top;
if (firstShownTop >= 0) {
if (null == previousPage) {
Log.e(TAG, "Can't find previous page when paging left and offsetY == 0");
mOffsetY += height / 2;
} else {
mOffsetY += Math.min(maxOffset, -previousPage.bounds().top);
}
} else {
mOffsetY += Math.min(maxOffset, -firstShownTop);
}
}
// Request fill
mGalleryView.requestFill();
}
}
@Override
public void onPageRight() {
int size = mAdapter.size();
if (size <= 0 || mPages.isEmpty()) {
return;
}
/////////
// DOWN
/////////
GalleryView galleryView = mGalleryView;
getBottomState();
int bottom = mBottomStateBottom;
boolean hasNext = mBottomStateHasNext;
if (hasNext || bottom > galleryView.getHeight()) {
// Cancel all animations
cancelAllAnimations();
// Get first shown page
GalleryPageView lastShownPage = null;
GalleryPageView nextPage = null;
for (GalleryPageView p : mPages) {
if (isInScreen(p)) {
lastShownPage = p;
} else if (null != lastShownPage) {
nextPage = p;
break;
}
}
int height = galleryView.getHeight();
int maxOffset = height - mInterval;
if (null == lastShownPage) {
Log.e(TAG, "Can't find last shown page when paging left");
mOffsetY -= height / 2;
} else {
int lastShownBottom = lastShownPage.bounds().bottom;
if (lastShownBottom <= height) {
if (null == nextPage) {
Log.e(TAG, "Can't find previous page when paging left and offsetY == 0");
mOffsetY -= height / 2;
} else {
mOffsetY -= Math.min(maxOffset, nextPage.bounds().bottom - height);
}
} else {
mOffsetY -= Math.min(maxOffset, lastShownBottom - height);
}
}
// Request fill
mGalleryView.requestFill();
}
}
@Override
public boolean isTapOrPressEnable() {
return !mStopAnimationFinger;
}
@Override
public GalleryPageView findPageByIndex(int index) {
for (GalleryPageView page : mPages) {
if (page.getIndex() == index) {
return page;
}
}
return null;
}
@Override
public int getCurrentIndex() {
for (GalleryPageView page : mPages) {
if (isInScreen(page)) {
return page.getIndex();
}
}
return GalleryPageView.INVALID_INDEX;
}
@Override
public void setCurrentIndex(int index) {
int size = mAdapter.size();
if (size <= 0) {
// Can't get size now, assume size is MAX
size = Integer.MAX_VALUE;
}
if (index < 0 || index >= size) {
return;
}
mKeepTopPageIndex = index;
mKeepTop = INVALID_TOP;
if (mPages.isEmpty()) {
mIndex = index;
} else {
// Fix the index page
GalleryPageView targetPage = null;
for (GalleryPageView page : mPages) {
if (page.getIndex() == index) {
targetPage = page;
break;
}
}
if (targetPage != null) {
// Cancel all animations
cancelAllAnimations();
mOffsetY -= targetPage.bounds().top;
// Request fill
mGalleryView.requestFill();
} else {
mIndex = index;
mOffsetY = 0;
// Cancel all animations
cancelAllAnimations();
// Remove all view
removeProgress();
removeErrorView();
removeAllPages();
// Request fill
mGalleryView.requestFill();
}
}
}
@Override
public int getIndexUnder(float x, float y) {
if (mPages.isEmpty()) {
return GalleryPageView.INVALID_INDEX;
} else {
int intX = (int) x;
int intY = (int) y;
for (GalleryPageView page : mPages) {
if (page.bounds().contains(intX, intY)) {
return page.getIndex();
}
}
return GalleryPageView.INVALID_INDEX;
}
}
@Override
int getInternalCurrentIndex() {
int currentIndex = getCurrentIndex();
if (currentIndex == GalleryPageView.INVALID_INDEX) {
currentIndex = mIndex;
}
return currentIndex;
}
private class PageFling extends Fling {
private int mVelocityX;
private int mVelocityY;
private int mDx;
private int mDy;
private int mLastX;
private int mLastY;
public PageFling(Context context) {
super(context);
}
public void startFling(int velocityX, int minX, int maxX,
int velocityY, int minY, int maxY) {
mVelocityX = velocityX;
mVelocityY = velocityY;
mDx = (int) (getSplineFlingDistance(velocityX) * Math.signum(velocityX));
mDy = (int) (getSplineFlingDistance(velocityY) * Math.signum(velocityY));
mLastX = 0;
mLastY = 0;
int durationX = getSplineFlingDuration(velocityX);
int durationY = getSplineFlingDuration(velocityY);
if (mDx < minX) {
durationX = adjustDuration(0, mDx, minX, durationX);
mDx = minX;
}
if (mDx > maxX) {
durationX = adjustDuration(0, mDx, maxX, durationX);
mDx = maxX;
}
if (mDy < minY) {
durationY = adjustDuration(0, mDy, minY, durationY);
mDy = minY;
}
if (mDy > maxY) {
durationY = adjustDuration(0, mDy, maxY, durationY);
mDy = maxY;
}
if (mDx == 0 && mDy == 0) {
return;
}
setDuration(Math.max(durationX, durationY));
start();
mGalleryView.invalidate();
}
@Override
protected void onCalculate(float progress) {
int x = (int) (mDx * progress);
int y = (int) (mDy * progress);
int offsetX = x - mLastX;
int offsetY = y - mLastY;
if (scrollInternal(-offsetX, -offsetY, true, 0, 0)) {
cancel();
onFinish();
}
mLastX = x;
mLastY = y;
}
@Override
protected void onFinish() {
mFlingUp = false;
int index = mIndex;
boolean topEdge = index <= 0 && mOffsetY >= 0;
getBottomState();
int bottom = mBottomStateBottom;
boolean hasNext = mBottomStateHasNext;
boolean bottomEdge = !hasNext && bottom <= mGalleryView.getHeight();
}
}
private class SmoothScaler extends Animation {
private float mFocusX;
private float mFocusY;
private float mStartScale;
private float mEndScale;
private float mLastScale;
public SmoothScaler() {
setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
}
public void startSmoothScaler(float focusX, float focusY,
float startScale, float endScale, int duration) {
mFocusX = focusX;
mFocusY = focusY;
mStartScale = startScale;
mEndScale = endScale;
mLastScale = startScale;
setDuration(duration);
start();
mGalleryView.invalidate();
}
@Override
protected void onCalculate(float progress) {
if (mPages.size() <= 0) {
return;
}
float scale = MathUtils.lerp(mStartScale, mEndScale, progress);
onScale(mFocusX, mFocusY, scale / mLastScale);
mLastScale = scale;
}
}
}