Improve GalleryView

This commit is contained in:
seven332 committed 2016-02-11 22:14:21 +08:00
1 parent a585c7cd20
commit 293ada12c6
16 files changed
+557 -656

No files matched your search

@@ -25,13 +25,20 @@ import android.view.MotionEvent;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.ui.gl.GalleryPageView;
import com.hippo.ehviewer.ui.gl.GalleryView;
import com.hippo.gl.glrenderer.ImageTexture;
import com.hippo.gl.view.GLRootView;
import com.hippo.image.Image;
import com.hippo.util.Dpad;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class GalleryActivity extends AppCompatActivity {
private GalleryView mGalleryView;
private ImageTexture.Uploader mUploader;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -40,12 +47,14 @@ public class GalleryActivity extends AppCompatActivity {
GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view);
mGalleryView = new GalleryView(this, new InvalidPageIterator(), null);
glRootView.setContentPane(mGalleryView);
mUploader = new ImageTexture.Uploader(glRootView);
}
@Override
protected void onDestroy() {
super.onDestroy();
mGalleryView = null;
mUploader = null;
}
@Override
@@ -145,51 +154,73 @@ public class GalleryActivity extends AppCompatActivity {
}
private static final int MAX_SIZE = 10;
private class InvalidPageIterator implements GalleryView.PageIterator {
private int mIndex = 0;
private int mBackup = mIndex;
public ImageTexture mImageTexture;
public InvalidPageIterator() {
try {
mImageTexture = new ImageTexture(Image.decode(new FileInputStream("/sdcard/1.jpg"), false));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void mark() {
mBackup = mIndex;
}
@Override
public void reset() {
mIndex = mBackup;
}
@Override
public boolean hasNext() {
return false;
return mIndex < MAX_SIZE - 1;
}
@Override
public boolean hasPrevious() {
return false;
return mIndex > 0;
}
@Override
public boolean isValid() {
return false;
return mIndex >= 0 && mIndex < MAX_SIZE;
}
@Override
public void next() {
if (mIndex >= MAX_SIZE - 1) {
throw new IndexOutOfBoundsException();
}
mIndex++;
}
@Override
public void previous() {
if (mIndex <= 0) {
throw new IndexOutOfBoundsException();
}
mIndex--;
}
@Override
public void bind(GalleryPageView view) {
//view.showImage();
view.getImageView().setTexture(mImageTexture);
}
@Override
public void unbind(GalleryPageView view) {
view.getImageView().setTexture(null);
}
}
@@ -23,7 +23,7 @@ import android.view.animation.Interpolator;
import com.hippo.gl.anim.Animation;
class Fling extends Animation {
public abstract class Fling extends Animation {
private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
@@ -39,13 +39,6 @@ class Fling extends Animation {
private float mPhysicalCoeff;
//private GalleryPageView mPage;
private int mDx;
private int mDy;
private int mLastX;
private int mLastY;
private int[] mTemp = new int[2];
static {
float x_min = 0.0f;
float y_min = 0.0f;
@@ -103,6 +96,7 @@ class Fling extends Animation {
* 39.37f // inch/meter
* ppi
* 0.84f; // look and feel tuning
setInterpolator(FLING_INTERPOLATOR);
}
private double getSplineDeceleration(int velocity) {
@@ -110,13 +104,13 @@ class Fling extends Animation {
}
/* Returns the duration, expressed in milliseconds */
private int getSplineFlingDuration(int velocity) {
protected int getSplineFlingDuration(int velocity) {
final double l = getSplineDeceleration(velocity);
final double decelMinusOne = DECELERATION_RATE - 1.0;
return (int) (1000.0 * Math.exp(l / decelMinusOne));
}
private double getSplineFlingDistance(int velocity) {
protected double getSplineFlingDistance(int velocity) {
final double l = getSplineDeceleration(velocity);
final double decelMinusOne = DECELERATION_RATE - 1.0;
return FLING_FRICTION * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
@@ -126,7 +120,7 @@ class Fling extends Animation {
* Modifies mDuration to the duration it takes to get from start to newFinal using the
* spline interpolation. The previous duration was needed to get to oldFinal.
**/
private int adjustDuration(int start, int oldFinal, int newFinal, int duration) {
protected int adjustDuration(int start, int oldFinal, int newFinal, int duration) {
final int oldDistance = oldFinal - start;
final int newDistance = newFinal - start;
final float x = Math.abs((float) newDistance / oldDistance);
@@ -141,49 +135,4 @@ class Fling extends Animation {
}
return duration;
}
public void startFling(GalleryPageView page, int velocityX, int minX, int maxX,
int velocityY, int minY, int maxY) {
//mPage = page;
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;
}
setDuration(Math.max(durationX, durationY));
start();
// TODO 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 (offsetX != 0 || offsetY != 0) {
// TODO mPage.scroll(-offsetX, -offsetY, mTemp);
}
mLastX = x;
mLastY = y;
}
}
@@ -29,10 +29,11 @@ import com.hippo.yorozuya.LayoutUtils;
public class GalleryPageView extends GLFrameLayout {
public ImageView mImage;
public GLTextView mPage;
public GLTextureView mError;
public GLProgressView mProgress;
private ImageView mImage;
private GLLinearLayout mInfo;
private GLTextView mPage;
private GLTextureView mError;
private GLProgressView mProgress;
// TODO
private static TextTexture sPageTextTexture;
@@ -45,12 +46,12 @@ public class GalleryPageView extends GLFrameLayout {
addComponent(mImage, glp);
// Add other panel
GLLinearLayout ll = new GLLinearLayout();
ll.setOrientation(GLLinearLayout.VERTICAL);
mInfo = new GLLinearLayout();
mInfo.setOrientation(GLLinearLayout.VERTICAL);
glp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
glp.gravity = Gravity.CENTER;
addComponent(ll, glp);
addComponent(mInfo, glp);
// Add page
mPage = new GLTextView();
@@ -58,14 +59,14 @@ public class GalleryPageView extends GLFrameLayout {
GLLinearLayout.LayoutParams lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_HORIZONTAL;
ll.addComponent(mPage, lp);
mInfo.addComponent(mPage, lp);
// Add error
mError = new GLTextureView();
lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_HORIZONTAL;
ll.addComponent(mError, lp);
mInfo.addComponent(mError, lp);
// Add progress
mProgress = new GLProgressView();
@@ -74,7 +75,12 @@ public class GalleryPageView extends GLFrameLayout {
lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_HORIZONTAL;
ll.addComponent(mProgress, lp);
mInfo.addComponent(mProgress, lp);
}
public void showImage() {
//mImage.setVisibility(VISIBLE);
//mInfo.setVisibility(INVISIBLE);
}
public ImageView getImageView() {
@@ -26,6 +26,7 @@ import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import com.hippo.ehviewer.R;
import com.hippo.gl.annotation.RenderThread;
import com.hippo.gl.glrenderer.GLCanvas;
import com.hippo.gl.util.GalleryUtils;
import com.hippo.gl.view.AnimationTime;
@@ -139,7 +140,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
int interval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP);
int progressSize = LayoutUtils.dp2pix(context, PROGRESS_SIZE_IN_DP);
mPagerLayoutManager = new PagerLayoutManager(this, interval, progressSize);
mPagerLayoutManager = new PagerLayoutManager(context, this, interval, progressSize);
mPagerLayoutManager.onAttach(iterator);
mLayoutManager = mPagerLayoutManager;
@@ -182,12 +183,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
}
public void requestFill() {
if (GalleryUtils.isRenderThread()) {
fill();
} else {
mRequestFill = true;
invalidate();
}
mRequestFill = true;
invalidate();
}
@Override
@@ -254,16 +251,9 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
return onScale(focusX, focusY, 1.0f);
}
private boolean canScale() {
if (mLastLayoutMode != LAYOUT_MODE_LEFT_TO_RIGHT && mLastLayoutMode != LAYOUT_MODE_RIGHT_TO_LEFT) {
return false;
}
return !mScroll;
}
@Override
public boolean onScale(float focusX, float focusY, float scale) {
if (!canScale()) {
if (mScroll || !mLayoutManager.canScale()) {
return false;
}
mScale = true;
@@ -294,7 +284,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
@Override
public void onPointerDown(float x, float y) {
if (canScale()) {
if (!mScroll && mLayoutManager.canScale()) {
mScale = true;
}
}
@@ -330,6 +320,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
}
}
@RenderThread
private void fill() {
// Must be in render thread
GalleryUtils.assertInRenderThread();
@@ -341,7 +332,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
}
@Override
public void onRender(GLCanvas canvas) {
public void render(GLCanvas canvas) {
long time = AnimationTime.get();
boolean invalidate = mLayoutManager.onUpdateAnimation(time);
if (invalidate) {
@@ -354,7 +345,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
fill();
}
mEdgeView.onRender(canvas);
super.render(canvas);
mEdgeView.render(canvas);
}
public GalleryPageView obtainPage() {
@@ -436,6 +428,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener {
public abstract void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
public abstract boolean canScale();
public abstract void onScale(float focusX, float focusY, float scale);
public abstract boolean onUpdateAnimation(long time);
@@ -39,7 +39,7 @@ public class ImageView extends GLView {
@Retention(RetentionPolicy.SOURCE)
public @interface StartPosition {}
// TODO adjust scale max and min accroding to image size and screen size
// TODO adjust scale max and min according to image size and screen size
private static final float SCALE_MIN = 1 / 10.0f;
private static final float SCALE_MAX = 10.0f;
@@ -100,6 +100,83 @@ public class ImageView extends GLView {
mPositionInRootDirty = true;
}
public boolean isLoaded() {
return mTexture != null;
}
public boolean canFlingVertically() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return false;
}
}
return mDst.top < 0.0f || mDst.bottom > getHeight();
}
public boolean canFlingHorizontally() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return false;
}
}
return mDst.left < 0.0f || mDst.right > getWidth();
}
public boolean canFling() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return false;
}
}
return mDst.left < 0.0f || mDst.top < 0.0f || mDst.right > getWidth() || mDst.bottom > getHeight();
}
public int getMaxDx() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return 0;
}
}
return Math.max(0, -(int) mDst.left);
}
public int getMinDx() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return 0;
}
}
return Math.min(0, getWidth() - (int) mDst.right);
}
public int getMaxDy() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return 0;
}
}
return Math.max(0, -(int) mDst.top);
}
public int getMinDy() {
if (mScaleOffsetDirty) {
setScaleOffset(mScaleMode, mStartPosition, mScaleValue);
if (mScaleOffsetDirty) {
return 0;
}
}
return Math.min(0, getHeight() - (int) mDst.bottom);
}
/**
* If target is shorter then screen, make it in screen center. If target is
* longer then parent, make sure target fill parent over
@@ -251,7 +328,6 @@ public class ImageView extends GLView {
mPositionInRootDirty = true;
}
public void scroll(int dx, int dy, int[] remain) {
// Only work after layout
if (mScaleOffsetDirty) {
@@ -367,6 +443,8 @@ public class ImageView extends GLView {
srcActual.setEmpty();
dstActual.setEmpty();
}
mPositionInRootDirty = false;
}
@Override
@@ -381,7 +459,6 @@ public class ImageView extends GLView {
}
if (mPositionInRootDirty) {
mPositionInRootDirty = false;
applyPositionInRoot();
} else {
mSrcActual.set(0, 0, texture.getWidth(), texture.getHeight());
@@ -16,9 +16,9 @@
package com.hippo.ehviewer.ui.gl;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
@@ -70,10 +70,17 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
private int mDeltaX;
private int mDeltaY;
public PagerLayoutManager(@NonNull GalleryView galleryView, int interval, int progressSize) {
private PageFling mPageFling;
private SmoothScroller mSmoothScroller;
public PagerLayoutManager(Context context, @NonNull GalleryView galleryView,
int interval, int progressSize) {
super(galleryView);
mInterval = interval;
mProgressSpec = GLView.MeasureSpec.makeMeasureSpec(progressSize, GLView.MeasureSpec.EXACTLY);
mPageFling = new PageFling(context);
mSmoothScroller = new SmoothScroller();
}
public void setMode(@Mode int mode) {
@@ -255,11 +262,30 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
public void onDown() {
mDeltaX = 0;
mDeltaY = 0;
mSmoothScroller.cancel();
mPageFling.cancel();
}
@Override
public void onUp() {
mGalleryView.getEdgeView().onRelease();
// Scroll
if (mOffset != 0) {
int width = mGalleryView.getWidth();
int dx;
if (mOffset >= mInterval && getLeftPage() != null) {
dx = mOffset - width - mInterval;
} else if (mOffset <= -mInterval && getRightPage() != null) {
dx = mOffset + width + mInterval;
} else {
dx = mOffset;
}
final float pageDelta = 7 * (float) Math.abs(mOffset) / (width + mInterval);
int duration = (int) ((pageDelta + 1) * 100);
mSmoothScroller.startSmoothScroll(dx, 0, duration);
}
}
private void pagePrevious() {
@@ -303,9 +329,9 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
if (iterator.hasNext()) {
iterator.mark();
iterator.next();
mPrevious = mGalleryView.obtainPage();
iterator.bind(mPrevious);
mGalleryView.addComponent(mPrevious);
mNext = mGalleryView.obtainPage();
iterator.bind(mNext);
mGalleryView.addComponent(mNext);
iterator.reset();
}
}
@@ -341,31 +367,41 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
int remain;
if (dx < 0) { // Try to show left
int limit;
if (leftPage == null) {
return dx;
limit = 0;
} else {
limit = width + mInterval;
}
if (dx > mOffset - width - mInterval) {
if (dx > mOffset - limit) {
remain = 0;
mOffset -= dx;
} else {
// Go to left page
pageLeft();
remain = dx + width + mInterval - mOffset;
// Go to left page if left page not null
if (leftPage != null) {
pageLeft();
}
remain = dx + limit - mOffset;
mOffset = 0;
}
} else { // Try to show right
int limit;
if (rightPage == null) {
return dx;
limit = 0;
} else {
limit = - width - mInterval;
}
if (dx < width + mInterval + mOffset) {
if (dx < mOffset - limit) {
remain = 0;
mOffset -= dx;
} else {
// Go to right page
pageRight();
remain = dx - width - mInterval - mOffset;
// Go to right page if right page not null
if (rightPage != null) {
pageRight();
}
remain = dx + limit - mOffset;
mOffset = 0;
}
}
@@ -373,7 +409,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
return remain;
}
public void overScrollEdge(float x, float y, int dx, int dy) {
public void overScrollEdge(int dx, int dy, float x, float y) {
GLEdgeView edgeView = mGalleryView.getEdgeView();
mDeltaX += dx;
@@ -391,11 +427,22 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
}
}
// TODO What about top and bottom
if (mCurrent.getImageView().canFlingVertically()) {
if (mDeltaY < 0) {
edgeView.onPull(-mDeltaY, x, GLEdgeView.TOP);
if (!edgeView.isFinished(GLEdgeView.BOTTOM)) {
edgeView.onRelease(GLEdgeView.BOTTOM);
}
} else if (mDeltaY > 0) {
edgeView.onPull(mDeltaY, y, GLEdgeView.BOTTOM);
if (!edgeView.isFinished(GLEdgeView.TOP)) {
edgeView.onRelease(GLEdgeView.TOP);
}
}
}
}
@Override
public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) {
public void scrollInternal(float dx, float dy, float x, float y) {
if (mCurrent == null) {
return;
}
@@ -422,7 +469,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
(getLeftPage() == null && mOffset == 0 && remainX < 0) ||
(getRightPage() == null && mOffset == 0 && remainX > 0)) {
// On edge
overScrollEdge(x, y, remainX, remainY);
overScrollEdge(remainX, remainY, x, y);
remainX = 0;
remainY = 0;
} else if (mCanScrollBetweenPages) {
@@ -432,7 +479,6 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
mDeltaX = 0;
mDeltaY = 0;
} else {
Log.w(TAG, "Something wrong?");
remainX = 0;
remainY = 0;
mDeltaX = 0;
@@ -445,14 +491,26 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
}
}
@Override
public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) {
scrollInternal(dx, dy, x, y);
}
@Override
public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mCurrent == null) {
if (mCurrent == null || mOffset != 0 || !mCurrent.getImageView().isLoaded() ||
!mCurrent.getImageView().canFling()) {
return;
}
ImageView image = mCurrent.getImageView();
mPageFling.startFling((int) velocityX, image.getMinDx(), image.getMaxDx(),
(int) velocityY, image.getMinDy(), image.getMaxDy());
}
@Override
public boolean canScale() {
return mCurrent != null && mOffset == 0 && mCurrent.getImageView().isLoaded();
}
@Override
@@ -461,22 +519,17 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
return;
}
mCurrent.getImageView().scale(focusX, focusY, scale);
// TODO Save scale
}
@Override
public boolean onUpdateAnimation(long time) {
return false;
boolean invalidate = mSmoothScroller.calculate(time);
invalidate |= mPageFling.calculate(time);
return invalidate;
}
class SmoothScroller extends Animation {
private int mDx;
@@ -484,6 +537,10 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
private int mLastX;
private int mLastY;
public SmoothScroller() {
setInterpolator(SMOOTH_SCROLLER_INTERPOLATOR);
}
public void startSmoothScroll(int dx, int dy, int duration) {
mDx = dx;
mDy = dy;
@@ -491,7 +548,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
mLastY = 0;
setDuration(duration);
start();
// TODO invalidate();
mGalleryView.invalidate();
}
@Override
@@ -501,16 +558,105 @@ public class PagerLayoutManager extends GalleryView.LayoutManager {
int offsetX = x - mLastX;
while (offsetX != 0) {
int oldOffsetX = offsetX;
// TODO offsetX = scrollBetweenPagesHMode(offsetX);
offsetX = scrollBetweenPages(offsetX);
// Avoid loop infinitely
if (offsetX == oldOffsetX) {
break;
} else {
// TODO mRequestFill = true;
mGalleryView.requestFill();
}
}
mLastX = x;
mLastY = y;
}
}
class PageFling extends Fling {
private int mVelocityX;
private int mVelocityY;
private int mDx;
private int mDy;
private int mLastX;
private int mLastY;
private int[] mTemp = new int[2];
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;
}
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 (mCurrent != null && (offsetX != 0 || offsetY != 0)) {
mCurrent.getImageView().scroll(-offsetX, -offsetY, mTemp);
}
mLastX = x;
mLastY = y;
}
@Override
protected void onFinish() {
if (mCurrent == null) {
return;
}
GLEdgeView edgeView = mGalleryView.getEdgeView();
ImageView imageView = mCurrent.getImageView();
if (imageView.canFlingHorizontally()) {
if (mVelocityX > 0 && getLeftPage() == null && imageView.getMaxDx() == 0 &&
edgeView.isFinished(GLEdgeView.LEFT)) {
edgeView.onAbsorb(mVelocityX, GLEdgeView.LEFT);
} else if (mVelocityX < 0 && getRightPage() == null && imageView.getMinDx() == 0 &&
edgeView.isFinished(GLEdgeView.RIGHT)) {
edgeView.onAbsorb(-mVelocityX, GLEdgeView.RIGHT);
}
}
if (imageView.canFlingVertically()) {
if (mVelocityY > 0 && imageView.getMaxDy() == 0 &&
edgeView.isFinished(GLEdgeView.TOP)) {
edgeView.onAbsorb(mVelocityY, GLEdgeView.TOP);
} else if (mVelocityY < 0 && imageView.getMinDy() == 0 &&
edgeView.isFinished(GLEdgeView.BOTTOM)) {
edgeView.onAbsorb(-mVelocityY, GLEdgeView.BOTTOM);
}
}
}
}
}
@@ -60,6 +60,11 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager {
}
@Override
public boolean canScale() {
return false;
}
@Override
public void onScale(float focusX, float focusY, float scale) {
@@ -125,11 +125,11 @@ abstract public class Animation {
return mStartTime != NO_ANIMATION;
}
public void forceStop() {
public void cancel() {
mStartTime = NO_ANIMATION;
}
public void forceReset() {
public void reset() {
mStartTime = ANIMATION_START;
mRunnedCount = 0;
mLastFrameTime = 0;
@@ -153,6 +153,7 @@ abstract public class Animation {
if (elapse >= mDuration) {
mRunnedCount++;
if (mRunnedCount >= mRepeatCount && mRepeatCount != INFINITE) {
onFinish();
mStartTime = NO_ANIMATION;
} else {
mStartTime += elapse;
@@ -163,4 +164,7 @@ abstract public class Animation {
}
abstract protected void onCalculate(float progress);
protected void onFinish() {
}
}
@@ -0,0 +1,27 @@
/*
* 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.gl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
public @interface RenderThread {
}
@@ -19,14 +19,13 @@ package com.hippo.gl.glrenderer;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.RectF;
import android.os.Process;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import com.hippo.gl.image.AnimatedImage;
import com.hippo.gl.image.Image;
import com.hippo.gl.annotation.RenderThread;
import com.hippo.gl.view.GLRoot;
import com.hippo.image.Image;
import com.hippo.yorozuya.InfiniteThreadExecutor;
import com.hippo.yorozuya.PVLock;
import com.hippo.yorozuya.PriorityThreadFactory;
@@ -39,12 +38,12 @@ import java.util.concurrent.LinkedBlockingQueue;
public class ImageTexture implements Texture {
@IntDef({SMALL, LARGE})
@IntDef({TILE_SMALL, TILE_LARGE})
@Retention(RetentionPolicy.SOURCE)
private @interface Size {}
private @interface TileType {}
private static final int SMALL = 0;
private static final int LARGE = 1;
private static final int TILE_SMALL = 0;
private static final int TILE_LARGE = 1;
private static final int SMALL_CONTENT_SIZE = 254;
private static final int SMALL_BORDER_SIZE = 1;
@@ -62,7 +61,6 @@ public class ImageTexture implements Texture {
private static Bitmap sSmallUploadBitmap;
private static Bitmap sLargeUploadBitmap;
private static InfiniteThreadExecutor sThreadExecutor;
private static PVLock sPVLock;
@@ -70,21 +68,6 @@ public class ImageTexture implements Texture {
private static Tile sLargeFreeTileHead = null;
private static final Object sFreeTileLock = new Object();
public static void prepareResources() {
sSmallUploadBitmap = Bitmap.createBitmap(SMALL_TILE_SIZE, SMALL_TILE_SIZE, Bitmap.Config.ARGB_8888);
sLargeUploadBitmap = Bitmap.createBitmap(LARGE_TILE_SIZE, LARGE_TILE_SIZE, Bitmap.Config.ARGB_8888);
sThreadExecutor = new InfiniteThreadExecutor(3000, new LinkedBlockingQueue<Runnable>(),
new PriorityThreadFactory(ImageTexture.class.getSimpleName(), Process.THREAD_PRIORITY_BACKGROUND));
sPVLock = new PVLock(3);
}
public static void freeResources() {
sSmallUploadBitmap = null;
sLargeUploadBitmap = null;
sThreadExecutor = null;
sPVLock = null;
}
private final Image mImage;
private int mUploadIndex = 0;
private final Tile[] mTiles; // Can be modified in different threads.
@@ -95,12 +78,17 @@ public class ImageTexture implements Texture {
private final RectF mSrcRect = new RectF();
private final RectF mDestRect = new RectF();
private boolean mRecycled = false;
private boolean mRecycleLock = false;
private boolean mPause = false;
private volatile boolean mConfirmFrame = false;
private int mTargetFrame = -1;
private Callback mCallback;
private volatile boolean mFrameDirty = false;
static {
sSmallUploadBitmap = Bitmap.createBitmap(SMALL_TILE_SIZE, SMALL_TILE_SIZE, Bitmap.Config.ARGB_8888);
sLargeUploadBitmap = Bitmap.createBitmap(LARGE_TILE_SIZE, LARGE_TILE_SIZE, Bitmap.Config.ARGB_8888);
sThreadExecutor = new InfiniteThreadExecutor(3000, new LinkedBlockingQueue<Runnable>(),
new PriorityThreadFactory(ImageTexture.class.getSimpleName(),
android.os.Process.THREAD_PRIORITY_BACKGROUND));
sPVLock = new PVLock(3);
}
public static class Uploader implements GLRoot.OnGLIdleListener {
private final ArrayDeque<ImageTexture> mTextures =
@@ -150,7 +138,8 @@ public class ImageTexture implements Texture {
private static class Tile extends UploadedTexture {
private int mSize;
@TileType
private int mTileType;
public int offsetX;
public int offsetY;
public Image image;
@@ -161,18 +150,18 @@ public class ImageTexture implements Texture {
public int tileSize;
public Bitmap uploadBitmap;
public void setSize(@Size int size, int width, int height) {
mSize = size;
if (size == SMALL) {
public void setSize(@TileType int tileType, int width, int height) {
mTileType = tileType;
if (tileType == TILE_SMALL) {
borderSize = SMALL_BORDER_SIZE;
tileSize = SMALL_TILE_SIZE;
uploadBitmap = sSmallUploadBitmap;
} else if (size == LARGE) {
} else if (tileType == TILE_LARGE) {
borderSize = LARGE_BORDER_SIZE;
tileSize = LARGE_TILE_SIZE;
uploadBitmap = sLargeUploadBitmap;
} else {
throw new IllegalStateException("Not support size " + size);
throw new IllegalStateException("Not support tile type: " + tileType);
}
contentWidth = width;
contentHeight = height;
@@ -188,8 +177,29 @@ public class ImageTexture implements Texture {
final Image image = this.image;
if (image != null) {
synchronized (image) {
image.copyPixels(offsetX - borderSize, offsetY - borderSize, uploadBitmap,
0, 0, tileSize, tileSize, Color.TRANSPARENT);
int srcX;
int dstX;
if (offsetX - borderSize < 0) {
srcX = 0;
dstX = borderSize - offsetX;
} else {
srcX = offsetX - borderSize;
dstX = 0;
}
int srcY;
int dstY;
if (offsetY - borderSize < 0) {
srcY = 0;
dstY = borderSize - offsetY;
} else {
srcY = offsetY - borderSize;
dstY = 0;
}
image.render(srcX, srcY, uploadBitmap, dstX, dstY,
Math.min(image.getWidth() - srcX, tileSize - dstX),
Math.min(image.getHeight() - srcY, tileSize - dstY),
true, Color.TRANSPARENT);
}
}
return uploadBitmap;
@@ -224,15 +234,31 @@ public class ImageTexture implements Texture {
}
public void free() {
switch (mSize) {
case SMALL:
switch (mTileType) {
case TILE_SMALL:
freeSmallTile(this);
break;
case LARGE:
case TILE_LARGE:
freeLargeTile(this);
break;
default:
throw new IllegalStateException("Not support size " + mSize);
throw new IllegalStateException("Not support tile type: " + mTileType);
}
}
private static void freeSmallTile(Tile tile) {
tile.invalidate();
synchronized (sFreeTileLock) {
tile.nextFreeTile = sSmallFreeTileHead;
sSmallFreeTileHead = tile;
}
}
private static void freeLargeTile(Tile tile) {
tile.invalidate();
synchronized (sFreeTileLock) {
tile.nextFreeTile = sLargeFreeTileHead;
sLargeFreeTileHead = tile;
}
}
}
@@ -263,20 +289,69 @@ public class ImageTexture implements Texture {
}
}
private static void freeSmallTile(Tile tile) {
tile.invalidate();
synchronized (sFreeTileLock) {
tile.nextFreeTile = sSmallFreeTileHead;
sSmallFreeTileHead = tile;
}
}
public ImageTexture(@NonNull Image image) {
mImage = image;
int width = mWidth = image.getWidth();
int height = mHeight = image.getHeight();
ArrayList<Tile> list = new ArrayList<>();
private static void freeLargeTile(Tile tile) {
tile.invalidate();
synchronized (sFreeTileLock) {
tile.nextFreeTile = sLargeFreeTileHead;
sLargeFreeTileHead = tile;
for (int x = 0; x < width; x += LARGE_CONTENT_SIZE) {
for (int y = 0; y < height; y += LARGE_CONTENT_SIZE) {
int w = Math.min(LARGE_CONTENT_SIZE, width - x);
int h = Math.min(LARGE_CONTENT_SIZE, height - y);
if (w <= SMALL_CONTENT_SIZE) {
Tile tile = obtainSmallTile();
tile.offsetX = x;
tile.offsetY = y;
tile.image = image;
tile.setSize(TILE_SMALL, w, Math.min(SMALL_CONTENT_SIZE, h));
list.add(tile);
int nextHeight = h - SMALL_CONTENT_SIZE;
if (nextHeight > 0) {
Tile nextTile = obtainSmallTile();
nextTile.offsetX = x;
nextTile.offsetY = y + SMALL_CONTENT_SIZE;
nextTile.image = image;
nextTile.setSize(TILE_SMALL, w, nextHeight);
list.add(nextTile);
}
} else if (h <= SMALL_CONTENT_SIZE) {
Tile tile = obtainSmallTile();
tile.offsetX = x;
tile.offsetY = y;
tile.image = image;
tile.setSize(TILE_SMALL, Math.min(SMALL_CONTENT_SIZE, w), h);
list.add(tile);
int nextWidth = w - SMALL_CONTENT_SIZE;
if (nextWidth > 0) {
Tile nextTile = obtainSmallTile();
nextTile.offsetX = x + SMALL_CONTENT_SIZE;
nextTile.offsetY = y;
nextTile.image = image;
nextTile.setSize(TILE_SMALL, nextWidth, h);
list.add(nextTile);
}
} else {
Tile tile = obtainLargeTile();
tile.offsetX = x;
tile.offsetY = y;
tile.image = image;
tile.setSize(TILE_LARGE, w, h);
list.add(tile);
}
}
}
mTiles = list.toArray(new Tile[list.size()]);
/*
if (image instanceof AnimatedImage) {
sThreadExecutor.execute(new DecodeTask());
}
*/
}
private boolean uploadNextTile(GLCanvas canvas) {
@@ -301,195 +376,6 @@ public class ImageTexture implements Texture {
return mUploadIndex == mTiles.length;
}
private class DecodeTask implements Runnable {
private boolean checkRecycle() {
if (mRecycled) {
if (!mImage.isRecycled()) {
mImage.recycle();
}
return true;
} else {
return false;
}
}
@Override
public void run() {
synchronized (mImage) {
if (checkRecycle()) {
return;
}
mRecycleLock = true;
}
AnimatedImage aImage = (AnimatedImage) mImage;
// Avoid a lot of decode thread at same time
sPVLock.p();
boolean decode = aImage.decode();
sPVLock.v();
synchronized (mImage) {
mRecycleLock = false;
if (checkRecycle()) {
return;
}
}
// No need to do animate if decode failed
if (!decode) {
return;
}
long delay;
long time;
int frame = aImage.getCurrentFrame();
int frameCount = aImage.getFrameCount();
long forecast = System.currentTimeMillis();
// No need to do animate if only on frame
if (frameCount <= 1) {
return;
}
while (true) {
synchronized (mImage) {
// Check recycle and unlock recycle
mRecycleLock = false;
if (checkRecycle()) {
return;
}
// Check pause
while (mPause && mTargetFrame == -1) {
try {
mImage.wait();
} catch (InterruptedException e) {
// Ignore
}
mPause = false;
// Check recycle
if (checkRecycle()) {
return;
}
// Update forecast
forecast = System.currentTimeMillis();
}
// Get next frame and delay
time = System.currentTimeMillis();
if (mTargetFrame == -1) {
frame = (frame + 1) % frameCount;
delay = aImage.getDelay(frame) - (time - forecast);
} else {
frame = mTargetFrame;
delay = 0;
mTargetFrame = -1;
}
forecast = time + delay;
}
// Do sleep
if (delay > 0) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// Ignore
}
}
// Check recycle and lock recycle
synchronized (mImage) {
if (checkRecycle()) {
return;
}
mRecycleLock = true;
}
aImage.setCurrentFrame(frame);
mConfirmFrame = true;
// Notify
Callback callback = mCallback;
if (callback != null) {
callback.invalidateImage(mImage);
}
}
}
}
public ImageTexture(@NonNull Image image) {
mImage = image;
int width = mWidth = image.getWidth();
int height = mHeight = image.getHeight();
ArrayList<Tile> list = new ArrayList<>();
for (int x = 0; x < width; x += LARGE_CONTENT_SIZE) {
for (int y = 0; y < height; y += LARGE_CONTENT_SIZE) {
int w = Math.min(LARGE_CONTENT_SIZE, width - x);
int h = Math.min(LARGE_CONTENT_SIZE, height - y);
if (w <= SMALL_TILE_SIZE) {
Tile tile = obtainSmallTile();
tile.offsetX = x;
tile.offsetY = y;
tile.image = image;
tile.setSize(SMALL, w, Math.min(SMALL_TILE_SIZE, h));
list.add(tile);
int nextHeight = h - SMALL_TILE_SIZE;
if (nextHeight > 0) {
Tile nextTile = obtainSmallTile();
nextTile.offsetX = x;
nextTile.offsetY = y + SMALL_TILE_SIZE;
nextTile.image = image;
nextTile.setSize(SMALL, w, nextHeight);
list.add(nextTile);
}
} else if (h <= SMALL_TILE_SIZE) {
Tile tile = obtainSmallTile();
tile.offsetX = x;
tile.offsetY = y;
tile.image = image;
tile.setSize(SMALL, Math.min(SMALL_TILE_SIZE, w), h);
list.add(tile);
int nextWidth = w - SMALL_TILE_SIZE;
if (nextWidth > 0) {
Tile nextTile = obtainSmallTile();
nextTile.offsetX = x + SMALL_TILE_SIZE;
nextTile.offsetY = y;
nextTile.image = image;
nextTile.setSize(SMALL, nextWidth, h);
list.add(nextTile);
}
} else {
Tile tile = obtainLargeTile();
tile.offsetX = x;
tile.offsetY = y;
tile.image = image;
tile.setSize(LARGE, w, h);
list.add(tile);
}
}
}
mTiles = list.toArray(new Tile[list.size()]);
if (image instanceof AnimatedImage) {
sThreadExecutor.execute(new DecodeTask());
}
}
public void setCallback(Callback callback) {
mCallback = callback;
}
@Override
public int getWidth() {
return mWidth;
@@ -522,9 +408,10 @@ public class ImageTexture implements Texture {
y + (src.bottom - y0) * scaleY);
}
@RenderThread
private void syncFrame() {
if (mConfirmFrame && mImage instanceof AnimatedImage) {
mConfirmFrame = false;
if (mFrameDirty) {
mFrameDirty = false;
// invalid tiles
for (int i = 0, n = mTiles.length; i < n; i++) {
@@ -582,8 +469,9 @@ public class ImageTexture implements Texture {
Tile t = mTiles[i];
src.set(0, 0, t.contentWidth, t.contentHeight);
src.offset(t.offsetX, t.offsetY);
if (!src.intersect(source))
if (!src.intersect(source)) {
continue;
}
mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
src.offset(t.borderSize - t.offsetX, t.borderSize - t.offsetY);
canvas.drawTexture(t, src, dest);
@@ -630,8 +518,9 @@ public class ImageTexture implements Texture {
Tile t = mTiles[i];
src.set(0, 0, t.contentWidth, t.contentHeight);
src.offset(t.offsetX, t.offsetY);
if (!src.intersect(source))
if (!src.intersect(source)) {
continue;
}
mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
src.offset(t.borderSize - t.offsetX, t.borderSize - t.offsetY);
canvas.drawMixed(t, color, ratio, src, dest);
@@ -647,41 +536,4 @@ public class ImageTexture implements Texture {
public boolean isReady() {
return mUploadIndex == mTiles.length;
}
public void setFrame(int frame) {
synchronized (mImage) {
mTargetFrame = frame;
mImage.notify();
}
}
public void setPause(boolean pause) {
synchronized (mImage) {
mPause = pause;
if (!pause) {
mImage.notify();
}
}
}
// Can be called in UI thread.
public void recycle() {
synchronized (mImage) {
mRecycled = true;
for (int i = 0, n = mTiles.length; i < n; ++i) {
mTiles[i].free();
}
mImage.notify();
if (!mRecycleLock) {
mImage.recycle();
}
}
}
public interface Callback {
void invalidateImage(Image image);
}
}
@@ -1,122 +0,0 @@
/*
* Copyright (C) 2015 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.gl.image;
public class AnimatedImage extends Image {
private boolean mDecoded;
private int mCurrentFrame;
private boolean mDirty = false;
protected AnimatedImage(long nativeImage, int format, int width, int height) {
super(nativeImage, format, width, height);
mCurrentFrame = 0;
}
protected void checkDecoded() {
if (!mDecoded) {
throw new IllegalStateException("Call decode() first!");
}
}
public boolean isDecoded() {
checkRecycled();
return mDecoded;
}
public boolean decode() {
checkRecycled();
if (mDecoded) {
return true;
}
if (mNativeImage != 0) {
boolean result = nativeDecode(mNativeImage, mFormat);
mDecoded = true;
return result;
} else {
return false;
}
}
public boolean isAnimated() {
checkRecycled();
checkDecoded();
return getFrameCount() != 1;
}
public int getFrameCount() {
checkRecycled();
checkDecoded();
return nativeGetFrameCount(mNativeImage, mFormat);
}
public int getCurrentFrame() {
checkRecycled();
checkDecoded();
return mCurrentFrame;
}
public void setCurrentFrame(int frame) {
checkRecycled();
checkDecoded();
int frameCount = getFrameCount();
if (frame < 0 || frame >= frameCount) {
throw new IndexOutOfBoundsException("Invalid frame " + frame + ", frame count is " + frameCount);
}
mCurrentFrame = frame;
if (mNativeImage != 0) {
nativeSetCurrentFrame(mNativeImage, mFormat, frame);
mDirty = true;
}
}
public void advance() {
setCurrentFrame((mCurrentFrame + 1) % getFrameCount());
}
public int getCurrentDelay() {
return getDelay(mCurrentFrame);
}
public int getDelay(int frame) {
checkRecycled();
checkDecoded();
int frameCount = getFrameCount();
if (frame < 0 || frame >= frameCount) {
throw new IndexOutOfBoundsException("Invalid frame " + frame + ", frame count is " + frameCount);
}
return nativeGetDelay(mNativeImage, mFormat, frame);
}
@Override
public void recycle() {
super.recycle();
}
private static native boolean nativeDecode(long nativeImage, int format);
private static native int nativeGetFrameCount(long nativeImage, int format);
private static native void nativeSetCurrentFrame(long nativeImage, int format, int frame);
private static native int nativeGetDelay(long nativeImage, int format, int frame);
}
@@ -1,87 +0,0 @@
/*
* Copyright (C) 2015 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.gl.image;
import android.graphics.Bitmap;
import com.hippo.yorozuya.io.InputStreamPipe;
public class Image {
protected long mNativeImage;
protected final int mFormat;
protected final int mWidth;
protected final int mHeight;
protected Image(long nativeImage, int format, int width, int height) {
mNativeImage = nativeImage;
mFormat = format;
mWidth = width;
mHeight = height;
}
protected void checkRecycled() {
if (mNativeImage == 0) {
throw new IllegalStateException("Image is recycled");
}
}
public int getFormat() {
return mFormat;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public boolean copyPixels(int srcX, int srcY, Bitmap bitmap, int dstX, int dstY,
int width, int height, int defaultColor) {
checkRecycled();
return nativeCopyPixels(mNativeImage, mFormat, srcX, srcY, bitmap,
dstX, dstY, width, height, defaultColor);
}
public void recycle() {
if (mNativeImage != 0) {
nativeRecycle(mNativeImage, mFormat);
mNativeImage = 0;
}
}
public boolean isRecycled() {
return mNativeImage == 0;
}
public static Image decodeStreamPipe(InputStreamPipe isPipe) {
return nativeDecodeStreamPipe(isPipe);
}
static {
System.loadLibrary("image");
}
private static native Image nativeDecodeStreamPipe(InputStreamPipe isPipe);
private static native boolean nativeCopyPixels(long nativeImage, int format, int srcX, int srcY,
Bitmap bitmap, int dstX, int dstY, int width, int height, int otherColor);
private static native void nativeRecycle(long nativeImage, int format);
}
+42 -26
View File
@@ -49,7 +49,7 @@ import java.util.ArrayList;
public class GLView implements TouchOwner {
private static final String TAG = "GLView";
private static final boolean DEBUG_DRAW_BOUNDS = false;
private static final boolean DEBUG_DRAW_BOUNDS = true;
public static final int VISIBLE = 0;
public static final int INVISIBLE = 1;
@@ -86,7 +86,7 @@ public class GLView implements TouchOwner {
/**
* Position in root
*/
private final int[] mPosition = new int[2];
private final int[] mPositionInRoot = new int[2];
protected final Rect mPaddings = new Rect();
private GLRoot mRoot;
@@ -429,7 +429,7 @@ public class GLView implements TouchOwner {
}
}
protected void render(GLCanvas canvas) {
public void render(GLCanvas canvas) {
// render background color
if (Color.alpha(mBackgroundColor) != 0) {
canvas.fillRect(0, 0, getWidth(), getHeight(), mBackgroundColor);
@@ -626,10 +626,45 @@ public class GLView implements TouchOwner {
final boolean forceLayout = (mViewFlags & FLAG_LAYOUT_REQUESTED) == FLAG_LAYOUT_REQUESTED;
if (sizeChanged || forceLayout) {
onLayout(sizeChanged, left, top, right, bottom);
notifyPositionInRoot();
} else {
dispatchNotifyPositionInRoot();
}
mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
}
public void dispatchNotifyPositionInRoot() {
if (notifyPositionInRoot()) {
for (int i = 0, size = getComponentCount(); i < size; i++) {
getComponent(i).dispatchNotifyPositionInRoot();
}
}
}
public boolean notifyPositionInRoot() {
// Update position in root
int[] position = mPositionInRoot;
int oldX = position[0];
int oldY = position[1];
if (mParent == null) {
position[0] = 0;
position[1] = 0;
} else {
mParent.getPositionInRoot(position);
// Apply offset in parent
position[0] += mBounds.left;
position[1] += mBounds.top;
}
int x = position[0];
int y = position[1];
if (x != oldX || y != oldY) {
onPositionInRootChanged(x, y, oldX, oldY);
return true;
} else {
return false;
}
}
private boolean setBounds(int left, int top, int right, int bottom) {
Rect bounds = mBounds;
int oldW = bounds.right - bounds.left;
@@ -644,25 +679,6 @@ public class GLView implements TouchOwner {
onSizeChanged(newW, newH, oldW, oldH);
}
// Update position in root
int[] position = mPosition;
int oldX = position[0];
int oldY = position[1];
if (mParent == null) {
position[0] = 0;
position[1] = 0;
} else {
mParent.getPositionInRoot(position);
// Apply offset in parent
position[0] += left;
position[1] += top;
}
int x = position[0];
int y = position[1];
if (x != oldX || y != oldY) {
onPositionInRootChanged(x, y, oldX, oldY);
}
return sizeChanged;
}
@@ -674,8 +690,8 @@ public class GLView implements TouchOwner {
public void getPositionInRoot(int[] position) {
AssertUtils.assertEquals("position should be 2 length int array", position.length, 2);
position[0] = mPosition[0];
position[1] = mPosition[1];
position[0] = mPositionInRoot[0];
position[1] = mPositionInRoot[1];
}
/**
@@ -689,8 +705,8 @@ public class GLView implements TouchOwner {
return;
}
int x = mPosition[0];
int y = mPosition[1];
int x = mPositionInRoot[0];
int y = mPositionInRoot[1];
rect.set(x, y, x + getWidth(), y + getHeight());
if (!rect.intersect(0, 0, root.getWidth(), root.getHeight())) {
rect.setEmpty();
@@ -96,8 +96,7 @@ public final class GLEdgeView extends GLView {
}
@Override
public void onRender(GLCanvas canvas) {
super.onRender(canvas);
public void render(GLCanvas canvas) {
boolean more = false;
for (int i = 0; i < 4; i++) {
canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
@@ -135,14 +135,14 @@ public class GLProgressView extends GLView {
private void startAnimations() {
List<Animation> animations = mAnimations;
for (int i = 0, n = animations.size(); i < n; i++) {
animations.get(i).forceReset();
animations.get(i).reset();
}
}
private void stopAnimations() {
List<Animation> animations = mAnimations;
for (int i = 0, n = animations.size(); i < n; i++) {
animations.get(i).forceStop();
animations.get(i).cancel();
}
}
@@ -94,6 +94,10 @@ public class GLTextView extends GLView {
@Override
public void onRender(GLCanvas canvas) {
if (mTextTexture == null || mIndexes == null) {
return;
}
Rect paddings = getPaddings();
int x = getDefaultBegin(getWidth(), (int) mTextTexture.getTextWidth(mIndexes),
paddings.left, paddings.right, Gravity.getPosition(mGravity, Gravity.HORIZONTAL));