Files
EhViewer/src/com/hippo/ehviewer/gallery/GalleryView.java
T

1234 lines
43 KiB
Java

package com.hippo.ehviewer.gallery;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.MotionEvent;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.gallery.data.ImageSet;
import com.hippo.ehviewer.gallery.glrenderer.BitmapTexture;
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
import com.hippo.ehviewer.gallery.glrenderer.GLPaint;
import com.hippo.ehviewer.gallery.glrenderer.StringTexture;
import com.hippo.ehviewer.gallery.ui.GLView;
import com.hippo.ehviewer.gallery.ui.GestureRecognizer;
import com.hippo.ehviewer.gallery.util.GalleryUtils;
import com.hippo.ehviewer.util.Config;
import com.hippo.ehviewer.util.TimeRunner;
import com.hippo.ehviewer.util.Ui;
// TODO 手动触屏滑动可能闪烁
public class GalleryView extends GLView {
@SuppressWarnings("unused")
private static final String TAG = "ImagesView";
public static final int INVALID_ID = -1;
private static final int STATE_FIRST = 0x1;
private static final int STATE_LAST = 0x2;
private static final int STATE_NONE = 0x0;
// scale mode
private static final int ORGIN = 0x0;
private static final int FIT_WIDTH = 0x1;
private static final int FIT_HEIGHT = 0x2;
private static final int FIT = 0x3;
private static final int FIXED = 0x4;
// start position mode
private static final int TOP_LEFT = 0x0;
private static final int TOP_RIGHT = 0x1;
private static final int BOTTOM_LEFT = 0x2;
private static final int BOTTOM_RIGHT = 0x3;
private static final int CENTER = 0x4;
// Scroll state
private static final int SCROLL_NONE = 0x0;
private static final int SCROLL_LEFT = 0x1;
private static final int SCROLL_RIGHT = 0x2;
private static final int SCROLL_ANIME_LEFT = 0x3;
private static final int SCROLL_ANIME_RIGHT = 0x4;
private static final int PRE_TARGET_INDEX = 0;
private static final int CUR_TARGET_INDEX = 1;
private static final int NEXT_TARGET_INDEX = 2;
private static final int TARGET_INDEX_SIZE = 3;
// Area
private static final float[] LEFT_AREA_SCALE = {0, 0, 2/7.0f, 1};
private static final float[] TOP_AREA_SCALE = {2/7.0f, 0, 5/7.0f, 3/8.0f};
private static final float[] RIGHT_AREA_SCALE = {5/7.0f, 0, 1, 1};
private static final float[] BOTTOM_AREA_SCALE = {2/7.0f, 5/8.0f, 5/7.0f, 1};
private static final float[] CENTER_AREA_SCALE = {2/7.0f, 3/8.0f, 5/7.0f, 5/8.0f};
private static final float MILLSEC_PER_DIX = 0.2f;
private static final float CHANGE_PAGE_PROPORTION = 0.05f;
// TODO in dip
private static final float CHANGE_PAGE_OFFSET = 20;
// TODO 根据界面的大小调整缩放界限
private static final float SCALE_MIN = 1/4.0f;
private static final float SCALE_MAX = 4;
private static final float LINE_WIDTH = Ui.dp2pix(3);
private static final int LINE_COLOR = -1; // White
private static final int TAP_AREA_TEXT_COLOR = -1; // White
private static final int TAP_AREA_TEXT_SIZE = Ui.dp2pix(24);
private static final int TAP_AREA_MASK_COLOR = 0x88000000;
// TODO
private static final int MASK_COLOR = 0x88000000;
private final GestureRecognizer mGestureRecognizer;
private Context mContext;
private ImageSet mImageSet;
private int mState;
// scale and scroll only can choose one
private boolean isScale = false;
private int scaleMode;
private int startMode;
private int mScrollState = SCROLL_NONE;
private int scrollXOffset = 0;
private int scrollYOffset = 0;
private int stopScrollXOffset = 0;
@SuppressWarnings("unused")
private int stopScrollYOffset = 0;
private float mScale = 1;
private boolean mShowEdegTip = true;
private int mWidth = -1;
private int mHeight = -1;
// at most keep three item
private ShowItem[] showItems;
private int mCurIndex;
private int mSize;
private boolean mInit = false;
private int[] leftArea;
private int[] topArea;
private int[] rightArea;
private int[] bottomArea;
private int[] centerArea;
private Text leftText;
private Text topText;
private Text rightText;
private Text bottomText;
private Text centerText;
private boolean mShowTapArea = false;
private boolean mShowTapAreaTurn = false;
private OnEdgeListener mOnEdgeListener;
private OnTapTextListener mOnTapTextListener;
private OnScrollPageListener mOnScrollPageListener;
public interface OnEdgeListener {
public void onFirstPageEdge();
public void onLastPageEdge();
}
public interface OnTapTextListener {
public void onTapText(int index);
}
public interface OnScrollPageListener {
public void onScrollPage(int index);
}
public void setOnEdgeListener(OnEdgeListener l) {
mOnEdgeListener = l;
}
public void setOnTapTextListener(OnTapTextListener l) {
mOnTapTextListener = l;
}
public void setOnScrollPageListener(OnScrollPageListener l) {
mOnScrollPageListener = l;
}
public GalleryView(Context context, ImageSet imageSet, int startIndex) {
mContext = context;
mImageSet = imageSet;
mSize = imageSet.getSize();
mCurIndex = startIndex;
// adjust mCurIndex
if (mCurIndex < 0 || mCurIndex >= mSize)
mCurIndex = 0;
setState();
mGestureRecognizer = new GestureRecognizer(mContext, new MyGestureListener());
setBackgroundColor(GalleryUtils.intColorToFloatARGBArray(Color.BLACK));
showItems = new ShowItem[TARGET_INDEX_SIZE];
// Init config
scaleMode = Config.getPageScalingMode();
startMode = Config.getStartPosition();
//
imageSet.setOnStateChangeListener(new ImageSet.OnStateChangeListener() {
@Override
public void onStateChange(int index, int state) {
int targetIndex = index - mCurIndex + 1;
if (targetIndex >= PRE_TARGET_INDEX
&& targetIndex <= NEXT_TARGET_INDEX) {
loadImage(index);
}
}
});
}
private void setState() {
mState = STATE_NONE;
if (mCurIndex == 0)
mState |= STATE_FIRST;
if (mCurIndex == mSize - 1)
mState |= STATE_LAST;
}
private void drawTapArea(GLCanvas canvas) {
// Background
canvas.fillRect(0, 0, mWidth, mHeight, TAP_AREA_MASK_COLOR);
drawRect(canvas, leftArea);
drawRect(canvas, topArea);
drawRect(canvas, rightArea);
drawRect(canvas, bottomArea);
drawRect(canvas, centerArea);
leftText.draw(canvas);
topText.draw(canvas);
rightText.draw(canvas);
bottomText.draw(canvas);
centerText.draw(canvas);
}
private void drawRect(GLCanvas canvas, int[] rect) {
int left = rect[0];
int top = rect[1];
int right = rect[2];
int bottom = rect[3];
GLPaint paint = new GLPaint(LINE_COLOR, LINE_WIDTH);
canvas.drawLine(left, top, right, top, paint);
canvas.drawLine(left, bottom, right, bottom, paint);
canvas.drawLine(left, top, left, bottom, paint);
canvas.drawLine(right, top, right, bottom, paint);
}
@Override
protected void render(GLCanvas canvas) {
super.render(canvas);
ShowItem item;
switch (mScrollState) {
case SCROLL_NONE:
item = showItems[CUR_TARGET_INDEX];
if (item != null) {
item.draw(canvas);
}
break;
case SCROLL_LEFT:
case SCROLL_ANIME_LEFT:
item = showItems[PRE_TARGET_INDEX];
if (item != null)
item.draw(canvas, scrollXOffset, scrollYOffset);
item = showItems[CUR_TARGET_INDEX];
if (item != null)
item.draw(canvas, scrollXOffset, scrollYOffset);
break;
case SCROLL_RIGHT:
case SCROLL_ANIME_RIGHT:
item = showItems[CUR_TARGET_INDEX];
if (item != null)
item.draw(canvas, scrollXOffset, scrollYOffset);
item = showItems[NEXT_TARGET_INDEX];
if (item != null)
item.draw(canvas, scrollXOffset, scrollYOffset);
break;
}
// Add mInit to make show the leftArea or other is not null
if (mShowTapArea && mInit)
drawTapArea(canvas);
// Mask to reduce brightness
//canvas.fillRect(0, 0, mWidth, mHeight, MASK_COLOR);
}
private void resetSizePosition() {
resetSizePosition(PRE_TARGET_INDEX);
resetSizePosition(CUR_TARGET_INDEX);
resetSizePosition(NEXT_TARGET_INDEX);
}
private void resetSizePosition(int targeIndex) {
ShowItem showItem = showItems[targeIndex];
if (showItem == null)
return;
int sumXOffset;
switch (targeIndex) {
case PRE_TARGET_INDEX:
sumXOffset = -mWidth;
break;
case NEXT_TARGET_INDEX:
sumXOffset = mWidth;
break;
case CUR_TARGET_INDEX:
default:
sumXOffset = 0;
break;
}
if (showItem instanceof Text) {
Text text = (Text)showItem;
int xOffset;
int yOffset;
Rect rect = text.mRect;
xOffset = (mWidth - text.width)/2;
yOffset = (mHeight - text.height)/2;
rect.left = sumXOffset + xOffset;
rect.top = yOffset;
rect.right = rect.left + text.width;
rect.bottom = rect.top + text.height;
return;
}
if (showItem instanceof Image) {
Image image = (Image)showItem;
// Set scale
int showWidth = 0;
int showHeight = 0;
Rect rect = image.mRect;
switch (scaleMode) {
case ORGIN:
image.imageScale = 1;
showWidth = image.width;
showHeight = image.height;
break;
case FIT_WIDTH:
image.imageScale = (float)mWidth/image.width;
showWidth = mWidth;
showHeight = (int)(image.height * image.imageScale);
break;
case FIT_HEIGHT:
image.imageScale = (float)mHeight/image.height;
showWidth = (int)(image.width * image.imageScale);
showHeight = mHeight;
break;
case FIT:
float scaleX = (float)mWidth/image.width;
float scaleY = (float)mHeight/image.height;
if (scaleX < scaleY) {
image.imageScale = scaleX;
showWidth = mWidth;
showHeight = (int)(image.height * image.imageScale);
} else {
image.imageScale = scaleY;
showWidth = (int)(image.width * image.imageScale);
showHeight = mHeight;
break;
}
break;
case FIXED:
default:
image.imageScale = mScale;
showWidth = (int)(image.width * mScale);
showHeight = (int)(image.height * mScale);
break;
}
// adjust scale
if (image.imageScale < SCALE_MIN) {
image.imageScale = SCALE_MIN;
showWidth = (int)(image.width * SCALE_MIN);
showHeight = (int)(image.height * SCALE_MIN);
} else if (image.imageScale > SCALE_MAX) {
image.imageScale = SCALE_MAX;
showWidth = (int)(image.width * SCALE_MAX);
showHeight = (int)(image.height * SCALE_MAX);
}
// set start position
int xOffset;
int yOffset;
switch (startMode) {
case TOP_LEFT:
xOffset = 0;
yOffset = 0;
break;
case TOP_RIGHT:
xOffset = mWidth - showWidth;
yOffset = 0;
break;
case BOTTOM_LEFT:
xOffset = 0;
yOffset = mHeight - showHeight;
break;
case BOTTOM_RIGHT:
xOffset = mWidth - showWidth;
yOffset = mHeight - showHeight;
break;
case CENTER:
default:
xOffset = (mWidth - showWidth)/2;
yOffset = (mHeight - showHeight)/2;
break;
}
rect.left = sumXOffset + xOffset;
rect.right = rect.left + showWidth;
rect.top = yOffset;
rect.bottom = rect.top + showHeight;
// adjust position
adjustPosition(image);
}
invalidate();
}
private void setCenterInArea(int[] area, ShowItem showItem) {
int xOffset = ((area[2] - area[0]) - showItem.width)/2;
int yOffset = ((area[3] - area[1]) - showItem.height)/2;
Rect rect = showItem.mRect;
rect.left = area[0] + xOffset;
rect.right = rect.left + showItem.width;
rect.top = area[1] + yOffset;
rect.bottom = rect.top + showItem.height;
}
private void setTapArea() {
leftArea = new int[]{
(int)(LEFT_AREA_SCALE[0] * mWidth),
(int)(LEFT_AREA_SCALE[1] * mHeight),
(int)(LEFT_AREA_SCALE[2] * mWidth),
(int)(LEFT_AREA_SCALE[3] * mHeight)};
topArea = new int[]{
(int)(TOP_AREA_SCALE[0] * mWidth),
(int)(TOP_AREA_SCALE[1] * mHeight),
(int)(TOP_AREA_SCALE[2] * mWidth),
(int)(TOP_AREA_SCALE[3] * mHeight)};
rightArea = new int[]{
(int)(RIGHT_AREA_SCALE[0] * mWidth),
(int)(RIGHT_AREA_SCALE[1] * mHeight),
(int)(RIGHT_AREA_SCALE[2] * mWidth),
(int)(RIGHT_AREA_SCALE[3] * mHeight)};
bottomArea = new int[]{
(int)(BOTTOM_AREA_SCALE[0] * mWidth),
(int)(BOTTOM_AREA_SCALE[1] * mHeight),
(int)(BOTTOM_AREA_SCALE[2] * mWidth),
(int)(BOTTOM_AREA_SCALE[3] * mHeight)};
centerArea = new int[]{
(int)(CENTER_AREA_SCALE[0] * mWidth),
(int)(CENTER_AREA_SCALE[1] * mHeight),
(int)(CENTER_AREA_SCALE[2] * mWidth),
(int)(CENTER_AREA_SCALE[3] * mHeight)};
if (leftText == null)
leftText = new Text(mContext.getString(R.string.pre_page),
TAP_AREA_TEXT_SIZE, TAP_AREA_TEXT_COLOR);
if (topText == null)
topText = new Text(mContext.getString(R.string.zoom_in),
TAP_AREA_TEXT_SIZE, TAP_AREA_TEXT_COLOR);
if (rightText == null)
rightText = new Text(mContext.getString(R.string.next_page),
TAP_AREA_TEXT_SIZE, TAP_AREA_TEXT_COLOR);
if (bottomText == null)
bottomText = new Text(mContext.getString(R.string.zoom_out),
TAP_AREA_TEXT_SIZE, TAP_AREA_TEXT_COLOR);
if (centerText == null)
centerText = new Text(mContext.getString(R.string.menu),
TAP_AREA_TEXT_SIZE, TAP_AREA_TEXT_COLOR);
setCenterInArea(leftArea, leftText);
setCenterInArea(topArea, topText);
setCenterInArea(rightArea, rightText);
setCenterInArea(bottomArea, bottomText);
setCenterInArea(centerArea, centerText);
}
@Override
protected void onLayout(
boolean changeSize, int left, int top, int right, int bottom) {
mWidth = right - left;
mHeight = bottom - top;
if (!mInit) {
mInit = true;
loadImage(mCurIndex);
if ((mState & STATE_LAST) == 0)
loadImage(mCurIndex + 1);
if ((mState & STATE_FIRST) == 0)
loadImage(mCurIndex - 1);
} else {
resetSizePosition();
}
setTapArea();
invalidate();
}
private void loadImage(int index) {
int targetIndex = index - mCurIndex + 1;
int state = mImageSet.getImage(index, new DecodeImageListener());
ShowItem showItem = showItems[targetIndex];
if (showItem != null)
showItem.recycle();
if (state == ImageSet.STATE_LOADED)
showItems[targetIndex] = new Image();
else {
showItems[targetIndex] = new Text(getErrorStateString(state));
}
resetSizePosition(targetIndex);
}
private String getErrorStateString(int state) {
switch (state) {
case ImageSet.INVALID_ID:
return "无效的 index";
case ImageSet.STATE_NONE:
return "尚未加载";
case ImageSet.STATE_LOADING:
return "正在加载";
case ImageSet.STATE_FAIL:
return "加载失败";
default:
return "错误的代码";
}
}
/**
* If cur page is first page return true
* @return
*/
private boolean isFirstPage() {
return (mState & STATE_FIRST) != 0;
}
/**
* If cur page is last page return true
* @return
*/
private boolean isLastPage() {
return (mState & STATE_LAST) != 0;
}
class DecodeImageListener implements ImageSet.OnDecodeOverListener {
@Override
public void onDecodeOver(Bitmap bmp, int index) {
int targetIndex = index - mCurIndex + 1;
ShowItem showItem;
Image image;
if (targetIndex < 0 || targetIndex > 2
|| !((showItem = showItems[targetIndex]) instanceof Image)
|| (image = (Image)showItem).isLoaded()) {// If it do not need any more
if (bmp != null)
bmp.recycle();
} else {
if (bmp == null) {
showItems[targetIndex] = new Text("读取图片错误");
} else {
image.load(bmp);
}
resetSizePosition(targetIndex);
}
}
}
/**
*
* @param mode
* true zoom in, false zoom out
* @return
*/
private boolean zoom(boolean mode) {
ShowItem curShowItem;
curShowItem = showItems[CUR_TARGET_INDEX];
if (curShowItem == null || !(curShowItem instanceof Image))
return false;
Image image = (Image)curShowItem;
float newScale;
if (mode) {
newScale = image.imageScale * 1.1f;
if (newScale == SCALE_MAX)
return false;
if (newScale > SCALE_MAX)
newScale = SCALE_MAX;
}
else {
newScale = image.imageScale * 0.9f;
if (newScale == SCALE_MIN)
return false;
if (newScale < SCALE_MIN)
newScale = SCALE_MIN;
}
image.imageScale = newScale;
mScale = newScale;
Rect rect = image.mRect;
int width = (int)(image.width * newScale);
int height = (int)(image.height * newScale);
int xOffset = (width - rect.width())/2;
int yOffset = (height - rect.height())/2;
rect.set(rect.left - xOffset, rect.top - yOffset, rect.right + xOffset, rect.bottom + yOffset);
adjustPosition(curShowItem);
invalidate();
return true;
}
/**
* You'd better resetSizePosition(PRE_TARGET_INDEX) before
* @return
*/
private boolean goToPrePage() {
if (isFirstPage())
return false;
ShowItem showItem;
showItem = showItems[NEXT_TARGET_INDEX];
if (showItem != null)
showItem.recycle();
showItems[NEXT_TARGET_INDEX] = showItems[CUR_TARGET_INDEX];
showItems[CUR_TARGET_INDEX] = showItems[PRE_TARGET_INDEX];
showItems[PRE_TARGET_INDEX] = null;
// adjust rect
showItem = showItems[NEXT_TARGET_INDEX];
if (showItem != null) {
showItem.mRect.offset(mWidth, 0);
}
showItem = showItems[CUR_TARGET_INDEX];
if (showItem != null) {
showItem.mRect.offset(mWidth, 0);
}
mCurIndex--;
setState();
loadImage(mCurIndex-1);
if (mOnScrollPageListener != null) {
mOnScrollPageListener.onScrollPage(mCurIndex);
}
invalidate();
return true;
}
/**
* You'd better resetSizePosition(NEXT_TARGET_INDEX) before
* @return
*/
private boolean goToNextPage() {
if (isLastPage())
return false;
ShowItem showItem;
showItem = showItems[PRE_TARGET_INDEX];
if (showItem != null)
showItem.recycle();
showItems[PRE_TARGET_INDEX] = showItems[CUR_TARGET_INDEX];
showItems[CUR_TARGET_INDEX] = showItems[NEXT_TARGET_INDEX];
showItems[NEXT_TARGET_INDEX] = null;
// adjust rect
showItem = showItems[PRE_TARGET_INDEX];
if (showItem != null) {
showItem.mRect.offset(-mWidth, 0);
}
showItem = showItems[CUR_TARGET_INDEX];
if (showItem != null) {
showItem.mRect.offset(-mWidth, 0);
}
mCurIndex++;
setState();
loadImage(mCurIndex+1);
if (mOnScrollPageListener != null) {
mOnScrollPageListener.onScrollPage(mCurIndex);
}
invalidate();
return true;
}
@Override
protected boolean onTouch(MotionEvent event) {
mGestureRecognizer.onTouchEvent(event);
return true;
}
private class MyGestureListener implements GestureRecognizer.Listener {
public MyGestureListener() {
mToPreTimeRunner.setOnTimerListener(new TimeRunner.OnTimeListener() {
@Override
public void onStart() {}
@Override
public void onEnd() {
scrollXOffset = 0;
goToPrePage();
mScrollState = SCROLL_NONE;
}
});
mToNextTimeRunner.setOnTimerListener(new TimeRunner.OnTimeListener() {
@Override
public void onStart() {}
@Override
public void onEnd() {
scrollXOffset = 0;
goToNextPage();
mScrollState = SCROLL_NONE;
}
});
mReturnTimeRunner.setOnTimerListener(new TimeRunner.OnTimeListener() {
@Override
public void onStart() {}
@Override
public void onEnd() {
scrollXOffset = 0;
mScrollState = SCROLL_NONE;
invalidate();
}
});
}
private boolean isInArea(int[] area, int x, int y) {
if (area.length != 4)
throw new IllegalArgumentException(
"area's length should be 4, but it's length is " + area.length);
if (x >= area[0] && x < area[2] && y >= area[1] && y < area[3])
return true;
else
return false;
}
@Override
public boolean onSingleTapUp(float x, float y) {
if (mScrollState != SCROLL_NONE || mShowTapAreaTurn)
return false;
if ( showItems[CUR_TARGET_INDEX] == null
|| showItems[CUR_TARGET_INDEX] instanceof Text) {
if (mOnTapTextListener != null)
mOnTapTextListener.onTapText(mCurIndex);
return true;
}
if (isInArea(leftArea, (int)x, (int)y)) {
// TODO goto bottom first the to pre page
resetSizePosition(PRE_TARGET_INDEX);
if (!goToPrePage() && mOnEdgeListener != null)
mOnEdgeListener.onFirstPageEdge();
} else if (isInArea(topArea, (int)x, (int)y)) {
zoom(true);
} else if (isInArea(rightArea, (int)x, (int)y)) {
// TODO goto bottom first the to pre page
resetSizePosition(NEXT_TARGET_INDEX);
if (!goToNextPage() && mOnEdgeListener != null)
mOnEdgeListener.onLastPageEdge();
} else if (isInArea(bottomArea, (int)x, (int)y)) {
zoom(false);
} else if (isInArea(centerArea, (int)x, (int)y)) {
mShowTapArea = true;
mShowTapAreaTurn = true;
invalidate();
} else {
// Can't catch tap
}
return true;
}
@Override
public boolean onDoubleTap(float x, float y) {
return true;
}
@Override
public boolean onScrollBegin(float dx, float dy, float totalX,
float totalY) {
return this.onScroll(dx, dy, totalX, totalY);
}
/**
* dx 和 totalX 符号相反,为啥
*/
@Override
public boolean onScroll(float dx, float dy, float totalX, float totalY) {
if (isScale)
return false;
ShowItem curShowItem = showItems[CUR_TARGET_INDEX];
switch(mScrollState) {
case SCROLL_ANIME_LEFT:
case SCROLL_ANIME_RIGHT:
return false;
case SCROLL_NONE:
boolean changePage = false;
Rect rect = curShowItem.mRect;
// Check change page or not
if (curShowItem == null || !(curShowItem instanceof Image))
changePage = true;
else{
if ( Math.abs(totalX/totalY) > 1 && ((totalX > CHANGE_PAGE_OFFSET && dx < 0 && rect.left >= 0)
|| (totalX < -CHANGE_PAGE_OFFSET && dx > 0 && rect.right <= mWidth)))
changePage = true;
}
if (changePage) { // If change page
if (dx < 0) { // Go to left
if (!isFirstPage()) { // Not first page
scrollXOffset = 0;
mScrollState = SCROLL_LEFT;
resetSizePosition(PRE_TARGET_INDEX);
} else {
changePage = false;
if (mShowEdegTip) { // First page
mShowEdegTip = false;
if (mOnEdgeListener != null)
mOnEdgeListener.onFirstPageEdge();
}
}
}
else { // Go to righ
if (!isLastPage()) { // Not last page
scrollXOffset = 0;
mScrollState = SCROLL_RIGHT;
resetSizePosition(NEXT_TARGET_INDEX);
} else {
changePage = false;
if (mShowEdegTip) { // last page
mShowEdegTip = false;
if (mOnEdgeListener != null)
mOnEdgeListener.onLastPageEdge();
}
}
}
}
if (!changePage){ // Move cur image
int actDx = -(int)dx;
int actDy = -(int)dy;
if (rect.width() <= mWidth)
actDx = 0;
if (rect.height() <= mHeight)
actDy = 0;
rect.offset(actDx, actDy);
// Fix position
adjustPosition(curShowItem);
}
break;
case SCROLL_LEFT:
scrollXOffset -= dx;
if (scrollXOffset <= 0) {
scrollXOffset = 0;
mScrollState = SCROLL_NONE;
}
break;
case SCROLL_RIGHT:
scrollXOffset -= dx;
if (scrollXOffset >= 0) {
scrollXOffset = 0;
mScrollState = SCROLL_NONE;
}
break;
}
invalidate();
return true;
}
@Override
public boolean onScrollEnd() {
if (isScale)
return false;
switch(mScrollState) {
case SCROLL_ANIME_LEFT:
case SCROLL_ANIME_RIGHT:
return false;
case SCROLL_LEFT:
stopScrollXOffset = scrollXOffset;
stopScrollYOffset = scrollYOffset;
mScrollState = SCROLL_ANIME_LEFT;
if (stopScrollXOffset > mWidth * CHANGE_PAGE_PROPORTION) { // Go to pre page
mToPreTimeRunner.setDuration((int)(MILLSEC_PER_DIX * (mWidth-stopScrollXOffset)));
mToPreTimeRunner.start();
} else {
mReturnTimeRunner.setDuration((int)(MILLSEC_PER_DIX * stopScrollXOffset));
mReturnTimeRunner.start();
}
break;
case SCROLL_RIGHT:
stopScrollXOffset = scrollXOffset;
stopScrollYOffset = scrollYOffset;
mScrollState = SCROLL_ANIME_RIGHT;
if (-stopScrollXOffset > mWidth * CHANGE_PAGE_PROPORTION) { // Go to next page
mToNextTimeRunner.setDuration((int)(MILLSEC_PER_DIX * (mWidth+stopScrollXOffset)));
mToNextTimeRunner.start();
} else {
mReturnTimeRunner.setDuration((int)(MILLSEC_PER_DIX * -stopScrollXOffset));
mReturnTimeRunner.start();
}
break;
}
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return true;
}
@Override
public boolean onScaleBegin(float focusX, float focusY) {
isScale = true;
return true;
}
@Override
public boolean onScale(float focusX, float focusY, float scale) {
if (mScrollState == SCROLL_ANIME_LEFT || mScrollState == SCROLL_ANIME_RIGHT)
return true;
if (mScrollState == SCROLL_LEFT || mScrollState == SCROLL_RIGHT) {
scrollXOffset = 0;
scrollYOffset = 0;
mScrollState = SCROLL_NONE;
}
ShowItem curShowItem = showItems[CUR_TARGET_INDEX];
if (curShowItem == null || !(curShowItem instanceof Image))
return true;
Image image = (Image)curShowItem;
float newScale = image.imageScale * scale;
if (newScale > SCALE_MAX || newScale < SCALE_MIN)
return true;
image.imageScale = newScale;
mScale = newScale;
Rect rect = image.mRect;
int left = rect.left;
int top = rect.top;
left = (int)(focusX - ((focusX - left) * scale));
top = (int)(focusY - ((focusY - top) * scale));
rect.set(left, top, (int)(left + (image.width * image.imageScale)), (int)(top + (image.height * image.imageScale)));
// adjust
adjustPosition(image);
invalidate();
return true;
}
@Override
public void onScaleEnd() {
isScale = false;
}
@Override
public void onDown(float x, float y) {
mShowTapArea = false;
invalidate();
}
@Override
public void onUp() {
mShowEdegTip = true;
if (!mShowTapArea)
mShowTapAreaTurn = false;
}
// *** TimeRunner *** //
TimeRunner mToPreTimeRunner = new TimeRunner() {
@Override
protected void run(float interpolatedTime, int runningTime) {
scrollXOffset = (int)(stopScrollXOffset + ((mWidth - stopScrollXOffset) * interpolatedTime));
invalidate();
}
};
TimeRunner mToNextTimeRunner = new TimeRunner() {
@Override
protected void run(float interpolatedTime, int runningTime) {
scrollXOffset = (int)(stopScrollXOffset + ((-mWidth - stopScrollXOffset) * interpolatedTime));
invalidate();
}
};
TimeRunner mReturnTimeRunner = new TimeRunner() {
@Override
protected void run(float interpolatedTime, int runningTime) {
scrollXOffset = (int)((1 - interpolatedTime) * stopScrollXOffset);
invalidate();
}
};
}
/**
* If side is shorter then parent's, make it in parent's center
* If side is longer then parent's, make sure it fill parent
*
* @param showItem
*/
private void adjustPosition(ShowItem showItem) {
Rect rect = showItem.mRect;
int showWidth = rect.width();
int showHeight = rect.height();
int sumXOffset;
int sumYOffset = 0;
int targetIndex = getTargetIndex(showItem);
switch (targetIndex) {
case PRE_TARGET_INDEX:
sumXOffset = -mWidth;
break;
case NEXT_TARGET_INDEX:
sumXOffset = mWidth;
break;
case CUR_TARGET_INDEX:
default:
sumXOffset = 0;
}
if (showWidth > mWidth) {
int fixXOffset = rect.left - sumXOffset;
if (fixXOffset > 0) {
rect.left -= fixXOffset;
rect.right -= fixXOffset;
} else if ((fixXOffset = sumXOffset + mWidth - rect.right) > 0) {
rect.left += fixXOffset;
rect.right += fixXOffset;
}
} else {
int left = sumXOffset + (mWidth - showWidth) / 2;
rect.offsetTo(left, rect.top);
}
if (showHeight > mHeight) {
int fixYOffset = rect.top - sumYOffset;
if (fixYOffset > 0) {
rect.top -= fixYOffset;
rect.bottom -= fixYOffset;
} else if ((fixYOffset = sumYOffset + mHeight - rect.bottom) > 0) {
rect.top += fixYOffset;
rect.bottom += fixYOffset;
}
} else {
int top = sumYOffset + (mHeight - showHeight) / 2;
rect.offsetTo(rect.left, top);
}
}
public int getTargetIndex(ShowItem showItem) {
if (mImageSet == null)
return INVALID_ID;
for (int i = 0; i < TARGET_INDEX_SIZE; i++) {
if (showItem == showItems[i])
return i;
}
return INVALID_ID;
}
private abstract class ShowItem {
protected int width = 1;
protected int height = 1;
public Rect mRect = new Rect();
public void draw(GLCanvas canvas) {
this.draw(canvas, 0, 0);
}
public abstract void draw(GLCanvas canvas, int xOffset, int yOffset);
public abstract void recycle();
}
private class Image extends ShowItem{
private BitmapTexture mTexture;
private Bitmap mContextBmp;
public float imageScale = 1;
public void load(Bitmap bmp) {
if (mTexture != null)
recycle();
mTexture = new BitmapTexture(bmp);
mContextBmp = bmp;
width = bmp.getWidth();
height = bmp.getHeight();
imageScale = 1;
}
public boolean isLoaded() {
return mTexture != null;
}
@Override
public void recycle() {
if (mTexture == null)
return;
mTexture.recycle();
mContextBmp.recycle();
mTexture = null;
mContextBmp = null;
}
@Override
public void draw(GLCanvas canvas, int xOffset, int yOffset) {
if (mTexture != null) {
int targetIndex;
if ((targetIndex = getTargetIndex(this)) == INVALID_ID)
mTexture.draw(canvas, mRect.left + xOffset, mRect.top + yOffset, mRect.width(), mRect.height());
else {
int leftBound = mWidth;
int topBound = 0;
int rightBound = 2 * mWidth;
int bottomBound = mHeight;
switch (targetIndex) {
case PRE_TARGET_INDEX:
leftBound = -leftBound;
rightBound = 0;
break;
case CUR_TARGET_INDEX:
rightBound = leftBound;
leftBound = 0;
break;
case NEXT_TARGET_INDEX:
default:
break;
}
int left = mRect.left;
int top = mRect.top;
int right = mRect.right;
int bottom = mRect.bottom;
// Only show what in the own box
// TODO only what can be seen
if (left < leftBound || top < topBound || right > rightBound || bottom > bottomBound) {
RectF source = new RectF();
RectF target = new RectF();
if (left < leftBound) {
target.left = leftBound;
source.left = (leftBound - left) / imageScale;
} else {
target.left = left;
source.left = 0;
}
if (top < topBound) {
target.top = topBound;
source.top = (topBound - top) / imageScale;
} else {
target.top = top;
source.top = 0;
}
if (right > rightBound) {
target.right = rightBound;
source.right = width - ((right - rightBound) / imageScale);
} else {
target.right = right;
source.right = width;
}
if (bottom > bottomBound) {
target.bottom = bottomBound;
source.bottom = height - ((bottom - bottomBound) / imageScale);
} else {
target.bottom = bottom;
source.bottom = height;
}
target.left += xOffset;
target.top += yOffset;
target.right += xOffset;
target.bottom += yOffset;
mTexture.draw(canvas, source, target);
} else {
mTexture.draw(canvas, left + xOffset, top + yOffset, right - left, bottom - top);
}
}
}
}
}
private class Text extends ShowItem{
private StringTexture mTexture;
public Text(String str) {
load(str);
}
public Text(String str, float size, int color) {
load(str, size, color);
}
public void load(String str) {
load(str, 100, -1);
}
public void load(String str, float size, int color) {
if (mTexture != null)
recycle();
mTexture = StringTexture.newInstance(str, size, color);
width = mTexture.getWidth();
height = mTexture.getHeight();
}
@Override
public void draw(GLCanvas canvas, int xOffset, int yOffset) {
if (mTexture != null)
mTexture.draw(canvas, mRect.left + xOffset, mRect.top + yOffset);
}
@Override
public void recycle() {
if (mTexture == null)
return;
mTexture.recycle();
}
}
}