Skip to content

Instantly share code, notes, and snippets.

@geminiwen
Last active February 1, 2016 14:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save geminiwen/c3f66c518476fbf6f9cd to your computer and use it in GitHub Desktop.
Save geminiwen/c3f66c518476fbf6f9cd to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
/**
* Created by geminiwen on 15/4/30.
*/
public class ScalableImageView extends ImageView{
public ScalableImageView(Context context) {
this(context, null);
}
public ScalableImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScalableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private double mRespectRatio = 1.0;
private double mTranslateX, mTranslateY;
private float mDownPointerX, mDownPointerY;
private int mDownPointerId, mScalePointerId;
private float centerPointX, centerPointY;
private double mOldDistance;
private float[] mMatrixValues = new float[9];
private Matrix mTransformMatrix = new Matrix();
//防止重复alloc memory
private RectF mTempSrc = new RectF();
private RectF mTempDst = new RectF();
private double fingerDistance(MotionEvent event) {
float disX = Math.abs(event.getX(0) - event.getX(1));
float disY = Math.abs(event.getY(0) - event.getY(1));
return Math.sqrt(disX * disX + disY * disY);
}
private void centerPointBetweenFingers(MotionEvent event) {
float xPoint0 = event.getX(0);
float yPoint0 = event.getY(0);
float xPoint1 = event.getX(1);
float yPoint1 = event.getY(1);
centerPointX = (xPoint0 + xPoint1) / 2;
centerPointY = (yPoint0 + yPoint1) / 2;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//看了源码,知道在onLayout setFrame之后,其中的drawable才有大小
resetMatrix();
setImageMatrix(mTransformMatrix);
}
@Override
public void setScaleType(ScaleType scaleType) {
if (!scaleType.equals(ScaleType.MATRIX)) {
throw new RuntimeException("Only Support matrix scale type");
}
super.setScaleType(scaleType);
}
private void resetMatrix() {
Drawable drawable = getDrawable();
if (drawable != null) {
//获取 fitCenter的效果
int vwidth = getWidth();
int vheight = getHeight();
int dwidth = drawable.getIntrinsicWidth();
int dheight = drawable.getIntrinsicHeight();
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mTransformMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER);
} else {
mTransformMatrix.reset();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
int index = MotionEventCompat.getActionIndex(event);
mDownPointerId = MotionEventCompat.getPointerId(event, index);
mDownPointerY = MotionEventCompat.getY(event, index);
mDownPointerX = MotionEventCompat.getX(event, index);
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
//如果第二只手指按下
if (event.getPointerCount() < 3 ) {
int index = MotionEventCompat.getActionIndex(event);
mScalePointerId = MotionEventCompat.getPointerId(event, index);
mOldDistance = fingerDistance(event);
//计算两只手指中心的距离
centerPointBetweenFingers(event);
}
break;
}
case MotionEvent.ACTION_MOVE: {
Drawable topLevelDrawable = getDrawable();
if (topLevelDrawable == null) {
break;
}
if (event.getPointerCount() == 1) {
float x = event.getX();
float y = event.getY();
float deltaX = x - mDownPointerX;
float deltaY = y - mDownPointerY;
mTranslateX += deltaX;
mTranslateY += deltaY;
mDownPointerX = x;
mDownPointerY = y;
//拖动
} else if (event.getPointerCount() == 2) {
//缩放
float oldCenterX = centerPointX;
float oldCenterY = centerPointY;
//计算中心距离
centerPointBetweenFingers(event);
mTranslateX += (centerPointX - oldCenterX);
mTranslateY += (centerPointY - oldCenterY);
//计算距离
double distance = fingerDistance(event);
double respectRatio = mRespectRatio * distance / mOldDistance;
respectRatio = respectRatio < 1 ? 1 : respectRatio;
mRespectRatio = respectRatio;
mOldDistance = distance;
}
invalidate();
break;
}
case MotionEvent.ACTION_POINTER_UP: {
//如果其中一个手指起来了,那么处理掉,使其另外一只手指能进行平移
mOldDistance = -1;
int index = MotionEventCompat.getActionIndex(event);
int id = MotionEventCompat.getPointerId(event, index);
int nextIndex;
//找到另外一只手指的位置
if (id == mDownPointerId) {
mDownPointerId = mScalePointerId;
nextIndex = MotionEventCompat.findPointerIndex(event, mScalePointerId);
} else if (id == mScalePointerId) {
nextIndex = MotionEventCompat.findPointerIndex(event, mDownPointerId);
} else {
nextIndex = -1;
}
if (nextIndex == -1) {
break;
}
mDownPointerX = MotionEventCompat.getX(event, nextIndex);
mDownPointerY = MotionEventCompat.getY(event, nextIndex);
mScalePointerId = -1;
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
mDownPointerId = -1;
mDownPointerX = mDownPointerY = -1;
break;
}
}
resolveTransform();
return true;
}
private void resolveTransform() {
resetMatrix();
mTransformMatrix.getValues(mMatrixValues);
int vwidth = getWidth();
int vheight = getHeight();
float ratio = mMatrixValues[0];
Drawable d = getDrawable();
if (d != null) {
//计算单指移动的边界
int dwidth = d.getIntrinsicWidth();
int dheight = d.getIntrinsicHeight();
float minWidth = 0.0f, maxWidth = 0.0f;
float minHeight = 0.0f, maxHeight = 0.0f;
//计算缩放后的drawable尺寸,ratio表示经过fitCenter之后的比例 [ratio, 0, 0] [0, ratio, 0] [0, 0, 1]
//mRespectRatio 双指拖放后的尺寸
dwidth *= ratio * mRespectRatio;
dheight *= ratio * mRespectRatio;
//如果drawable的宽度连边缘都没达到(就是黑边)
if (dwidth <= vwidth) {
minWidth = maxWidth = 0.0f;
} else {
minWidth = -(dwidth - vwidth) / 2;
maxWidth = -minWidth;
}
if (dheight <= vheight) {
minHeight = maxHeight = 0.0f;
} else {
minHeight = -(dheight - vheight) / 2;
maxHeight = -minHeight;
}
if (mTranslateX < minWidth) {
mTranslateX = minWidth;
}
if (mTranslateX > maxWidth) {
mTranslateX = maxWidth;
}
if (mTranslateY < minHeight) {
mTranslateY = minHeight;
}
if (mTranslateY > maxHeight) {
mTranslateY = maxHeight;
}
}
float centerX = vwidth * 1.0f / 2;
float centerY = vheight * 1.0f / 2;
//先进行缩放
mTransformMatrix.postScale((float) mRespectRatio, (float) mRespectRatio, centerX, centerY);
//后进行平移
mTransformMatrix.postTranslate((float) mTranslateX, (float) mTranslateY);
setImageMatrix(mTransformMatrix);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment