tt-rss-android/org.fox.ttrss/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java
2014-11-25 12:19:36 +03:00

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;
}
}