Last active
February 1, 2016 14:19
-
-
Save geminiwen/c3f66c518476fbf6f9cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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