/* * TouchImageView.java * By: Michael Ortiz * Updated By: Patrick Lackemacher * ------------------- * Extends Android ImageView to include pinch zooming and panning. */ package ee.ajapaik.android.external.touch; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.PointF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.widget.ImageView; public class TouchImageView extends ImageView { Matrix matrix = new Matrix(); // We can be in one of these 3 states static final int NONE = 0; static final int DRAG = 1; static final int ZOOM = 2; int mode = NONE; // Remember some things for zooming PointF last = new PointF(); PointF start = new PointF(); float minScale = 0.5f; float maxScale = 4.0f; float[] m; float redundantXSpace, redundantYSpace; float width, height; static final int CLICK = 3; float saveScale = 1f; float right, bottom, origWidth, origHeight, bmWidth, bmHeight; private float alpha = 0.5f; private float newAlpha = 0.5f; ScaleGestureDetector mScaleDetector; Context context; public TouchImageView(Context context) { super(context); sharedConstructing(context); } public TouchImageView(Context context, AttributeSet attrs) { super(context, attrs); sharedConstructing(context); } private float clamp(float value, float min, float max) { return Math.min(Math.max(min, value), max); } public float getScaleFactor() { return saveScale; } private boolean scaleChanged = false; private void sharedConstructing(Context context) { super.setClickable(true); this.context = context; mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); m = new float[9]; setImageMatrix(matrix); setScaleType(ScaleType.MATRIX); TouchImageView.this.setAlpha(128); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { scaleChanged = false; mScaleDetector.onTouchEvent(event); matrix.getValues(m); float x = m[Matrix.MTRANS_X]; float y = m[Matrix.MTRANS_Y]; PointF curr = new PointF(event.getX(), event.getY()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: last.set(event.getX(), event.getY()); start.set(last); mode = DRAG; break; case MotionEvent.ACTION_MOVE: if (mode == DRAG && !scaleChanged) { float startVal = (start.y / (float)(v.getHeight())); float curVal = (event.getY() / (float)(v.getHeight())); float diff = curVal - startVal; newAlpha = alpha + diff; newAlpha = clamp(newAlpha, 0.0f, 1.0f); // System.out.println("alpha=" + alpha + " newAlpha=" + newAlpha + " startVal=" + startVal + " curVal=" + curVal); //clamp TouchImageView.this.setAlpha((int)(clamp(newAlpha*255.0f, 0.0f, 255.0f))); } break; case MotionEvent.ACTION_UP: mode = NONE; alpha = newAlpha; int xDiff = (int) Math.abs(curr.x - start.x); int yDiff = (int) Math.abs(curr.y - start.y); if (xDiff < CLICK && yDiff < CLICK) performClick(); break; case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; } setImageMatrix(matrix); invalidate(); return true; // indicate event was handled } }); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); if (bm != null) { bmWidth = bm.getWidth(); bmHeight = bm.getHeight(); } // animate to 0.5f, 10fps final int[] counter = {0}; TouchImageView.this.setAlpha(0); postDelayed(new Runnable() { @Override public void run() { TouchImageView.this.setAlpha(Math.min(12 * (counter[0] + 1), 128)); counter[0]++; if (counter[0] < 10) { postDelayed(this, 50); } } }, 50); } private void animateToAlpha(final int diff, final int dest, final int frameDelay) { postDelayed(new Runnable() { @Override public void run() { int start = (int)((alpha) * 255); if ((diff > 0 && dest > start) || (diff < 0 && dest < start)) { TouchImageView.this.setAlpha(dest + diff); alpha = (float)(dest + diff) / 255.0f; postDelayed(this, 50); } else { TouchImageView.this.setAlpha(dest); alpha = (float)(dest) / 255.0f; } } }, frameDelay); } public void setMaxZoom(float x) { maxScale = x; } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { float mScaleFactor = detector.getScaleFactor(); float origScale = saveScale; saveScale *= mScaleFactor; if (saveScale > maxScale) { saveScale = maxScale; mScaleFactor = maxScale / origScale; } else if (saveScale < minScale) { saveScale = minScale; mScaleFactor = minScale / origScale; } matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2); scaleChanged = true; return true; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); // Fit to screen. float scale; float scaleX = (float) width / (float) bmWidth; float scaleY = (float) height / (float) bmHeight; scale = Math.min(scaleX, scaleY); matrix.setScale(scale, scale); setImageMatrix(matrix); saveScale = 1f; // Center the image redundantYSpace = (float) height - (scale * (float) bmHeight); redundantXSpace = (float) width - (scale * (float) bmWidth); redundantYSpace /= (float) 2; redundantXSpace /= (float) 2; System.out.println("redundantXSpace: " + redundantXSpace + ", redundantYspace: " + redundantYSpace); matrix.postTranslate(redundantXSpace, redundantYSpace); origWidth = width - 2 * redundantXSpace; origHeight = height - 2 * redundantYSpace; right = width * saveScale - width - (2 * redundantXSpace * saveScale); bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); setImageMatrix(matrix); } }