Files
EhViewer/app/src/main/java/com/hippo/glgallery/GalleryView.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

1209 lines
39 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.Color;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.view.MotionEvent;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.hippo.glview.annotation.RenderThread;
import com.hippo.glview.glrenderer.BasicTexture;
import com.hippo.glview.glrenderer.GLCanvas;
import com.hippo.glview.glrenderer.StringTexture;
import com.hippo.glview.glrenderer.Texture;
import com.hippo.glview.image.ImageMovableTextTexture;
import com.hippo.glview.util.GalleryUtils;
import com.hippo.glview.view.AnimationTime;
import com.hippo.glview.view.GLRoot;
import com.hippo.glview.view.GLView;
import com.hippo.glview.widget.GLProgressView;
import com.hippo.glview.widget.GLTextureView;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.Pool;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public final class GalleryView extends GLView implements GestureRecognizer.Listener {
public static final int LAYOUT_LEFT_TO_RIGHT = 0;
public static final int LAYOUT_RIGHT_TO_LEFT = 1;
public static final int LAYOUT_TOP_TO_BOTTOM = 2;
public static final int SCALE_ORIGIN = ImageView.SCALE_ORIGIN;
public static final int SCALE_FIT_WIDTH = ImageView.SCALE_FIT_WIDTH;
public static final int SCALE_FIT_HEIGHT = ImageView.SCALE_FIT_HEIGHT;
public static final int SCALE_FIT = ImageView.SCALE_FIT;
public static final int SCALE_FIXED = ImageView.SCALE_FIXED;
public static final int START_POSITION_TOP_LEFT = ImageView.START_POSITION_TOP_LEFT;
public static final int START_POSITION_TOP_RIGHT = ImageView.START_POSITION_TOP_RIGHT;
public static final int START_POSITION_BOTTOM_LEFT = ImageView.START_POSITION_BOTTOM_LEFT;
public static final int START_POSITION_BOTTOM_RIGHT = ImageView.START_POSITION_BOTTOM_RIGHT;
public static final int START_POSITION_CENTER = ImageView.START_POSITION_CENTER;
private static final float[] LEFT_AREA = {0.0f, 0.0f, 1.0f / 3.0f, 1f};
private static final float[] RIGHT_AREA = {2.0f / 3.0f, 0.0f, 1.0f, 1f};
private static final float[] MENU_AREA = {1.0f / 3.0f, 0.0f, 2.0f / 3.0f, 1.0f / 2.0f};
private static final float[] SLIDER_AREA = {1.0f / 3.0f, 1.0f / 2.0f, 2.0f / 3.0f, 1.0f};
private static final int METHOD_ON_SINGLE_TAP_UP = 0;
private static final int METHOD_ON_SINGLE_TAP_CONFIRMED = 1;
private static final int METHOD_ON_DOUBLE_TAP = 2;
private static final int METHOD_ON_DOUBLE_TAP_CONFIRMED = 3;
private static final int METHOD_ON_LONG_PRESS = 4;
private static final int METHOD_ON_SCROLL = 5;
private static final int METHOD_ON_FLING = 6;
private static final int METHOD_ON_SCALE_BEGIN = 7;
private static final int METHOD_ON_SCALE = 8;
private static final int METHOD_ON_SCALE_END = 9;
private static final int METHOD_ON_DOWN = 10;
private static final int METHOD_ON_UP = 11;
private static final int METHOD_ON_POINTER_DOWN = 12;
private static final int METHOD_ON_POINTER_UP = 13;
private static final int METHOD_SET_LAYOUT_MODE = 14;
private static final int METHOD_SET_CURRENT_PAGE = 15;
private static final int METHOD_PAGE_LEFT = 16;
private static final int METHOD_PAGE_RIGHT = 17;
private static final int METHOD_SET_SCALE_MODE = 18;
private static final int METHOD_SET_START_POSITION = 19;
private static final int METHOD_ON_ATTACH_TO_ROOT = 20;
private static final int METHOD_SET_PAGER_INTERVAL = 21;
private static final int METHOD_SET_SCROLL_INTERVAL = 22;
private final Context mContext;
private final GestureRecognizer mGestureRecognizer;
@Nullable
private final Listener mListener;
private final Pool<GalleryPageView> mGalleryPageViewPool = new Pool<>(5);
private final int mBackgroundColor;
private final int mPageMinHeight;
private final int mPageInfoInterval;
private final int mProgressColor;
private final int mProgressSize;
private final int mPageTextColor;
private final int mPageTextSize;
private final Typeface mPageTextTypeface;
private final int mErrorTextSize;
private final int mErrorTextColor;
private final String mDefaultErrorString;
private final String mEmptyString;
private final Rect mLeftArea = new Rect();
private final Rect mRightArea = new Rect();
private final Rect mMenuArea = new Rect();
private final Rect mSliderArea = new Rect();
private final List<Integer> mMethodList = new ArrayList<>(5);
private final List<Object[]> mArgsList = new ArrayList<>(5);
private final List<Integer> mMethodListTemp = new ArrayList<>(5);
private final List<Object[]> mArgsListTemp = new ArrayList<>(5);
private final AtomicInteger mCurrentIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private Adapter mAdapter;
private ImageMovableTextTexture mPageTextTexture;
private PagerLayoutManager mPagerLayoutManager;
private ScrollLayoutManager mScrollLayoutManager;
@Nullable
private LayoutManager mLayoutManager;
private GLProgressView mProgressCache;
private GLTextureView mErrorViewCache;
private int mPagerInterval;
private int mScrollInterval;
private boolean mEnableRequestFill = true;
private boolean mRequestFill = false;
private boolean mWillFill = false;
private boolean mScale = false;
private boolean mScroll = false;
private boolean mFirstScroll = false;
private int mLayoutMode = LAYOUT_RIGHT_TO_LEFT;
private int mScaleMode = ImageView.SCALE_FIT;
private int mStartPosition = ImageView.START_POSITION_TOP_LEFT;
private int mIndex;
private GalleryView(Builder build) {
mContext = build.mContext;
mAdapter = build.mAdapter;
mAdapter.setGalleryView(this);
mListener = build.mListener;
mGestureRecognizer = new GestureRecognizer(mContext, this);
mLayoutMode = build.mLayoutMode;
mScaleMode = build.mScaleMode;
mStartPosition = build.mStartPosition;
mIndex = MathUtils.clamp(build.mStartPage, 0, Integer.MAX_VALUE);
mBackgroundColor = build.mBackgroundColor;
mPageMinHeight = build.mPageMinHeight;
mPagerInterval = build.mPagerInterval;
mScrollInterval = build.mScrollInterval;
mPageInfoInterval = build.mPageInfoInterval;
mProgressColor = build.mProgressColor;
mProgressSize = build.mProgressSize;
mPageTextColor = build.mPageTextColor;
mPageTextSize = build.mPageTextSize;
mPageTextTypeface = build.mPageTextTypeface;
mErrorTextColor = build.mErrorTextColor;
mErrorTextSize = build.mErrorTextSize;
mDefaultErrorString = build.mDefaultErrorString;
mEmptyString = build.mEmptyString;
setBackgroundColor(mBackgroundColor);
}
@LayoutMode
public static int sanitizeLayoutMode(int layoutMode) {
if (layoutMode != GalleryView.LAYOUT_LEFT_TO_RIGHT &&
layoutMode != GalleryView.LAYOUT_RIGHT_TO_LEFT &&
layoutMode != GalleryView.LAYOUT_TOP_TO_BOTTOM) {
return GalleryView.LAYOUT_LEFT_TO_RIGHT;
} else {
return layoutMode;
}
}
@ScaleMode
public static int sanitizeScaleMode(int scaleMode) {
if (scaleMode != GalleryView.SCALE_ORIGIN &&
scaleMode != GalleryView.SCALE_FIT_WIDTH &&
scaleMode != GalleryView.SCALE_FIT_HEIGHT &&
scaleMode != GalleryView.SCALE_FIT &&
scaleMode != GalleryView.SCALE_FIXED) {
return GalleryView.SCALE_FIT;
} else {
return scaleMode;
}
}
@StartPosition
public static int sanitizeStartPosition(int startPosition) {
if (startPosition != GalleryView.START_POSITION_TOP_LEFT &&
startPosition != GalleryView.START_POSITION_TOP_RIGHT &&
startPosition != GalleryView.START_POSITION_BOTTOM_LEFT &&
startPosition != GalleryView.START_POSITION_BOTTOM_RIGHT &&
startPosition != GalleryView.START_POSITION_CENTER) {
return GalleryView.START_POSITION_TOP_LEFT;
} else {
return startPosition;
}
}
private void ensurePagerLayoutManager() {
if (mPagerLayoutManager == null) {
mPagerLayoutManager = new PagerLayoutManager(mContext, this,
mScaleMode, mStartPosition, 1.0f, mPagerInterval);
}
}
private void ensureScrollLayoutManager() {
if (mScrollLayoutManager == null) {
mScrollLayoutManager = new ScrollLayoutManager(mContext, this, mScrollInterval);
}
}
private void attachLayoutManager() {
if (null != mLayoutManager) {
return;
}
switch (mLayoutMode) {
case LAYOUT_LEFT_TO_RIGHT:
ensurePagerLayoutManager();
mPagerLayoutManager.setMode(PagerLayoutManager.MODE_LEFT_TO_RIGHT);
mPagerLayoutManager.onAttach(mAdapter);
mPagerLayoutManager.setCurrentIndex(mIndex);
mAdapter = null;
mLayoutManager = mPagerLayoutManager;
break;
case LAYOUT_RIGHT_TO_LEFT:
ensurePagerLayoutManager();
mPagerLayoutManager.setMode(PagerLayoutManager.MODE_RIGHT_TO_LEFT);
mPagerLayoutManager.onAttach(mAdapter);
mPagerLayoutManager.setCurrentIndex(mIndex);
mAdapter = null;
mLayoutManager = mPagerLayoutManager;
break;
case LAYOUT_TOP_TO_BOTTOM:
ensureScrollLayoutManager();
mScrollLayoutManager.onAttach(mAdapter);
mScrollLayoutManager.setCurrentIndex(mIndex);
mAdapter = null;
mLayoutManager = mScrollLayoutManager;
break;
}
requestFill();
}
private void detachLayoutManager() {
if (null == mLayoutManager) {
return;
}
mIndex = mLayoutManager.getInternalCurrentIndex();
mAdapter = mLayoutManager.onDetach();
mLayoutManager = null;
}
@SuppressWarnings("deprecation")
private void onAttachToRootInternal() {
if (null == mPageTextTexture) {
mPageTextTexture = ImageMovableTextTexture.create(mPageTextTypeface,
mPageTextSize, mPageTextColor,
new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'});
}
attachLayoutManager();
}
private void setPagerIntervalInternal(int interval) {
mPagerInterval = interval;
if (mPagerLayoutManager != null) {
mPagerLayoutManager.setInterval(interval);
}
}
private void setScrollIntervalInternal(int interval) {
mScrollInterval = interval;
if (mScrollLayoutManager != null) {
mScrollLayoutManager.setInterval(interval);
}
}
@Override
public void onAttachToRoot(GLRoot root) {
super.onAttachToRoot(root);
postMethod(METHOD_ON_ATTACH_TO_ROOT);
}
@Override
public void onDetachFromRoot() {
// When detached, render() will not be called. So do it here
detachLayoutManager();
if (null != mPageTextTexture) {
mPageTextTexture.recycle();
mPageTextTexture = null;
}
super.onDetachFromRoot();
}
public int getLayoutMode() {
return mLayoutMode;
}
public void setLayoutMode(@LayoutMode int layoutMode) {
postMethod(METHOD_SET_LAYOUT_MODE, layoutMode);
}
public int getCurrentIndex() {
return mCurrentIndex.get();
}
@Override
public void requestLayout() {
// Do not need requestLayout, because the size will not change
requestFill();
}
public void requestFill() {
if (mEnableRequestFill) {
mRequestFill = true;
if (!mWillFill) {
invalidate();
}
}
}
@Override
protected boolean dispatchTouchEvent(MotionEvent event) {
// Do not pass event to component, so handle event here
mGestureRecognizer.onTouchEvent(event);
return true;
}
String getDefaultErrorStr() {
return mDefaultErrorString;
}
String getEmptyStr() {
return mEmptyString;
}
boolean isFirstScroll() {
boolean firstScroll = mFirstScroll;
mFirstScroll = false;
return firstScroll;
}
// Make sure method run in render thread to ensure thread safe
private void postMethod(int method, Object... args) {
synchronized (this) {
mMethodList.add(method);
mArgsList.add(args);
}
invalidate();
}
public void setCurrentPage(int page) {
postMethod(METHOD_SET_CURRENT_PAGE, page);
}
public void pageLeft() {
postMethod(METHOD_PAGE_LEFT);
}
public void pageRight() {
postMethod(METHOD_PAGE_RIGHT);
}
public void setScaleMode(int scaleMode) {
postMethod(METHOD_SET_SCALE_MODE, scaleMode);
}
public void setStartPosition(int startPosition) {
postMethod(METHOD_SET_START_POSITION, startPosition);
}
public void setPagerInterval(int interval) {
postMethod(METHOD_SET_PAGER_INTERVAL, interval);
}
public void setScrollInterval(int interval) {
postMethod(METHOD_SET_SCROLL_INTERVAL, interval);
}
@Override
public boolean onSingleTapUp(float x, float y) {
postMethod(METHOD_ON_SINGLE_TAP_UP, x, y);
return true;
}
@Override
public boolean onSingleTapConfirmed(float x, float y) {
postMethod(METHOD_ON_SINGLE_TAP_CONFIRMED, x, y);
return true;
}
@Override
public boolean onDoubleTap(float x, float y) {
postMethod(METHOD_ON_DOUBLE_TAP, x, y);
return true;
}
@Override
public boolean onDoubleTapConfirmed(float x, float y) {
postMethod(METHOD_ON_DOUBLE_TAP_CONFIRMED, x, y);
return true;
}
@Override
public void onLongPress(float x, float y) {
if (mLayoutManager != null && !mLayoutManager.isTapOrPressEnable()) {
return;
}
postMethod(METHOD_ON_LONG_PRESS, x, y);
}
@Override
public boolean onScroll(float dx, float dy, float totalX, float totalY, float x, float y) {
postMethod(METHOD_ON_SCROLL, dx, dy, totalX, totalY, x, y);
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
postMethod(METHOD_ON_FLING, velocityX, velocityY);
return true;
}
@Override
public boolean onScaleBegin(float focusX, float focusY) {
postMethod(METHOD_ON_SCALE_BEGIN, focusX, focusY);
return true;
}
@Override
public boolean onScale(float focusX, float focusY, float scale) {
postMethod(METHOD_ON_SCALE, focusX, focusY, scale);
return true;
}
@Override
public void onScaleEnd() {
postMethod(METHOD_ON_SCALE_END);
}
@Override
public void onDown(float x, float y) {
postMethod(METHOD_ON_DOWN, x, y);
}
@Override
public void onUp() {
postMethod(METHOD_ON_UP);
}
@Override
public void onPointerDown(float x, float y) {
postMethod(METHOD_ON_POINTER_DOWN, x, y);
}
@Override
public void onPointerUp() {
postMethod(METHOD_ON_POINTER_UP);
}
@Override
protected void onLayout(boolean changeSize, int left, int top, int right, int bottom) {
fill();
if (changeSize) {
int width = right - left;
int height = bottom - top;
mLeftArea.set((int) (LEFT_AREA[0] * width), (int) (LEFT_AREA[1] * height),
(int) (LEFT_AREA[2] * width), (int) (LEFT_AREA[3] * height));
mRightArea.set((int) (RIGHT_AREA[0] * width), (int) (RIGHT_AREA[1] * height),
(int) (RIGHT_AREA[2] * width), (int) (RIGHT_AREA[3] * height));
mMenuArea.set((int) (MENU_AREA[0] * width), (int) (MENU_AREA[1] * height),
(int) (MENU_AREA[2] * width), (int) (MENU_AREA[3] * height));
mSliderArea.set((int) (SLIDER_AREA[0] * width), (int) (SLIDER_AREA[1] * height),
(int) (SLIDER_AREA[2] * width), (int) (SLIDER_AREA[3] * height));
}
}
@RenderThread
public void onDataChanged() {
GalleryUtils.assertInRenderThread();
if (mLayoutManager != null) {
mLayoutManager.onDataChanged();
}
}
private void onSingleTapUpInternal(float x, float y) {
}
private GalleryPageView findPageUnder(float x, float y) {
for (int i = 0, n = getComponentCount(); i < n; i++) {
GLView view = getComponent(i);
if (view instanceof GalleryPageView && view.bounds().contains((int) x, (int) y)) {
return (GalleryPageView) view;
}
}
return null;
}
private void onSingleTapConfirmedInternal(float x, float y) {
if (mLayoutManager == null || !mLayoutManager.isTapOrPressEnable()) {
return;
}
GalleryPageView page = findPageUnder(x, y);
if (page != null &&
page.getIndex() != GalleryPageView.INVALID_INDEX &&
page.isError() &&
page.isUnderInfo(x - page.bounds().left, y - page.bounds().top)) {
if (mListener != null) {
mListener.onTapErrorText(page.getIndex());
}
} else if (mSliderArea.contains((int) x, (int) y)) {
if (mListener != null) {
mListener.onTapSliderArea();
}
} else if (mMenuArea.contains((int) x, (int) y)) {
if (mListener != null) {
mListener.onTapMenuArea();
}
} else if (mLeftArea.contains((int) x, (int) y)) {
mLayoutManager.onPageLeft();
} else if (mRightArea.contains((int) x, (int) y)) {
mLayoutManager.onPageRight();
}
}
private void onDoubleTapInternal(float x, float y) {
}
private void onDoubleTapConfirmedInternal(float x, float y) {
if (mScale) {
return;
}
if (mLayoutManager != null) {
mLayoutManager.onDoubleTapConfirmed(x, y);
}
}
private void onLongPressInternal(float x, float y) {
if (mScale) {
return;
}
if (mLayoutManager == null) {
return;
}
int index = mLayoutManager.getIndexUnder(x, y);
if (index == GalleryPageView.INVALID_INDEX) {
return;
}
if (mListener != null) {
mListener.onLongPressPage(index);
}
}
private void onScrollInternal(float dx, float dy, float totalX, float totalY, float x, float y) {
if (mScale) {
return;
}
mScroll = true;
if (mLayoutManager != null) {
mLayoutManager.onScroll(dx, dy, totalX, totalY, x, y);
}
}
private void onFlingInternal(float velocityX, float velocityY) {
if (mLayoutManager != null) {
mLayoutManager.onFling(velocityX, velocityY);
}
}
private void onScaleBeginInternal(float focusX, float focusY) {
onScaleInternal(focusX, focusY, 1.0f);
}
private void onScaleInternal(float focusX, float focusY, float scale) {
if (mScroll || (mLayoutManager != null && !mLayoutManager.canScale())) {
return;
}
mScale = true;
if (mLayoutManager != null) {
mLayoutManager.onScale(focusX, focusY, scale);
}
}
private void onScaleEndInternal() {
}
private void onDownInternal(float x, float y) {
mScale = false;
mScroll = false;
mFirstScroll = true;
if (mLayoutManager != null) {
mLayoutManager.onDown();
}
}
private void onUpInternal() {
if (mLayoutManager != null) {
mLayoutManager.onUp();
}
}
private void onPointerDownInternal(float x, float y) {
if (!mScroll && (mLayoutManager != null && mLayoutManager.canScale())) {
mScale = true;
}
}
private void onPointerUpInternal() {
}
private void setLayoutModeInternal(int layoutMode) {
if (mLayoutMode == layoutMode) {
return;
}
mLayoutMode = layoutMode;
if (mLayoutManager == null) {
return;
}
switch (mLayoutMode) {
case LAYOUT_LEFT_TO_RIGHT:
if (mLayoutManager == mPagerLayoutManager) {
// mPagerLayoutManager already attached, just change mode
mPagerLayoutManager.setMode(PagerLayoutManager.MODE_LEFT_TO_RIGHT);
} else {
ensurePagerLayoutManager();
mPagerLayoutManager.setMode(PagerLayoutManager.MODE_LEFT_TO_RIGHT);
int index = mLayoutManager.getInternalCurrentIndex();
mPagerLayoutManager.onAttach(mLayoutManager.onDetach());
mPagerLayoutManager.setCurrentIndex(index);
mLayoutManager = mPagerLayoutManager;
}
break;
case LAYOUT_RIGHT_TO_LEFT:
if (mLayoutManager == mPagerLayoutManager) {
// mPagerLayoutManager already attached, just change mode
mPagerLayoutManager.setMode(PagerLayoutManager.MODE_RIGHT_TO_LEFT);
} else {
ensurePagerLayoutManager();
mPagerLayoutManager.setMode(PagerLayoutManager.MODE_RIGHT_TO_LEFT);
int index = mLayoutManager.getInternalCurrentIndex();
mPagerLayoutManager.onAttach(mLayoutManager.onDetach());
mPagerLayoutManager.setCurrentIndex(index);
mLayoutManager = mPagerLayoutManager;
}
break;
case LAYOUT_TOP_TO_BOTTOM:
ensureScrollLayoutManager();
int index = mLayoutManager.getInternalCurrentIndex();
mScrollLayoutManager.onAttach(mLayoutManager.onDetach());
mScrollLayoutManager.setCurrentIndex(index);
mLayoutManager = mScrollLayoutManager;
break;
}
requestFill();
}
private void setCurrentPageInternal(int page) {
if (mLayoutManager != null) {
mLayoutManager.setCurrentIndex(page);
} else {
mIndex = page;
}
}
private void pageLeftInternal() {
if (mLayoutManager != null) {
mLayoutManager.onPageLeft();
}
}
private void pageRightInternal() {
if (mLayoutManager != null) {
mLayoutManager.onPageRight();
}
}
private void setScaleModeInternal(int scaleMode) {
mScaleMode = scaleMode;
if (mPagerLayoutManager != null) {
mPagerLayoutManager.setScaleMode(scaleMode);
}
}
private void setStartPositionInternal(int startPosition) {
mStartPosition = startPosition;
if (mPagerLayoutManager != null) {
mPagerLayoutManager.setStartPosition(startPosition);
}
}
@RenderThread
void forceFill() {
mRequestFill = true;
fill();
}
@RenderThread
private void fill() {
GalleryUtils.assertInRenderThread();
if (!mRequestFill) {
return;
}
// Disable request layout
mEnableRequestFill = false;
if (mLayoutManager != null) {
mLayoutManager.onFill();
}
mEnableRequestFill = true;
mRequestFill = false;
}
private void dispatchMethod() {
List<Integer> methodListTemp = mMethodListTemp;
List<Object[]> argsListTemp = mArgsListTemp;
synchronized (this) {
if (mMethodList.isEmpty()) {
return;
}
methodListTemp.addAll(mMethodList);
argsListTemp.addAll(mArgsList);
mMethodList.clear();
mArgsList.clear();
}
for (int i = 0, n = methodListTemp.size(); i < n; i++) {
int method = methodListTemp.get(i);
Object[] args = argsListTemp.get(i);
switch (method) {
case METHOD_ON_SINGLE_TAP_UP:
onSingleTapUpInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_SINGLE_TAP_CONFIRMED:
onSingleTapConfirmedInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_DOUBLE_TAP:
onDoubleTapInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_DOUBLE_TAP_CONFIRMED:
onDoubleTapConfirmedInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_LONG_PRESS:
onLongPressInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_SCROLL:
onScrollInternal((Float) args[0], (Float) args[1], (Float) args[2],
(Float) args[3], (Float) args[4], (Float) args[5]);
break;
case METHOD_ON_FLING:
onFlingInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_SCALE_BEGIN:
onScaleBeginInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_SCALE:
onScaleInternal((Float) args[0], (Float) args[1], (Float) args[2]);
break;
case METHOD_ON_SCALE_END:
onScaleEndInternal();
break;
case METHOD_ON_DOWN:
onDownInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_UP:
onUpInternal();
break;
case METHOD_ON_POINTER_DOWN:
onPointerDownInternal((Float) args[0], (Float) args[1]);
break;
case METHOD_ON_POINTER_UP:
onPointerUpInternal();
break;
case METHOD_SET_LAYOUT_MODE:
setLayoutModeInternal((Integer) args[0]);
break;
case METHOD_SET_CURRENT_PAGE:
setCurrentPageInternal((Integer) args[0]);
break;
case METHOD_PAGE_LEFT:
pageLeftInternal();
break;
case METHOD_PAGE_RIGHT:
pageRightInternal();
break;
case METHOD_SET_SCALE_MODE:
setScaleModeInternal((Integer) args[0]);
break;
case METHOD_SET_START_POSITION:
setStartPositionInternal((Integer) args[0]);
break;
case METHOD_ON_ATTACH_TO_ROOT:
onAttachToRootInternal();
break;
case METHOD_SET_PAGER_INTERVAL:
setPagerIntervalInternal((Integer) args[0]);
break;
case METHOD_SET_SCROLL_INTERVAL:
setScrollIntervalInternal((Integer) args[0]);
break;
}
}
methodListTemp.clear();
argsListTemp.clear();
}
@Override
public void render(GLCanvas canvas) {
mWillFill = true;
int oldCurrentIndex = mCurrentIndex.get();
// Dispatch method
dispatchMethod();
long time = AnimationTime.get();
if (mLayoutManager != null && mLayoutManager.onUpdateAnimation(time)) {
invalidate();
}
fill();
mWillFill = false;
super.render(canvas);
int newCurrentIndex;
if (mLayoutManager != null) {
newCurrentIndex = mLayoutManager.getCurrentIndex();
} else {
newCurrentIndex = GalleryPageView.INVALID_INDEX;
}
mCurrentIndex.lazySet(newCurrentIndex);
if (oldCurrentIndex != newCurrentIndex && mListener != null) {
mListener.onUpdateCurrentIndex(newCurrentIndex);
}
}
@RenderThread
public GalleryPageView findPageByIndex(int id) {
if (mLayoutManager != null) {
return mLayoutManager.findPageByIndex(id);
} else {
return null;
}
}
GalleryPageView obtainPage() {
GalleryPageView page = mGalleryPageViewPool.pop();
if (page == null) {
page = new GalleryPageView(mPageTextTexture,
mProgressColor, mBackgroundColor, mProgressSize,
mPageMinHeight, mPageInfoInterval);
}
return page;
}
void releasePage(GalleryPageView page) {
mGalleryPageViewPool.push(page);
}
/**
* Indeterminate GLProgressView
*/
GLProgressView obtainProgress() {
GLProgressView progress;
if (mProgressCache != null) {
progress = mProgressCache;
mProgressCache = null;
} else {
progress = new GLProgressView();
progress.setColor(mProgressColor);
progress.setBgColor(mBackgroundColor);
progress.setIndeterminate(true);
progress.setMinimumWidth(mProgressSize);
progress.setMinimumHeight(mProgressSize);
}
return progress;
}
/**
* @param progress Indeterminate GLProgressView
*/
void releaseProgress(GLProgressView progress) {
mProgressCache = progress;
}
GLTextureView obtainErrorView() {
GLTextureView errorView;
if (mErrorViewCache != null) {
errorView = mErrorViewCache;
mErrorViewCache = null;
} else {
errorView = new GLTextureView();
}
return errorView;
}
void unbindErrorView(GLTextureView errorView) {
Texture texture = errorView.getTexture();
if (texture != null) {
errorView.setTexture(null);
if (texture instanceof BasicTexture) {
((BasicTexture) texture).recycle();
}
}
}
void bindErrorView(GLTextureView errorView, String error) {
unbindErrorView(errorView);
Texture texture = StringTexture.newInstance(error, mErrorTextSize, mErrorTextColor);
errorView.setTexture(texture);
}
void releaseErrorView(GLTextureView errorView) {
unbindErrorView(errorView);
mErrorViewCache = errorView;
}
@IntDef({LAYOUT_LEFT_TO_RIGHT, LAYOUT_RIGHT_TO_LEFT, LAYOUT_TOP_TO_BOTTOM})
@Retention(RetentionPolicy.SOURCE)
public @interface LayoutMode {
}
@IntDef({SCALE_ORIGIN, SCALE_FIT_WIDTH, SCALE_FIT_HEIGHT, SCALE_FIT, SCALE_FIXED})
@Retention(RetentionPolicy.SOURCE)
public @interface ScaleMode {
}
@IntDef({START_POSITION_TOP_LEFT, START_POSITION_TOP_RIGHT, START_POSITION_BOTTOM_LEFT,
START_POSITION_BOTTOM_RIGHT, START_POSITION_CENTER})
@Retention(RetentionPolicy.SOURCE)
public @interface StartPosition {
}
public interface Listener {
@RenderThread
void onUpdateCurrentIndex(int index);
@RenderThread
void onTapSliderArea();
@RenderThread
void onTapMenuArea();
@RenderThread
void onTapErrorText(int index);
@RenderThread
void onLongPressPage(int index);
}
public static class Builder {
private final Context mContext;
private final Adapter mAdapter;
private Listener mListener;
private int mLayoutMode = LAYOUT_LEFT_TO_RIGHT;
private int mScaleMode = SCALE_FIT;
private int mStartPosition = START_POSITION_TOP_LEFT;
private int mStartPage = 0;
private int mBackgroundColor = Color.BLACK;
private int mEdgeColor = Color.WHITE;
private int mPagerInterval = 48;
private int mScrollInterval = 24;
private int mPageMinHeight = 256;
private int mPageInfoInterval = 24;
private int mProgressColor = Color.WHITE;
private int mProgressSize = 56;
private int mPageTextColor = Color.WHITE;
private int mPageTextSize = 56;
private Typeface mPageTextTypeface = Typeface.DEFAULT;
private int mErrorTextColor = Color.RED;
private int mErrorTextSize = 24;
private String mDefaultErrorString = "Error";
private String mEmptyString = "Empty";
public Builder(@NonNull Context context, @NonNull Adapter adapter) {
mContext = context;
mAdapter = adapter;
}
public Builder setListener(Listener listener) {
mListener = listener;
return this;
}
public Builder setLayoutMode(@LayoutMode int layoutMode) {
mLayoutMode = layoutMode;
return this;
}
public Builder setScaleMode(@ScaleMode int scaleMode) {
mScaleMode = scaleMode;
return this;
}
public Builder setStartPosition(@StartPosition int startPosition) {
mStartPosition = startPosition;
return this;
}
public Builder setStartPage(int startPage) {
mStartPage = startPage;
return this;
}
public Builder setBackgroundColor(int backgroundColor) {
mBackgroundColor = backgroundColor;
return this;
}
public Builder setPagerInterval(int pagerInterval) {
mPagerInterval = pagerInterval;
return this;
}
public Builder setScrollInterval(int scrollInterval) {
mScrollInterval = scrollInterval;
return this;
}
public Builder setPageMinHeight(int pageMinHeight) {
mPageMinHeight = pageMinHeight;
return this;
}
public Builder setPageInfoInterval(int pageInfoInterval) {
mPageInfoInterval = pageInfoInterval;
return this;
}
public Builder setProgressColor(int progressColor) {
mProgressColor = progressColor;
return this;
}
public Builder setProgressSize(int progressSize) {
mProgressSize = progressSize;
return this;
}
public Builder setPageTextColor(int pageTextColor) {
mPageTextColor = pageTextColor;
return this;
}
public Builder setPageTextSize(int pageTextSize) {
mPageTextSize = pageTextSize;
return this;
}
public Builder setPageTextTypeface(Typeface pageTextTypeface) {
mPageTextTypeface = pageTextTypeface;
return this;
}
public Builder setErrorTextColor(int errorTextColor) {
mErrorTextColor = errorTextColor;
return this;
}
public Builder setErrorTextSize(int errorTextSize) {
mErrorTextSize = errorTextSize;
return this;
}
public Builder setDefaultErrorString(String defaultErrorString) {
mDefaultErrorString = defaultErrorString;
return this;
}
public Builder setEmptyString(String emptyString) {
mEmptyString = emptyString;
return this;
}
public GalleryView build() {
return new GalleryView(this);
}
}
public static abstract class Adapter {
protected GalleryView mGalleryView;
private void setGalleryView(@NonNull GalleryView galleryView) {
mGalleryView = galleryView;
}
public void bind(GalleryPageView view, int index) {
onBind(view, index);
view.setIndex(index);
}
public void unbind(GalleryPageView view) {
onUnbind(view, view.getIndex());
view.setIndex(GalleryPageView.INVALID_INDEX);
}
public abstract void onBind(GalleryPageView view, int index);
public abstract void onUnbind(GalleryPageView view, int index);
/**
* @return Null for no error
*/
public abstract String getError();
public abstract int size();
}
public static abstract class LayoutManager {
protected GalleryView mGalleryView;
public LayoutManager(@NonNull GalleryView galleryView) {
mGalleryView = galleryView;
}
public abstract void onAttach(Adapter iterator);
public abstract Adapter onDetach();
public abstract void onFill();
public abstract void onDown();
public abstract void onUp();
public abstract void onDoubleTapConfirmed(float x, float y);
public abstract void onLongPress(float x, float y);
public abstract void onScroll(float dx, float dy, float totalX, float totalY, float x, float y);
public abstract void onFling(float velocityX, float velocityY);
public abstract boolean canScale();
public abstract void onScale(float focusX, float focusY, float scale);
public abstract boolean onUpdateAnimation(long time);
public abstract void onDataChanged();
public abstract void onPageLeft();
public abstract void onPageRight();
public abstract boolean isTapOrPressEnable();
public abstract GalleryPageView findPageByIndex(int index);
/**
* @return {@link GalleryPageView#INVALID_INDEX} for error
*/
public abstract int getCurrentIndex();
public abstract void setCurrentIndex(int index);
public abstract int getIndexUnder(float x, float y);
abstract int getInternalCurrentIndex();
protected void placeCenter(GLView view) {
int spec = GLView.MeasureSpec.makeMeasureSpec(GLView.LayoutParams.WRAP_CONTENT,
GLView.LayoutParams.WRAP_CONTENT);
view.measure(spec, spec);
int viewWidth = view.getMeasuredWidth();
int viewHeight = view.getMeasuredHeight();
int viewLeft = mGalleryView.getWidth() / 2 - viewWidth / 2;
int viewTop = mGalleryView.getHeight() / 2 - viewHeight / 2;
view.layout(viewLeft, viewTop, viewLeft + viewWidth, viewTop + viewHeight);
}
}
}