package com.echo5bravo.govre.UTILS.ImageZoom; import com.echo5bravo.govre.UTILS.ImageZoom.Cubic; 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.widget.ImageView; public class ImageViewTouchBase extends ImageView implements IDisposable { public interface OnBitmapChangedListener { void onBitmapChanged( Bitmap bitmap ); }; protected enum Command { Center, Move, Zoom, Layout, Reset, }; public static final String LOG_TAG = "image"; 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; final protected RotateBitmap mBitmapDisplayed = new RotateBitmap( null, 0 ); final protected float MAX_ZOOM = 2.0f; 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() { setImageBitmapReset( null, true ); } @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 ( mBitmapDisplayed.getBitmap() != null ) { getProperBaseMatrix( mBitmapDisplayed, mBaseMatrix ); setImageMatrix( Command.Layout, getImageViewMatrix() ); } } public void setImageBitmapReset( final Bitmap bitmap, final boolean reset ) { setImageRotateBitmapReset( new RotateBitmap( bitmap, 0 ), reset ); } public void setImageBitmapReset( final Bitmap bitmap, final int rotation, final boolean reset ) { setImageRotateBitmapReset( new RotateBitmap( bitmap, rotation ), reset ); } public void setImageRotateBitmapReset( final RotateBitmap bitmap, final boolean reset ) { //Log.d( LOG_TAG, "setImageRotateBitmapReset" ); final int viewWidth = getWidth(); if ( viewWidth <= 0 ) { mOnLayoutRunnable = new Runnable() { public void run() { setImageBitmapReset( bitmap.getBitmap(), bitmap.getRotation(), reset ); } }; return; } if ( bitmap.getBitmap() != null ) { getProperBaseMatrix( bitmap, mBaseMatrix ); setImageBitmap( bitmap.getBitmap(), bitmap.getRotation() ); } else { mBaseMatrix.reset(); setImageBitmap( null ); } if ( reset ) { mSuppMatrix.reset(); } setImageMatrix( Command.Reset, getImageViewMatrix() ); mMaxZoom = maxZoom(); if( mListener != null ) { mListener.onBitmapChanged( bitmap.getBitmap() ); } } protected float maxZoom() { if ( mBitmapDisplayed.getBitmap() == null ) { return 1F; } float fw = (float)mBitmapDisplayed.getWidth() / (float)mThisWidth; float fh = (float)mBitmapDisplayed.getHeight() / (float)mThisHeight; float max = Math.max( fw, fh ) * 4; return max; } public RotateBitmap getDisplayBitmap() { return mBitmapDisplayed; } public float getMaxZoom() { return mMaxZoom; } @Override public void setImageBitmap( Bitmap bitmap ) { setImageBitmap( bitmap, 0 ); } /** * This is the ultimate method called when a new bitmap is set * @param bitmap * @param rotation */ protected void setImageBitmap( Bitmap bitmap, int rotation ) { super.setImageBitmap( bitmap ); Drawable d = getDrawable(); if ( d != null ) { d.setDither( true ); } mBitmapDisplayed.setBitmap( bitmap ); mBitmapDisplayed.setRotation( rotation ); } protected Matrix getImageViewMatrix() { mDisplayMatrix.set( mBaseMatrix ); mDisplayMatrix.postConcat( mSuppMatrix ); return mDisplayMatrix; } /** * Setup the base matrix so that the image is centered and scaled properly. * * @param bitmap * @param matrix */ protected void getProperBaseMatrix( RotateBitmap bitmap, Matrix matrix ) { float viewWidth = getWidth(); float viewHeight = getHeight(); float w = bitmap.getWidth(); float h = bitmap.getHeight(); 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.postConcat( bitmap.getRotateMatrix() ); matrix.postScale( scale, scale ); matrix.postTranslate( ( viewWidth - w * scale ) / MAX_ZOOM, ( viewHeight - h * scale ) / MAX_ZOOM ); } protected float getValue( Matrix matrix, int whichValue ) { matrix.getValues( mMatrixValues ); return mMatrixValues[whichValue]; } protected RectF getBitmapRect() { if ( mBitmapDisplayed.getBitmap() == null ) return null; Matrix m = getImageViewMatrix(); RectF rect = new RectF( 0, 0, mBitmapDisplayed.getBitmap().getWidth(), mBitmapDisplayed.getBitmap().getHeight() ); m.mapRect( rect ); return rect; } protected float getScale( Matrix matrix ) { return getValue( matrix, Matrix.MSCALE_X ); } public float getScale() { return getScale( mSuppMatrix ); } protected void center( boolean horizontal, boolean vertical ) { if ( mBitmapDisplayed.getBitmap() == null ) return; RectF rect = getCenter( horizontal, vertical ); if ( rect.left != 0 || rect.top != 0 ) { postTranslate( rect.left, rect.top ); } } protected void setImageMatrix( Command command, Matrix matrix ) { setImageMatrix( matrix ); } protected RectF getCenter( boolean horizontal, boolean vertical ) { if ( mBitmapDisplayed.getBitmap() == 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; } } return new RectF( deltaX, deltaY, 0, 0 ); } protected void postTranslate( float deltaX, float deltaY ) { mSuppMatrix.postTranslate( deltaX, deltaY ); setImageMatrix( Command.Move, getImageViewMatrix() ); } protected void postScale( float scale, float centerX, float centerY ) { mSuppMatrix.postScale( scale, scale, centerX, centerY ); setImageMatrix( Command.Zoom, 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( float dx, float dy ) { RectF rect = getBitmapRect(); RectF srect = new RectF( dx, dy, 0, 0 ); updateRect( rect, srect ); postTranslate( srect.left, srect.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 float durationMs ) { final float dx = distanceX; final float dy = distanceY; final long startTime = System.currentTimeMillis(); mHandler.post( new Runnable() { float old_x = 0; float old_y = 0; public void run() { long now = System.currentTimeMillis(); float currentMs = Math.min( durationMs, now - startTime ); float x = Cubic.easeOut( currentMs, 0, dx, durationMs ); float y = Cubic.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.d( 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() { 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 ) {} } } } ); } public void dispose() { if ( mBitmapDisplayed.getBitmap() != null ) { if ( !mBitmapDisplayed.getBitmap().isRecycled() ) { mBitmapDisplayed.getBitmap().recycle(); } } clear(); } }