package com.example.pic; |
import android.content.Context; |
import android.graphics.Matrix; |
import android.graphics.RectF; |
import android.graphics.drawable.Drawable; |
import android.support.v4.view.ViewPager; |
import android.util.AttributeSet; |
import android.view.GestureDetector; |
import android.view.MotionEvent; |
import android.view.ScaleGestureDetector; |
import android.view.ScaleGestureDetector.OnScaleGestureListener; |
import android.view.View; |
import android.view.View.OnTouchListener; |
import android.view.ViewConfiguration; |
import android.view.ViewTreeObserver.OnGlobalLayoutListener; |
import android.widget.ImageView; |
public class ZoomImageView extends ImageView implements OnGlobalLayoutListener, |
OnScaleGestureListener, OnTouchListener { |
//用于加载图片只执行一次 |
private boolean mOnce; |
/** |
* 初始化时缩放的值 |
*/ |
private float mInitScale; |
/** |
* 双击放大值到达的值 |
*/ |
private float mMidScale; |
/** |
* 放大的最大值 |
*/ |
private float mMaxScale; |
//矩阵比例 |
private Matrix mScaleMatrix; |
/** |
* 捕获用户多指触控时缩放的比例 |
*/ |
private ScaleGestureDetector mScaleGestureDetector; |
// **********自由移动的变量*********** |
/** |
* 记录上一次多点触控的数量 |
*/ |
private int mLastPointerCount; |
//记录上一次的X、Y坐标 |
private float mLastX; |
private float mLastY; |
//是否超过屏幕宽度或高度 |
private int mTouchSlop; |
//判断是否能拖动 |
private boolean isCanDrag; |
|
private boolean isCheckLeftAndRight; |
private boolean isCheckTopAndBottom; |
// *********双击放大与缩小********* |
private GestureDetector mGestureDetector; |
private boolean isAutoScale; |
public ZoomImageView(Context context) { |
this (context, null ); |
} |
public ZoomImageView(Context context, AttributeSet attrs) { |
this (context, attrs, 0 ); |
} |
public ZoomImageView(Context context, AttributeSet attrs, int defStyle) { |
super (context, attrs, defStyle); |
// init |
mScaleMatrix = new Matrix(); |
setScaleType(ScaleType.MATRIX); |
setOnTouchListener( this ); |
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); |
mScaleGestureDetector = new ScaleGestureDetector(context, this ); |
mGestureDetector = new GestureDetector(context, |
new GestureDetector.SimpleOnGestureListener() { |
@Override |
public boolean onDoubleTap(MotionEvent e) { |
if (isAutoScale) { |
return true ; |
} |
float x = e.getX(); |
float y = e.getY(); |
if (getScale() < mMidScale) { |
postDelayed( new AutoScaleRunnable(mMidScale, x, y), 16 ); |
isAutoScale = true ; |
} else { |
postDelayed( new AutoScaleRunnable(mInitScale, x, y), 16 ); |
isAutoScale = true ; |
} |
return true ; |
} |
}); |
} |
/** |
* 自动放大与缩小 |
* |
* @author zhangyan@lzt.com.cn |
* |
*/ |
private class AutoScaleRunnable implements Runnable { |
/** |
* 缩放的目标值 |
*/ |
private float mTargetScale; |
// 缩放的中心点 |
private float x; |
private float y; |
private final float BIGGER = 1 .07f; |
private final float SMALL = 0 .93f; |
private float tmpScale; |
/** |
* @param mTargetScale |
* @param x |
* @param y |
*/ |
public AutoScaleRunnable( float mTargetScale, float x, float y) { |
this .mTargetScale = mTargetScale; |
this .x = x; |
this .y = y; |
if (getScale() < mTargetScale) { |
tmpScale = BIGGER; |
} |
if (getScale() > mTargetScale) { |
tmpScale = SMALL; |
} |
} |
@Override |
public void run() { |
//进行缩放 |
mScaleMatrix.postScale(tmpScale, tmpScale, x, y); |
checkBorderAndCenterWhenScale(); |
setImageMatrix(mScaleMatrix); |
float currentScale = getScale(); |
if ((tmpScale > 1 .0f && currentScale <mTargetScale) ||(tmpScale< 1 .0f &¤tScale>mTargetScale)) { |
//这个方法是重新调用run()方法 |
postDelayed( this , 16 ); |
} else { |
//设置为我们的目标值 |
float scale = mTargetScale/currentScale; |
mScaleMatrix.postScale(scale, scale, x, y); |
checkBorderAndCenterWhenScale(); |
setImageMatrix(mScaleMatrix); |
isAutoScale = false ; |
} |
} |
} |
/** |
* 获取ImageView加载完成的图片 |
*/ |
@Override |
public void onGlobalLayout() { |
if (!mOnce) { |
// 得到控件的宽和高 |
int width = getWidth(); |
int height = getHeight(); |
// 得到我们的图片,以及宽和高 |
Drawable drawable = getDrawable(); |
if (drawable == null ) { |
return ; |
} |
int dh = drawable.getIntrinsicHeight(); |
int dw = drawable.getIntrinsicWidth(); |
float scale = 1 .0f; |
// 图片的宽度大于控件的宽度,图片的高度小于空间的高度,我们将其缩小 |
if (dw > width && dh < height) { |
scale = width * 1 .0f / dw; |
} |
// 图片的宽度小于控件的宽度,图片的高度大于空间的高度,我们将其缩小 |
if (dh > height && dw < width) { |
scale = height * 1 .0f / dh; |
} |
// 缩小值 |
if (dw > width && dh > height) { |
scale = Math.min(width * 1 .0f / dw, height * 1 .0f / dh); |
} |
// 放大值 |
if (dw < width && dh < height) { |
scale = Math.min(width * 1 .0f / dw, height * 1 .0f / dh); |
} |
/** |
* 得到了初始化时缩放的比例 |
*/ |
mInitScale = scale; |
mMaxScale = mInitScale * 4 ; |
mMidScale = mInitScale * 2 ; |
// 将图片移动至控件的中间 |
int dx = getWidth() / 2 - dw / 2 ; |
int dy = getHeight() / 2 - dh / 2 ; |
mScaleMatrix.postTranslate(dx, dy); |
mScaleMatrix.postScale(mInitScale, mInitScale, width / 2 , |
height / 2 ); |
setImageMatrix(mScaleMatrix); |
mOnce = true ; |
} |
} |
/** |
* 注册OnGlobalLayoutListener这个接口 |
*/ |
@Override |
protected void onAttachedToWindow() { |
super .onAttachedToWindow(); |
getViewTreeObserver().addOnGlobalLayoutListener( this ); |
} |
/** |
* 取消OnGlobalLayoutListener这个接口 |
*/ |
@SuppressWarnings ( "deprecation" ) |
@Override |
protected void onDetachedFromWindow() { |
super .onDetachedFromWindow(); |
getViewTreeObserver().removeGlobalOnLayoutListener( this ); |
} |
/** |
* 获取当前图片的缩放值 |
* |
* @return |
*/ |
public float getScale() { |
float [] values = new float [ 9 ]; |
mScaleMatrix.getValues(values); |
return values[Matrix.MSCALE_X]; |
} |
// 缩放区间时initScale maxScale |
@Override |
public boolean onScale(ScaleGestureDetector detector) { |
float scale = getScale(); |
float scaleFactor = detector.getScaleFactor(); |
if (getDrawable() == null ) { |
return true ; |
} |
// 缩放范围的控制 |
if ((scale < mMaxScale && scaleFactor > 1 .0f) |
|| (scale > mInitScale && scaleFactor < 1 .0f)) { |
if (scale * scaleFactor < mInitScale) { |
scaleFactor = mInitScale / scale; |
} |
if (scale * scaleFactor > mMaxScale) { |
scale = mMaxScale / scale; |
} |
// 缩放 |
mScaleMatrix.postScale(scaleFactor, scaleFactor, |
detector.getFocusX(), detector.getFocusY()); |
checkBorderAndCenterWhenScale(); |
setImageMatrix(mScaleMatrix); |
} |
return true ; |
} |
/** |
* 获得图片放大缩小以后的宽和高,以及left,right,top,bottom |
* |
* @return |
*/ |
private RectF getMatrixRectF() { |
Matrix matrix = mScaleMatrix; |
RectF rectF = new RectF(); |
Drawable d = getDrawable(); |
if (d != null ) { |
rectF.set( 0 , 0 , d.getIntrinsicWidth(), d.getIntrinsicHeight()); |
matrix.mapRect(rectF); |
} |
return rectF; |
} |
/** |
* 在缩放的时候进行边界以及我们的位置的控制 |
*/ |
private void checkBorderAndCenterWhenScale() { |
RectF rectF = getMatrixRectF(); |
float deltaX = 0 ; |
float deltaY = 0 ; |
int width = getWidth(); |
int height = getHeight(); |
// 缩放时进行边界检测,防止出现白边 |
if (rectF.width() >= width) { |
if (rectF.left > 0 ) { |
deltaX = -rectF.left; |
} |
if (rectF.right < width) { |
deltaX = width - rectF.right; |
} |
} |
if (rectF.height() >= height) { |
if (rectF.top > 0 ) { |
deltaY = -rectF.top; |
} |
if (rectF.bottom < height) { |
deltaY = height - rectF.bottom; |
} |
} |
/** |
* 如果宽度或高度小于空间的宽或者高,则让其居中 |
*/ |
if (rectF.width() < width) { |
deltaX = width / 2f - rectF.right + rectF.width() / 2f; |
} |
if (rectF.height() < height) { |
deltaY = height / 2f - rectF.bottom + rectF.height() / 2f; |
} |
mScaleMatrix.postTranslate(deltaX, deltaY); |
} |
@Override |
public boolean onScaleBegin(ScaleGestureDetector detector) { |
return true ; |
} |
@Override |
public void onScaleEnd(ScaleGestureDetector detector) { |
} |
@Override |
public boolean onTouch(View v, MotionEvent event) { |
if (mGestureDetector.onTouchEvent(event)) { |
return true ; |
} |
mScaleGestureDetector.onTouchEvent(event); |
float x = 0 ; |
float y = 0 ; |
// 拿到多点触控的数量 |
int pointerCount = event.getPointerCount(); |
for ( int i = 0 ; i < pointerCount; i++) { |
x += event.getX(i); |
y += event.getY(i); |
} |
x /= pointerCount; |
y /= pointerCount; |
if (mLastPointerCount != pointerCount) { |
isCanDrag = false ; |
mLastX = x; |
mLastY = y; |
} |
mLastPointerCount = pointerCount; |
RectF rectF = getMatrixRectF(); |
switch (event.getAction()) { |
case MotionEvent.ACTION_DOWN: |
if (rectF.width()>getWidth() + 0.01 || rectF.height()>getHeight()+ 0.01 ) { |
if (getParent() instanceof ViewPager) |
getParent().requestDisallowInterceptTouchEvent( true ); |
} |
break ; |
case MotionEvent.ACTION_MOVE: |
if (rectF.width()>getWidth()+ 0.01 || rectF.height()>getHeight()+ 0.01 ) { |
if (getParent() instanceof ViewPager) |
getParent().requestDisallowInterceptTouchEvent( true ); |
} |
float dx = x - mLastX; |
float dy = y - mLastY; |
if (!isCanDrag) { |
isCanDrag = isMoveAction(dx, dy); |
} |
if (isCanDrag) { |
if (getDrawable() != null ) { |
isCheckLeftAndRight = isCheckTopAndBottom = true ; |
// 如果宽度小于控件宽度,不允许横向移动 |
if (rectF.width() < getWidth()) { |
isCheckLeftAndRight = false ; |
dx = 0 ; |
} |
// 如果高度小于控件高度,不允许纵向移动 |
if (rectF.height() < getHeight()) { |
isCheckTopAndBottom = false ; |
dy = 0 ; |
} |
mScaleMatrix.postTranslate(dx, dy); |
checkBorderWhenTranslate(); |
setImageMatrix(mScaleMatrix); |
} |
} |
mLastX = x; |
mLastY = y; |
break ; |
case MotionEvent.ACTION_UP: |
case MotionEvent.ACTION_CANCEL: |
mLastPointerCount = 0 ; |
break ; |
default : |
break ; |
} |
return true ; |
} |
/** |
* 当移动时进行边界检查 |
*/ |
private void checkBorderWhenTranslate() { |
RectF rectF = getMatrixRectF(); |
float deltaX = 0 ; |
float deltaY = 0 ; |
int width = getWidth(); |
int heigth = getHeight(); |
if (rectF.top > 0 && isCheckTopAndBottom) { |
deltaY = -rectF.top; |
} |
if (rectF.bottom < heigth && isCheckTopAndBottom) { |
deltaY = heigth - rectF.bottom; |
} |
if (rectF.left > 0 && isCheckLeftAndRight) { |
deltaX = -rectF.left; |
} |
if (rectF.right < width && isCheckLeftAndRight) { |
deltaX = width - rectF.right; |
} |
mScaleMatrix.postTranslate(deltaX, deltaY); |
} |
/** |
* 判断是否是move |
* |
* @param dx |
* @param dy |
* @return |
*/ |
private boolean isMoveAction( float dx, float dy) { |
return Math.sqrt(dx * dx + dy * dy) > mTouchSlop; |
} |
} |