package com.external.imagezoom;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.ViewConfiguration;
public class ImageViewTouch extends ImageViewTouchBase {
static final float SCROLL_DELTA_THRESHOLD = 1.0f;
protected ScaleGestureDetector mScaleDetector;
protected GestureDetector mGestureDetector;
protected int mTouchSlop;
protected float mScaleFactor;
protected int mDoubleTapDirection;
protected OnGestureListener mGestureListener;
protected OnScaleGestureListener mScaleListener;
protected boolean mDoubleTapEnabled = true;
protected boolean mScaleEnabled = true;
protected boolean mScrollEnabled = true;
private OnImageViewTouchDoubleTapListener mDoubleTapListener;
private OnImageViewTouchSingleTapListener mSingleTapListener;
public ImageViewTouch ( Context context) {
super( context);
}
public ImageViewTouch ( Context context, AttributeSet attrs ) {
this( context, attrs, 0 );
}
public ImageViewTouch ( Context context, AttributeSet attrs, int defStyle ) {
super( context, attrs, defStyle );
}
@Override
protected void init(Context context, AttributeSet attrs, int defStyle) {
super.init( context, attrs, defStyle );
mTouchSlop = ViewConfiguration.get( getContext() ).getScaledTouchSlop();
mGestureListener = getGestureListener();
mScaleListener = getScaleListener();
mScaleDetector = new ScaleGestureDetector( getContext(), mScaleListener );
mGestureDetector = new GestureDetector( getContext(), mGestureListener, null, true );
mDoubleTapDirection = 1;
}
public void setDoubleTapListener( OnImageViewTouchDoubleTapListener listener ) {
mDoubleTapListener = listener;
}
public void setSingleTapListener( OnImageViewTouchSingleTapListener listener ) {
mSingleTapListener = listener;
}
public void setDoubleTapEnabled( boolean value ) {
mDoubleTapEnabled = value;
}
public void setScaleEnabled( boolean value ) {
mScaleEnabled = value;
}
public void setScrollEnabled( boolean value ) {
mScrollEnabled = value;
}
public boolean getDoubleTapEnabled() {
return mDoubleTapEnabled;
}
protected OnGestureListener getGestureListener() {
return new GestureListener();
}
protected OnScaleGestureListener getScaleListener() {
return new ScaleListener();
}
@Override
protected void _setImageDrawable( final Drawable drawable, final Matrix initial_matrix, float min_zoom, float max_zoom ) {
super._setImageDrawable( drawable, initial_matrix, min_zoom, max_zoom );
mScaleFactor = getMaxScale() / 3;
}
@Override
public boolean onTouchEvent( MotionEvent event ) {
mScaleDetector.onTouchEvent( event );
if ( !mScaleDetector.isInProgress() ) {
mGestureDetector.onTouchEvent( event );
}
int action = event.getAction();
switch ( action & MotionEvent.ACTION_MASK ) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
return onUp( event );
}
return true;
}
@Override
protected void onZoomAnimationCompleted( float scale ) {
if( LOG_ENABLED ) {
Log.d( LOG_TAG, "onZoomAnimationCompleted. scale: " + scale + ", minZoom: " + getMinScale() );
}
if ( scale < getMinScale() ) {
zoomTo( getMinScale(), 50 );
}
}
protected float onDoubleTapPost( float scale, float maxZoom ) {
if ( mDoubleTapDirection == 1 ) {
if ( ( scale + ( mScaleFactor * 2 ) ) <= maxZoom ) {
return scale + mScaleFactor;
} else {
mDoubleTapDirection = -1;
return maxZoom;
}
} else {
mDoubleTapDirection = 1;
return 1f;
}
}
public boolean onSingleTapConfirmed( MotionEvent e ) {
return true;
}
public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY ) {
if ( getScale() == 1f ) return false;
mUserScaled = true;
scrollBy( -distanceX, -distanceY );
invalidate();
return true;
}
public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) {
float diffX = e2.getX() - e1.getX();
float diffY = e2.getY() - e1.getY();
if ( Math.abs( velocityX ) > 800 || Math.abs( velocityY ) > 800 ) {
mUserScaled = true;
scrollBy( diffX / 2, diffY / 2, 300 );
invalidate();
return true;
}
return false;
}
public boolean onDown( MotionEvent e ) {
return true;
}
public boolean onUp( MotionEvent e ) {
if ( getScale() < getMinScale() ) {
zoomTo( getMinScale(), 50 );
}
return true;
}
public boolean onSingleTapUp( MotionEvent e ) {
return true;
}
/**
* Determines whether this ImageViewTouch can be scrolled.
*
* @param direction
* - positive direction value means scroll from right to left,
* negative value means scroll from left to right
*
* @return true if there is some more place to scroll, false - otherwise.
*/
public boolean canScroll( int direction ) {
RectF bitmapRect = getBitmapRect();
updateRect( bitmapRect, mScrollRect );
Rect imageViewRect = new Rect();
getGlobalVisibleRect( imageViewRect );
if( null == bitmapRect ) {
return false;
}
if ( bitmapRect.right >= imageViewRect.right ) {
if ( direction < 0 ) {
return Math.abs( bitmapRect.right - imageViewRect.right ) > SCROLL_DELTA_THRESHOLD;
}
}
double bitmapScrollRectDelta = Math.abs( bitmapRect.left - mScrollRect.left );
return bitmapScrollRectDelta > SCROLL_DELTA_THRESHOLD;
}
public class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapConfirmed( MotionEvent e ) {
if ( null != mSingleTapListener ) {
mSingleTapListener.onSingleTapConfirmed();
}
return ImageViewTouch.this.onSingleTapConfirmed( e );
}
@Override
public boolean onDoubleTap( MotionEvent e ) {
Log.i( LOG_TAG, "onDoubleTap. double tap enabled? " + mDoubleTapEnabled );
if ( mDoubleTapEnabled ) {
mUserScaled = true;
float scale = getScale();
float targetScale = scale;
targetScale = onDoubleTapPost( scale, getMaxScale() );
targetScale = Math.min( getMaxScale(), Math.max( targetScale, getMinScale() ) );
zoomTo( targetScale, e.getX(), e.getY(), DEFAULT_ANIMATION_DURATION );
invalidate();
}
if ( null != mDoubleTapListener ) {
mDoubleTapListener.onDoubleTap();
}
return super.onDoubleTap( e );
}
@Override
public void onLongPress( MotionEvent e ) {
if ( isLongClickable() ) {
if ( !mScaleDetector.isInProgress() ) {
setPressed( true );
performLongClick();
}
}
}
@Override
public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY ) {
if ( !mScrollEnabled ) return false;
if ( e1 == null || e2 == null ) return false;
if ( e1.getPointerCount() > 1 || e2.getPointerCount() > 1 ) return false;
if ( mScaleDetector.isInProgress() ) return false;
return ImageViewTouch.this.onScroll( e1, e2, distanceX, distanceY );
}
@Override
public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) {
if ( !mScrollEnabled ) return false;
if ( e1.getPointerCount() > 1 || e2.getPointerCount() > 1 ) return false;
if ( mScaleDetector.isInProgress() ) return false;
if ( getScale() == 1f ) return false;
return ImageViewTouch.this.onFling( e1, e2, velocityX, velocityY );
}
@Override
public boolean onSingleTapUp( MotionEvent e ) {
return ImageViewTouch.this.onSingleTapUp( e );
}
@Override
public boolean onDown( MotionEvent e ) {
return ImageViewTouch.this.onDown( e );
}
}
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
protected boolean mScaled = false;
@Override
public boolean onScale( ScaleGestureDetector detector ) {
float span = detector.getCurrentSpan() - detector.getPreviousSpan();
float targetScale = getScale() * detector.getScaleFactor();
if ( mScaleEnabled ) {
if( mScaled && span != 0 ) {
mUserScaled = true;
targetScale = Math.min( getMaxScale(), Math.max( targetScale, getMinScale() - 0.1f ) );
zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
mDoubleTapDirection = 1;
invalidate();
return true;
}
// This is to prevent a glitch the first time
// image is scaled.
if( !mScaled ) mScaled = true;
}
return true;
}
}
public interface OnImageViewTouchDoubleTapListener {
void onDoubleTap();
}
public interface OnImageViewTouchSingleTapListener {
void onSingleTapConfirmed();
}
}