summaryrefslogtreecommitdiff
path: root/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2014-10-16 23:34:20 +0400
committerAndrew Dolgov <[email protected]>2014-10-16 23:34:20 +0400
commit124f869faae3a0f75a3825e6a8e195c17f3c626a (patch)
tree4a6f120195b60673994cfd78b6d0002bd1020bea /org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
parentbc10c74406c0e8a16c40dbfcd30a138d441dd859 (diff)
initial for idea
Diffstat (limited to 'org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java')
-rw-r--r--org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java509
1 files changed, 509 insertions, 0 deletions
diff --git a/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
new file mode 100644
index 0000000..8452a21
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
@@ -0,0 +1,509 @@
+package it.sephiroth.android.library.imagezoom;
+
+import it.sephiroth.android.library.imagezoom.easing.Cubic;
+import it.sephiroth.android.library.imagezoom.easing.Easing;
+import it.sephiroth.android.library.imagezoom.graphics.FastBitmapDrawable;
+import it.sephiroth.android.library.imagezoom.utils.IDisposable;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ImageView;
+
+/**
+ * Base View to manage image zoom/scrool/pinch operations
+ *
+ * @author alessandro
+ *
+ */
+public class ImageViewTouchBase extends ImageView implements IDisposable {
+
+ public interface OnBitmapChangedListener {
+
+ void onBitmapChanged( Drawable drawable );
+ };
+
+ public static final String LOG_TAG = "image";
+
+ protected Easing mEasing = new Cubic();
+ protected Matrix mBaseMatrix = new Matrix();
+ protected Matrix mSuppMatrix = new Matrix();
+ protected Handler mHandler = new Handler();
+ protected Runnable mOnLayoutRunnable = null;
+ protected float mMaxZoom;
+ protected final Matrix mDisplayMatrix = new Matrix();
+ protected final float[] mMatrixValues = new float[9];
+ protected int mThisWidth = -1, mThisHeight = -1;
+ protected boolean mFitToScreen = false;
+ protected boolean mFitToWidth = false;
+ final protected float MAX_ZOOM = 3.0f;
+
+ protected RectF mBitmapRect = new RectF();
+ protected RectF mCenterRect = new RectF();
+ protected RectF mScrollRect = new RectF();
+
+ private OnBitmapChangedListener mListener;
+
+ public ImageViewTouchBase( Context context ) {
+ super( context );
+ init();
+ }
+
+ public ImageViewTouchBase( Context context, AttributeSet attrs ) {
+ super( context, attrs );
+ init();
+ }
+
+ public void setOnBitmapChangedListener( OnBitmapChangedListener listener ) {
+ mListener = listener;
+ }
+
+ protected void init() {
+ setScaleType( ImageView.ScaleType.MATRIX );
+ }
+
+ public void clear() {
+ setImageBitmap( null, true );
+ }
+
+ public void setFitToScreen( boolean value ) {
+ if ( value != mFitToScreen ) {
+ mFitToScreen = value;
+ requestLayout();
+ }
+ }
+
+ public void setFitToWidth( boolean value ) {
+ if ( value != mFitToWidth ) {
+ mFitToWidth = value;
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onLayout( boolean changed, int left, int top, int right, int bottom ) {
+ super.onLayout( changed, left, top, right, bottom );
+ mThisWidth = right - left;
+ mThisHeight = bottom - top;
+ Runnable r = mOnLayoutRunnable;
+ if ( r != null ) {
+ mOnLayoutRunnable = null;
+ r.run();
+ }
+ if ( getDrawable() != null ) {
+ if ( mFitToScreen )
+ getProperBaseMatrix2( getDrawable(), mBaseMatrix );
+ else
+ getProperBaseMatrix( getDrawable(), mBaseMatrix );
+ setImageMatrix( getImageViewMatrix() );
+
+ if (mFitToWidth) zoomToWidth();
+ }
+ }
+
+ @Override
+ public void setImageBitmap( Bitmap bm ) {
+ setImageBitmap( bm, true );
+ }
+
+ @Override
+ public void setImageResource( int resId ) {
+ setImageDrawable( getContext().getResources().getDrawable( resId ) );
+ }
+
+ /**
+ * Set the new image to display and reset the internal matrix.
+ *
+ * @param bitmap
+ * - the {@link Bitmap} to display
+ * @param reset
+ * - if true the image bounds will be recreated, otherwise the old {@link Matrix} is used to display the new bitmap
+ * @see #setImageBitmap(Bitmap)
+ */
+ public void setImageBitmap( final Bitmap bitmap, final boolean reset ) {
+ setImageBitmap( bitmap, reset, null );
+ }
+
+ /**
+ * Similar to {@link #setImageBitmap(Bitmap, boolean)} but an optional view {@link Matrix} can be passed to determine the new
+ * bitmap view matrix.<br />
+ * This method is useful if you need to restore a Bitmap with the same zoom/pan values from a previous state
+ *
+ * @param bitmap
+ * - the {@link Bitmap} to display
+ * @param reset
+ * @param matrix
+ * - the {@link Matrix} to be used to display the new bitmap
+ *
+ * @see #setImageBitmap(Bitmap, boolean)
+ * @see #setImageBitmap(Bitmap)
+ * @see #getImageViewMatrix()
+ * @see #getDisplayMatrix()
+ */
+ public void setImageBitmap( final Bitmap bitmap, final boolean reset, Matrix matrix ) {
+ setImageBitmap( bitmap, reset, matrix, -1 );
+ }
+
+ /**
+ *
+ * @param bitmap
+ * @param reset
+ * @param matrix
+ * @param maxZoom
+ * - maximum zoom allowd during zoom gestures
+ *
+ * @see #setImageBitmap(Bitmap, boolean, Matrix)
+ */
+ public void setImageBitmap( final Bitmap bitmap, final boolean reset, Matrix matrix, float maxZoom ) {
+
+ Log.i( LOG_TAG, "setImageBitmap: " + bitmap );
+
+ if ( bitmap != null )
+ setImageDrawable( new FastBitmapDrawable( bitmap ), reset, matrix, maxZoom );
+ else
+ setImageDrawable( null, reset, matrix, maxZoom );
+ }
+
+ @Override
+ public void setImageDrawable( Drawable drawable ) {
+ setImageDrawable( drawable, true, null, -1 );
+ }
+
+ public void setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
+
+ final int viewWidth = getWidth();
+
+ if ( viewWidth <= 0 ) {
+ mOnLayoutRunnable = new Runnable() {
+
+ @Override
+ public void run() {
+ setImageDrawable( drawable, reset, initial_matrix, maxZoom );
+ }
+ };
+ return;
+ }
+
+ _setImageDrawable( drawable, reset, initial_matrix, maxZoom );
+ }
+
+ protected void _setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
+
+ if ( drawable != null ) {
+ if ( mFitToScreen )
+ getProperBaseMatrix2( drawable, mBaseMatrix );
+ else
+ getProperBaseMatrix( drawable, mBaseMatrix );
+ super.setImageDrawable( drawable );
+
+ if (mFitToWidth) zoomToWidth();
+
+ } else {
+ mBaseMatrix.reset();
+ super.setImageDrawable( null );
+ }
+
+ if ( reset ) {
+ mSuppMatrix.reset();
+ if ( initial_matrix != null ) {
+ mSuppMatrix = new Matrix( initial_matrix );
+ }
+ }
+
+ setImageMatrix( getImageViewMatrix() );
+
+ if ( maxZoom < 1 )
+ mMaxZoom = maxZoom();
+ else
+ mMaxZoom = maxZoom;
+
+ onBitmapChanged( drawable );
+ }
+
+ protected void onBitmapChanged( final Drawable bitmap ) {
+ if ( mListener != null ) {
+ mListener.onBitmapChanged( bitmap );
+ }
+ }
+
+ protected float maxZoom() {
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) {
+ return 1F;
+ }
+
+ float fw = (float) drawable.getIntrinsicWidth() / (float) mThisWidth;
+ float fh = (float) drawable.getIntrinsicHeight() / (float) mThisHeight;
+ float max = Math.max( fw, fh ) * 4;
+ return max;
+ }
+
+ public float getMaxZoom() {
+ return mMaxZoom;
+ }
+
+ public Matrix getImageViewMatrix() {
+ mDisplayMatrix.set( mBaseMatrix );
+ mDisplayMatrix.postConcat( mSuppMatrix );
+ return mDisplayMatrix;
+ }
+
+ /**
+ * Returns the current image display matrix. This matrix can be used in the next call to the
+ * {@link #setImageBitmap(Bitmap, boolean, Matrix)} to restore the same view state of the previous {@link Bitmap}
+ *
+ * @return
+ */
+ public Matrix getDisplayMatrix() {
+ return new Matrix( mSuppMatrix );
+ }
+
+ /**
+ * Setup the base matrix so that the image is centered and scaled properly.
+ *
+ * @param bitmap
+ * @param matrix
+ */
+ protected void getProperBaseMatrix( Drawable drawable, Matrix matrix ) {
+ float viewWidth = getWidth();
+ float viewHeight = getHeight();
+ float w = drawable.getIntrinsicWidth();
+ float h = drawable.getIntrinsicHeight();
+ matrix.reset();
+
+ if ( w > viewWidth || h > viewHeight ) {
+ float widthScale = Math.min( viewWidth / w, 2.0f );
+ float heightScale = Math.min( viewHeight / h, 2.0f );
+ float scale = Math.min( widthScale, heightScale );
+ matrix.postScale( scale, scale );
+ float tw = ( viewWidth - w * scale ) / 2.0f;
+ float th = ( viewHeight - h * scale ) / 2.0f;
+ matrix.postTranslate( tw, th );
+ } else {
+ float tw = ( viewWidth - w ) / 2.0f;
+ float th = ( viewHeight - h ) / 2.0f;
+ matrix.postTranslate( tw, th );
+ }
+ }
+
+ /**
+ * Setup the base matrix so that the image is centered and scaled properly.
+ *
+ * @param bitmap
+ * @param matrix
+ */
+ protected void getProperBaseMatrix2( Drawable bitmap, Matrix matrix ) {
+ float viewWidth = getWidth();
+ float viewHeight = getHeight();
+ float w = bitmap.getIntrinsicWidth();
+ float h = bitmap.getIntrinsicHeight();
+ matrix.reset();
+ float widthScale = Math.min( viewWidth / w, MAX_ZOOM );
+ float heightScale = Math.min( viewHeight / h, MAX_ZOOM );
+ float scale = Math.min( widthScale, heightScale );
+ matrix.postScale( scale, scale );
+ matrix.postTranslate( ( viewWidth - w * scale ) / 2.0f, ( viewHeight - h * scale ) / 2.0f );
+ }
+
+ protected float getValue( Matrix matrix, int whichValue ) {
+ matrix.getValues( mMatrixValues );
+ return mMatrixValues[whichValue];
+ }
+
+ protected RectF getBitmapRect() {
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) return null;
+ Matrix m = getImageViewMatrix();
+ mBitmapRect.set( 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight() );
+ m.mapRect( mBitmapRect );
+ return mBitmapRect;
+ }
+
+ protected float getScale( Matrix matrix ) {
+ return getValue( matrix, Matrix.MSCALE_X );
+ }
+
+ public float getRotation() {
+ return 0;
+ }
+
+ public float getScale() {
+ return getScale( mSuppMatrix );
+ }
+
+ protected void center( boolean horizontal, boolean vertical ) {
+ // Log.i(LOG_TAG, "center");
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) return;
+ RectF rect = getCenter( horizontal, vertical );
+ if ( rect.left != 0 || rect.top != 0 ) {
+ postTranslate( rect.left, rect.top );
+ }
+ }
+
+ protected RectF getCenter( boolean horizontal, boolean vertical ) {
+ final Drawable drawable = getDrawable();
+
+ if ( drawable == null ) return new RectF( 0, 0, 0, 0 );
+
+ RectF rect = getBitmapRect();
+ float height = rect.height();
+ float width = rect.width();
+ float deltaX = 0, deltaY = 0;
+ if ( vertical ) {
+ int viewHeight = getHeight();
+ if ( height < viewHeight ) {
+ deltaY = ( viewHeight - height ) / 2 - rect.top;
+ } else if ( rect.top > 0 ) {
+ deltaY = -rect.top;
+ } else if ( rect.bottom < viewHeight ) {
+ deltaY = getHeight() - rect.bottom;
+ }
+ }
+ if ( horizontal ) {
+ int viewWidth = getWidth();
+ if ( width < viewWidth ) {
+ deltaX = ( viewWidth - width ) / 2 - rect.left;
+ } else if ( rect.left > 0 ) {
+ deltaX = -rect.left;
+ } else if ( rect.right < viewWidth ) {
+ deltaX = viewWidth - rect.right;
+ }
+ }
+ mCenterRect.set( deltaX, deltaY, 0, 0 );
+ return mCenterRect;
+ }
+
+ protected void postTranslate( float deltaX, float deltaY ) {
+ mSuppMatrix.postTranslate( deltaX, deltaY );
+ setImageMatrix( getImageViewMatrix() );
+ }
+
+ protected void postScale( float scale, float centerX, float centerY ) {
+ mSuppMatrix.postScale( scale, scale, centerX, centerY );
+ setImageMatrix( getImageViewMatrix() );
+ }
+
+ protected void zoomTo( float scale ) {
+ float cx = getWidth() / 2F;
+ float cy = getHeight() / 2F;
+ zoomTo( scale, cx, cy );
+ }
+
+ public void zoomTo( float scale, float durationMs ) {
+ float cx = getWidth() / 2F;
+ float cy = getHeight() / 2F;
+ zoomTo( scale, cx, cy, durationMs );
+ }
+
+ protected void zoomTo( float scale, float centerX, float centerY ) {
+ if ( scale > mMaxZoom ) scale = mMaxZoom;
+ float oldScale = getScale();
+ float deltaScale = scale / oldScale;
+ postScale( deltaScale, centerX, centerY );
+ onZoom( getScale() );
+ center( true, true );
+ }
+
+ protected void onZoom( float scale ) {}
+
+ public void scrollBy( float x, float y ) {
+ panBy( x, y );
+ }
+
+ protected void panBy( double dx, double dy ) {
+ RectF rect = getBitmapRect();
+ mScrollRect.set( (float) dx, (float) dy, 0, 0 );
+ updateRect( rect, mScrollRect );
+ postTranslate( mScrollRect.left, mScrollRect.top );
+ center( true, true );
+ }
+
+ protected void updateRect( RectF bitmapRect, RectF scrollRect ) {
+ float width = getWidth();
+ float height = getHeight();
+
+ if ( bitmapRect.top >= 0 && bitmapRect.bottom <= height ) scrollRect.top = 0;
+ if ( bitmapRect.left >= 0 && bitmapRect.right <= width ) scrollRect.left = 0;
+ if ( bitmapRect.top + scrollRect.top >= 0 && bitmapRect.bottom > height ) scrollRect.top = (int) ( 0 - bitmapRect.top );
+ if ( bitmapRect.bottom + scrollRect.top <= ( height - 0 ) && bitmapRect.top < 0 )
+ scrollRect.top = (int) ( ( height - 0 ) - bitmapRect.bottom );
+ if ( bitmapRect.left + scrollRect.left >= 0 ) scrollRect.left = (int) ( 0 - bitmapRect.left );
+ if ( bitmapRect.right + scrollRect.left <= ( width - 0 ) ) scrollRect.left = (int) ( ( width - 0 ) - bitmapRect.right );
+ // Log.d( LOG_TAG, "scrollRect(2): " + scrollRect.toString() );
+ }
+
+ protected void scrollBy( float distanceX, float distanceY, final double durationMs ) {
+ final double dx = distanceX;
+ final double dy = distanceY;
+ final long startTime = System.currentTimeMillis();
+ mHandler.post( new Runnable() {
+
+ double old_x = 0;
+ double old_y = 0;
+
+ @Override
+ public void run() {
+ long now = System.currentTimeMillis();
+ double currentMs = Math.min( durationMs, now - startTime );
+ double x = mEasing.easeOut( currentMs, 0, dx, durationMs );
+ double y = mEasing.easeOut( currentMs, 0, dy, durationMs );
+ panBy( ( x - old_x ), ( y - old_y ) );
+ old_x = x;
+ old_y = y;
+ if ( currentMs < durationMs ) {
+ mHandler.post( this );
+ } else {
+ RectF centerRect = getCenter( true, true );
+ if ( centerRect.left != 0 || centerRect.top != 0 ) scrollBy( centerRect.left, centerRect.top );
+ }
+ }
+ } );
+ }
+
+ protected void zoomTo( float scale, final float centerX, final float centerY, final float durationMs ) {
+ // Log.i( LOG_TAG, "zoomTo: " + scale + ", " + centerX + ": " + centerY );
+ final long startTime = System.currentTimeMillis();
+ final float incrementPerMs = ( scale - getScale() ) / durationMs;
+ final float oldScale = getScale();
+ mHandler.post( new Runnable() {
+
+ @Override
+ public void run() {
+ long now = System.currentTimeMillis();
+ float currentMs = Math.min( durationMs, now - startTime );
+ float target = oldScale + ( incrementPerMs * currentMs );
+ zoomTo( target, centerX, centerY );
+ if ( currentMs < durationMs ) {
+ mHandler.post( this );
+ } else {
+ // if ( getScale() < 1f ) {}
+ }
+ }
+ } );
+ }
+
+ @Override
+ public void dispose() {
+ clear();
+ }
+
+ protected void zoomToWidth() {
+ RectF bitmapRect = getBitmapRect();
+
+ float w = bitmapRect.right - bitmapRect.left;
+
+ if (w < getWidth()) {
+ float scale = (float)getWidth() / w;
+
+ zoomTo(scale, 0f, 0f);
+ }
+ }
+}