269 lines
7.9 KiB
Java
269 lines
7.9 KiB
Java
package it.sephiroth.android.library.imagezoom;
|
|
|
|
import android.content.Context;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.Rect;
|
|
import android.graphics.RectF;
|
|
import android.graphics.drawable.Drawable;
|
|
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 {
|
|
|
|
private static final float SCROLL_DELTA_THRESHOLD = 1.0f;
|
|
static final float MIN_ZOOM = 0.9f;
|
|
protected ScaleGestureDetector mScaleDetector;
|
|
protected GestureDetector mGestureDetector;
|
|
protected int mTouchSlop;
|
|
protected float mCurrentScaleFactor;
|
|
protected float mScaleFactor;
|
|
protected int mDoubleTapDirection;
|
|
protected OnGestureListener mGestureListener;
|
|
protected OnScaleGestureListener mScaleListener;
|
|
protected boolean mDoubleTapToZoomEnabled = true;
|
|
protected boolean mScaleEnabled = true;
|
|
protected boolean mScrollEnabled = true;
|
|
|
|
private OnImageViewTouchDoubleTapListener doubleTapListener;
|
|
|
|
public interface OnScaleChangedListener {
|
|
public void onScaleChanged(float scale);
|
|
}
|
|
|
|
protected OnScaleChangedListener mScaleChangedListener;
|
|
|
|
public ImageViewTouch( Context context, AttributeSet attrs ) {
|
|
super( context, attrs );
|
|
}
|
|
|
|
@Override
|
|
protected void init() {
|
|
super.init();
|
|
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
|
|
mGestureListener = getGestureListener();
|
|
mScaleListener = getScaleListener();
|
|
|
|
mScaleDetector = new ScaleGestureDetector( getContext(), mScaleListener );
|
|
mGestureDetector = new GestureDetector( getContext(), mGestureListener, null, true );
|
|
|
|
mCurrentScaleFactor = 1f;
|
|
mDoubleTapDirection = 1;
|
|
}
|
|
|
|
public void setDoubleTapListener( OnImageViewTouchDoubleTapListener doubleTapListener ){
|
|
this.doubleTapListener = doubleTapListener;
|
|
}
|
|
|
|
public void setDoubleTapToZoomEnabled( boolean value ) {
|
|
mDoubleTapToZoomEnabled = value;
|
|
}
|
|
|
|
public void setScaleEnabled( boolean value ) {
|
|
mScaleEnabled = value;
|
|
}
|
|
|
|
public void setScrollEnabled( boolean value ) {
|
|
mScrollEnabled = value;
|
|
}
|
|
|
|
public boolean getDoubleTapEnabled() {
|
|
return mDoubleTapToZoomEnabled;
|
|
}
|
|
|
|
protected OnGestureListener getGestureListener() {
|
|
return new GestureListener();
|
|
}
|
|
|
|
protected OnScaleGestureListener getScaleListener() {
|
|
return new ScaleListener();
|
|
}
|
|
|
|
@Override
|
|
protected void onBitmapChanged( Drawable drawable ) {
|
|
super.onBitmapChanged( drawable );
|
|
|
|
float v[] = new float[9];
|
|
mSuppMatrix.getValues( v );
|
|
mCurrentScaleFactor = v[Matrix.MSCALE_X];
|
|
}
|
|
|
|
@Override
|
|
protected void _setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
|
|
super._setImageDrawable( drawable, reset, initial_matrix, maxZoom );
|
|
mScaleFactor = getMaxZoom() / 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_UP:
|
|
if ( getScale() < 1f ) {
|
|
zoomTo( 1f, 50 );
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void onZoom( float scale ) {
|
|
super.onZoom( scale );
|
|
if ( !mScaleDetector.isInProgress() ) mCurrentScaleFactor = scale;
|
|
|
|
if (mScaleChangedListener != null) {
|
|
mScaleChangedListener.onScaleChanged(mCurrentScaleFactor);
|
|
}
|
|
}
|
|
|
|
protected float onDoubleTapPost( float scale, float maxZoom ) {
|
|
if ( mDoubleTapDirection == 1 ) {
|
|
if (mCurrentScaleFactor - 1.0f < 0.01) { //( scale + ( mScaleFactor * 2 ) ) <= maxZoom
|
|
|
|
float scaleFactor = mScaleFactor;
|
|
|
|
RectF bitmapRect = getBitmapRect();
|
|
|
|
float w = bitmapRect.right - bitmapRect.left;
|
|
float h = bitmapRect.bottom - bitmapRect.top;
|
|
|
|
if (w < getWidth()) {
|
|
scaleFactor = (float)getWidth() / w - scale;
|
|
} else if (h < getHeight()) {
|
|
scaleFactor = (float)getHeight() / h - scale;
|
|
}
|
|
|
|
return scale + scaleFactor;
|
|
} else {
|
|
mDoubleTapDirection = -1;
|
|
return maxZoom;
|
|
}
|
|
} else {
|
|
mDoubleTapDirection = 1;
|
|
return 1f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 (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 onDoubleTap( MotionEvent e ) {
|
|
Log.i( LOG_TAG, "onDoubleTap. double tap enabled? " + mDoubleTapToZoomEnabled);
|
|
if (mDoubleTapToZoomEnabled) {
|
|
float scale = getScale();
|
|
float targetScale = scale;
|
|
targetScale = onDoubleTapPost( scale, getMaxZoom() );
|
|
targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
|
|
mCurrentScaleFactor = targetScale;
|
|
zoomTo( targetScale, e.getX(), e.getY(), 200 );
|
|
invalidate();
|
|
}
|
|
|
|
if( null != doubleTapListener ){
|
|
doubleTapListener.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;
|
|
if ( getScale() == 1f ) return false;
|
|
scrollBy( -distanceX, -distanceY );
|
|
invalidate();
|
|
return super.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;
|
|
|
|
float diffX = e2.getX() - e1.getX();
|
|
float diffY = e2.getY() - e1.getY();
|
|
|
|
if ( Math.abs( velocityX ) > 800 || Math.abs( velocityY ) > 800 ) {
|
|
scrollBy( diffX / 2, diffY / 2, 300 );
|
|
invalidate();
|
|
}
|
|
return super.onFling( e1, e2, velocityX, velocityY );
|
|
}
|
|
}
|
|
|
|
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
|
|
|
|
@SuppressWarnings("unused")
|
|
@Override
|
|
public boolean onScale( ScaleGestureDetector detector ) {
|
|
float span = detector.getCurrentSpan() - detector.getPreviousSpan();
|
|
float targetScale = mCurrentScaleFactor * detector.getScaleFactor();
|
|
if ( mScaleEnabled ) {
|
|
targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
|
|
zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
|
|
mCurrentScaleFactor = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
|
|
mDoubleTapDirection = 1;
|
|
invalidate();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public interface OnImageViewTouchDoubleTapListener {
|
|
void onDoubleTap();
|
|
}
|
|
|
|
public void setOnScaleChangedListener(OnScaleChangedListener listener) {
|
|
mScaleChangedListener = listener;
|
|
}
|
|
}
|