package info.justaway.widget; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.widget.ImageView; public class ScaleImageView extends ImageView implements OnTouchListener { static boolean sBounds = false; private Activity mActivity; private Context mContext; private static final float MAX_SCALE = 10f; private Matrix mMatrix; private final float[] mMatrixValues = new float[9]; // display width height. private int mWidth; private int mHeight; private int mIntrinsicWidth; private int mIntrinsicHeight; private float mMinScale; private float mPrevDistance; private boolean mIsScaling; private boolean mIsInitializedScaling; private int mPrevMoveX; private int mPrevMoveY; private GestureDetector mDetector; @SuppressWarnings("unused") public ScaleImageView(Context context, AttributeSet attr) { super(context, attr); mContext = context; initialize(); } public void setActivity(Activity activity) { mActivity = activity; } public ScaleImageView(Context context) { super(context); mContext = context; initialize(); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); initialize(); } @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); initialize(); } @Override public void setImageResource(int resId) { super.setImageResource(resId); initialize(); } private void initialize() { setScaleType(ScaleType.MATRIX); mMatrix = new Matrix(); Drawable d = getDrawable(); if (d != null) { mIntrinsicWidth = d.getIntrinsicWidth(); mIntrinsicHeight = d.getIntrinsicHeight(); setOnTouchListener(this); } mDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { maxZoomTo((int) e.getX(), (int) e.getY()); cutting(); return super.onDoubleTap(e); } @Override public boolean onSingleTapUp(MotionEvent e) { if (mActivity != null) { mActivity.finish(); } return super.onSingleTapUp(e); } @Override public void onLongPress(MotionEvent e) { if (mActivity != null) { mActivity.openOptionsMenu(); } super.onLongPress(e); } }); } @Override protected boolean setFrame(int l, int t, int r, int b) { mWidth = r - l; mHeight = b - t; mMatrix.reset(); int r_norm = r - l; float scale = (float) r_norm / (float) mIntrinsicWidth; // Log.d("justaway", "[setFrame] l:" + l + " t:" + t + " r:" + r + " b:" + b + " width:" + mWidth + " height:" + mHeight + " intrinsicWidth:" + mIntrinsicWidth + " scale:" + scale); int paddingHeight; int paddingWidth; // scaling vertical if (scale * mIntrinsicHeight > mHeight) { scale = (float) mHeight / (float) mIntrinsicHeight; mMatrix.postScale(scale, scale); paddingWidth = (r - mWidth) / 2; paddingHeight = 0; // scaling horizontal } else { mMatrix.postScale(scale, scale); paddingHeight = (b - mHeight) / 2; paddingWidth = 0; } mMatrix.postTranslate(paddingWidth, paddingHeight); setImageMatrix(mMatrix); if (!mIsInitializedScaling) { mIsInitializedScaling = true; // zoomTo(scale, mWidth / 2, mHeight / 2); // Log.d("justaway", "[setFrame] mIsInitializedScaling scale:" + scale); } cutting(); boolean isChanges = super.setFrame(l, t, r, b); if (mMinScale != scale) { mMinScale = scale; isChanges = true; } return isChanges; } protected float getValue(Matrix matrix, int whichValue) { matrix.getValues(mMatrixValues); return mMatrixValues[whichValue]; } protected float getScale() { return getValue(mMatrix, Matrix.MSCALE_X); } public float getTranslateX() { return getValue(mMatrix, Matrix.MTRANS_X); } protected float getTranslateY() { return getValue(mMatrix, Matrix.MTRANS_Y); } protected void maxZoomTo(int x, int y) { if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) { // threshold 0.1f float scale = mMinScale / getScale(); zoomTo(scale, x, y); } else { float scale = 2f / getScale(); zoomTo(scale, x, y); } } public void zoomTo(float scale, int x, int y) { if (getScale() * scale < mMinScale) { return; } if (scale >= 1 && getScale() * scale > MAX_SCALE) { return; } mMatrix.postScale(scale, scale); // move to center mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2, -(mHeight * scale - mHeight) / 2); // move x and y distance mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0); mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale); setImageMatrix(mMatrix); } public void cutting() { int width = (int) (mIntrinsicWidth * getScale()); int height = (int) (mIntrinsicHeight * getScale()); boolean bounds = false; if (getTranslateX() < -(width - mWidth)) { mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0); bounds = true; } else if (getTranslateX() == -(width - mWidth)) { bounds = true; } if (getTranslateX() > 0) { mMatrix.postTranslate(-getTranslateX(), 0); bounds = true; } else if (getTranslateX() == 0) { bounds = true; } if (getTranslateY() < -(height - mHeight)) { mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight)); } if (getTranslateY() > 0) { mMatrix.postTranslate(0, -getTranslateY()); } if (width < mWidth) { mMatrix.postTranslate((mWidth - width) / 2, 0); } if (height < mHeight) { mMatrix.postTranslate(0, (mHeight - height) / 2); } sBounds = bounds; setImageMatrix(mMatrix); } private float distance(float x0, float x1, float y0, float y1) { float x = x0 - x1; float y = y0 - y1; return (float) Math.sqrt(x * x + y * y); } private float displayDistance() { return (float) Math.sqrt(mWidth * mWidth + mHeight * mHeight); } @SuppressWarnings("NullableProblems") @Override public boolean onTouchEvent(MotionEvent event) { if (event == null) { return true; } if (mDetector.onTouchEvent(event)) { return true; } int touchCount = event.getPointerCount(); int action = event.getAction() & MotionEvent.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: if (touchCount >= 2) { mPrevDistance = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); mIsScaling = true; } else { mPrevMoveX = (int) event.getX(); mPrevMoveY = (int) event.getY(); } case MotionEvent.ACTION_MOVE: if (touchCount >= 2 && mIsScaling) { float dist = distance(event.getX(0), event.getX(1), event.getY(0), event.getY(1)); float scale = (dist - mPrevDistance) / displayDistance(); mPrevDistance = dist; scale += 1; scale = scale * scale; zoomTo(scale, mWidth / 2, mHeight / 2); cutting(); } else { int distanceX = mPrevMoveX - (int) event.getX(); int distanceY = mPrevMoveY - (int) event.getY(); mPrevMoveX = (int) event.getX(); mPrevMoveY = (int) event.getY(); mMatrix.postTranslate(-distanceX, -distanceY); cutting(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: if (event.getPointerCount() <= 1) { mIsScaling = false; } break; } return true; } @Override public boolean onTouch(View v, MotionEvent event) { return super.onTouchEvent(event); } }