跟着hyman做的ZoomImageView,实现了双击放大,缩小,多指触碰放大缩小的功能;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
/**
* Created by Administrator on 2016/5/12.
*/
public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {
private Boolean first = false;
private float initScale;//初始化的缩放量
private float maxScale;//最大缩放量
private float midScale;//中间缩放量
private Matrix matrix;
private int lastTouchPointCount = 0;//上次屏幕触点数
private float lastX;
private float lastY;
private int onTouchSlop;//开始移动的滑动距离
private Boolean isCanDrag = false;//是否可以移动
private Boolean isNeedCheckTopAndBottom;//是否需要考虑top和boottom出现白边
private Boolean isNeedCheckLeftAndRight;//是否需要考虑left和right出现白边
private GestureDetector gestureDetector;//手势监听
private ScaleGestureDetector scaleGestureDetector;//缩放手势监听
public ZoomImageView(Context context) {
this(context, null);
}
public ZoomImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setScaleType(ScaleType.MATRIX);//设置缩放类型
matrix = new Matrix();
scaleGestureDetector = new ScaleGestureDetector(getContext(), this);
setOnTouchListener(this);
onTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();//获取系统的默认开始移动的滑动距离
/**
* 处理双击图片放大和缩小
*/
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
float scale = getScale();
float eventX = e.getX();
float eventY = getY();
if (scale < midScale) {
postDelayed(new AutoScaleRunnable(eventX, eventY, maxScale), 16);
} else {
postDelayed(new AutoScaleRunnable(eventX, eventY, initScale), 16);
}
return true;
}
});
}
/**
* 用线程来实现图片缓慢变大和缩小
*/
public class AutoScaleRunnable implements Runnable {
private float clickX;
private float clickY;
private float targetScale;
private float scaleTo;
private float scaleBig = 1.07f;
private float scaleSmall = 0.93f;
public AutoScaleRunnable(float clickX, float clickY, float targetScale) {
this.clickX = clickX;
this.clickY = clickY;
this.targetScale = targetScale;
if (getScale() < targetScale) {
scaleTo = scaleBig;
}
if (getScale() > targetScale) {
scaleTo = scaleSmall;
}
}
@Override
public void run() {
matrix.postScale(scaleTo, scaleTo, clickX, clickY);
setImageMatrix(matrix);
checkBorderWhenScale();
float currentScale = getScale();
if ((currentScale < targetScale && scaleTo > 1.0f) || (currentScale > targetScale && scaleTo < 1.0f)) {
postDelayed(this, 16);
} else {
matrix.postScale(targetScale / currentScale, targetScale / currentScale, clickX, clickY);
setImageMatrix(matrix);
checkBorderWhenScale();
}
}
}
/**
* 注册 GlobalLayoutListener监听事件
*/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
/**
* 移除监听事件
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
/**
* 把图片定位到屏幕中央,并进行初始化缩放适应屏幕
*/
@Override
public void onGlobalLayout() {
if (!first) {
int with = getWidth();
int height = getHeight();
Drawable drawable = getDrawable();
if (drawable == null) return;
int draWith = drawable.getIntrinsicWidth();
int draHeight = drawable.getIntrinsicHeight();
if (draWith > with && draHeight < height) {
initScale = with * 1.0f / draWith;
}
if ((draWith > with && draHeight > height) || (draWith < with && draHeight < height)) {
initScale = Math.min(with * 1.0f / draWith, height * 1.0f / draHeight);
}
if (draWith < with && draHeight > height) {
// initScale = height * 1.0f / draHeight;
initScale = getWidth() * 1.0f / draHeight;
}
maxScale = 4f * initScale;
midScale = 2f * initScale;
float dx = with / 2.0f - draWith / 2.0f;
float dy = height / 2.0f - draHeight / 2.0f;
matrix.postTranslate(dx, dy);
matrix.postScale(initScale, initScale, with / 2.0f, height / 2.0f);
this.setImageMatrix(matrix);
first = true;
}
}
/**
* 获得缩放图片中图片的缩放值
*
* @return
*/
public float getScale() {
float values[] = new float[9];
matrix.getValues(values);
return values[Matrix.MSCALE_X];
}
/**
* 获的缩放中图片的边界信息;
*
* @return
*/
private RectF getDrawableInfo() {
Drawable drawable = getDrawable();
if (drawable == null)
return null;
int draWith = drawable.getIntrinsicWidth();
int draHeight = drawable.getIntrinsicHeight();
RectF rectF = new RectF();
rectF.set(0, 0, draWith, draHeight);
matrix.mapRect(rectF);//why
return rectF;
}
/**
* 处理多触点缩放
* @param detector
* @return
*/
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = getScale();
float scaleFactor = detector.getScaleFactor();
if (getDrawable() == null) return true;
if ((scale < maxScale && scaleFactor > 1.0f) || (scale > initScale && scaleFactor < 1.0f)) {
if (scale * scaleFactor > maxScale) {
scaleFactor = maxScale / scale;
}
if (scale * scaleFactor < initScale) {
scaleFactor = initScale / scale;
}
matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
setImageMatrix(matrix);
checkBorderWhenScale();
}
return true;//why
}
/**
* 缩放中边缘控制,防止边缘出现白边
*/
private void checkBorderWhenScale() {
RectF draRectF = getDrawableInfo();
if (draRectF == null) return;
int with = getWidth();
int height = getHeight();
float dx = 0;
float dy = 0;
if (draRectF.width() >= with) {
if (draRectF.left > 0) {
dx = -draRectF.left;
}
if (draRectF.right < with) {
dx = with - draRectF.right;
}
}
if (draRectF.height() >= height) {
if (draRectF.top > 0) {
dy = -draRectF.top;
}
if (draRectF.bottom < height) {
dy = height - draRectF.bottom;
}
}
if (draRectF.height() < height) {
dy = (height / 2.0f - draRectF.bottom + draRectF.height() / 2.0f);
}
if (draRectF.width() < with) {
dx = (with / 2.0f - draRectF.right + draRectF.width() / 2.0f);
}
matrix.postTranslate(dx, dy);
setImageMatrix(matrix);
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;//why
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
/**
* 处理touch事件
* @param v
* @param event
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event))//传入touch事件给GestureDetector
return true;
scaleGestureDetector.onTouchEvent(event);//传入touch事件给scaleGestureDetector
int touchPointCount = event.getPointerCount();
float x = 0;
float y = 0;
for (int i = 0; i < touchPointCount; i++) {
x += event.getX(i);
y += event.getY(i);
}
float centerX = x / touchPointCount;
float centerY = y / touchPointCount;
if (lastTouchPointCount != touchPointCount) {
isCanDrag = false;
lastX = centerX;
lastY = centerY;
}
lastTouchPointCount = touchPointCount;
RectF rectF = getDrawableInfo();
switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// if (rectF != null && (rectF.height() > getHeight() || rectF.width() > getWidth())) {
// getParent().requestDisallowInterceptTouchEvent(true);//阻止父布局(viewpager)拦截事件
// }
// break;
case MotionEvent.ACTION_MOVE:
//
// if (rectF != null && (rectF.height() > getHeight() || rectF.width() > getWidth())) {
// getParent().requestDisallowInterceptTouchEvent(true);
// }
float dx = centerX - lastX;
float dy = centerY - lastY;
if (!isCanDrag) {
isCanDrag = isMoveAction(dx, dy);
}
if (isCanDrag) {
isNeedCheckLeftAndRight = true;
isNeedCheckTopAndBottom = true;
RectF drawRecF = getDrawableInfo();
if (drawRecF != null) {
if (drawRecF.width() < getWidth()) {
isNeedCheckLeftAndRight = false;
dx = 0;
}
if (drawRecF.height() < getHeight()) {
dy = 0;
isNeedCheckTopAndBottom = false;
}
matrix.postTranslate(dx, dy);
setImageMatrix(matrix);
checkBorderWhenTraslate();
}
}
lastX = centerX;
lastY = centerY;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
lastTouchPointCount = 0;
break;
default:
break;
}
return true;
}
/**
* 保证触点缩放后平移不出现白边
*/
public void checkBorderWhenTraslate() {
RectF rectF2 = getDrawableInfo();
float dx = 0;
float dy = 0;
if (rectF2 == null) return;
if (rectF2.right < getWidth() && isNeedCheckLeftAndRight) {
dx = getWidth() - rectF2.right;
}
if (rectF2.left > 0 && isNeedCheckLeftAndRight) {
dx = -rectF2.left;
}
if (rectF2.top > 0 && isNeedCheckTopAndBottom) {
dy = -rectF2.top;
}
if (rectF2.bottom < getHeight() && isNeedCheckTopAndBottom) {
dy = getHeight() - rectF2.bottom;
}
matrix.postTranslate(dx, dy);
setImageMatrix(matrix);
}
/**
* 判断是否可以移动
*
* @param dx
* @param dy
* @return
*/
private boolean isMoveAction(float dx, float dy) {
return Math.sqrt(dx * dx + dy * dy) > onTouchSlop;
}
}