页面效果如下
当作为viewpager里的一个子item时,解决了滑动事件冲突的问题,各个方法的用途都已经加了注释,不过还是得亲自敲过才行
/**
* Created by Administrator on 2017/7/20.
*/
public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener,ScaleGestureDetector.OnScaleGestureListener,
View.OnTouchListener{
//保证执行一次
private boolean isOnce=false;
//初始化时的缩放的值
private float mInitScale;
//双击放大时到达值
private float mMidScale;
//放大的最大值
private float mMaxScale;
private Matrix mScaleMatrix;
//多点触控需要实现
private ScaleGestureDetector mScaleGestureDetector;
//记录上一次多点触控的数量
private int mLastPointCount;
//记录上一次多点触控的数量
private float mLastX,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 defStyleAttr) {
super(context, attrs, defStyleAttr);
mScaleMatrix=new Matrix();
setScaleType(ScaleType.MATRIX);
mScaleGestureDetector=new ScaleGestureDetector(context,this);
setOnTouchListener(this);
mTouchSlop= ViewConfiguration.get(context).getScaledTouchSlop();
//处理双击事件
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)
{
/*mScaleMatrix.postScale(mMidScale/getScale(),mMidScale/getScale(),x,y);
setImageMatrix(mScaleMatrix);*/
isAutoScale=true;
postDelayed(new AutoScaleRunnable(mMidScale,x,y),18);
}
else
{
/*mScaleMatrix.postScale(mInitScale/getScale(),mInitScale/getScale(),x,y);
setImageMatrix(mScaleMatrix);*/
//开始缩放
isAutoScale=true;
postDelayed(new AutoScaleRunnable(mInitScale,x,y),18);
}
return true;
}
});
}
private class AutoScaleRunnable implements Runnable{
//缩放的目标值
private float mTargetScale;
//缩放的中心点
private float x,y;
private final float BIGGER=1.07f;
private final float SMALL=0.93f;
private float tmpScale;
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);
checkBordAndCenterWhenScale();
setImageMatrix(mScaleMatrix);
float currentScale=getScale();
if ((tmpScale>1.0f&¤tScale<mTargetScale)||(tmpScale<1.0f&¤tScale>mTargetScale))
{
//重复执行当前方法
postDelayed(this,18);
}
else
{
isAutoScale=false;
float scale=mTargetScale/currentScale;
mScaleMatrix.postScale(scale,scale,x,y);
checkBordAndCenterWhenScale();
setImageMatrix(mScaleMatrix);
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
//获取imageview加载完成的图片
@Override
public void onGlobalLayout() {
if (!isOnce)
{
//得到控件的宽和高
int width = getWidth();
int height = getHeight();
//得到我们的图片,以及宽和高
Drawable drawable = getDrawable();
if (drawable==null)
{
return;
}
int dWidth = drawable.getIntrinsicWidth();
int dHeight = drawable.getIntrinsicHeight();
//缩放值
float scale=1.0f;
//如果图片的宽度大于控件的宽度,图片的高度小于控件的高度,我们将其缩小
if (dWidth>width&&dHeight<height)
{
scale=width*1.0f/dWidth;
}
if (dHeight>height&&dWidth<width)
{
scale=height*1.0f/dHeight;
}
if (dWidth>width&&dHeight>height)
{
scale=Math.min(width*1.0f/dWidth,height*1.0f/dHeight);
}
if (dWidth<width&&dHeight<height)
{
scale=Math.min(width*1.0f/dWidth,height*1.0f/dHeight);
}
//得到初始化时缩放的比例
mInitScale=scale;
mMaxScale=mInitScale*4;
mMidScale=mInitScale*2;
//将图片移动至控件的中心
int dx = width / 2 - dWidth / 2;
int dy = height / 2 - dHeight / 2;
mScaleMatrix.postTranslate(dx,dy);
//以控件中心作为图片缩放的中心
mScaleMatrix.postScale(mInitScale,mInitScale,width/2,height/2);
setImageMatrix(mScaleMatrix);
isOnce=true;
}
}
//获得当前的缩放值
public float getScale()
{
float [] values=new float[9];
mScaleMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
//缩放进行中
//detector 可以捕获缩放时的手势,比如缩放比例
@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)
{
scaleFactor=mMaxScale/scale;
}
//一多指触控的中心作为缩放的中心
mScaleMatrix.postScale(scaleFactor,scaleFactor,detector.getFocusX(),detector.getFocusY());
checkBordAndCenterWhenScale();
setImageMatrix(mScaleMatrix);
}
return true;
}
//获得放大或缩小后的图片的宽高以及四点坐标
private RectF getMatrixRectF()
{
Matrix matrix=mScaleMatrix;
RectF rectF = new RectF();
Drawable drawable = getDrawable();
if (drawable!=null)
{
rectF.set(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());
matrix.mapRect(rectF);
}
return rectF;
}
//检测边界和中心,也即在缩放的时候进行边界控制以及我们的位置的控制
private void checkBordAndCenterWhenScale() {
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);
}
//监测边界,在移动时
private void checkBorderWhenTranslate() {
RectF rectF = getMatrixRectF();
float deltaX=0;
float deltaY=0;
int width = getWidth();
int height = getHeight();
if (rectF.top>0&&isCheckTopAndBottom)
{
deltaY=-rectF.top;
}
if (rectF.bottom<height&&isCheckTopAndBottom)
{
deltaY=height-rectF.bottom;
}
if (rectF.left>0&&isCheckLeftAndRight)
{
deltaX=-rectF.left;
}
if (rectF.right<width&&isCheckLeftAndRight)
{
deltaX=width-rectF.right;
}
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处理
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 (mLastPointCount!=pointerCount)
{
isCanDrag=false;
mLastX=x;
mLastY=y;
}
mLastPointCount=pointerCount;
RectF rectF1 = getMatrixRectF();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
//当宽度或者高度大于控件的宽高时,要求父布局不拦截滑动事件,解决与viewpager的滑动冲突
if (rectF1.width()>getWidth()+0.01||rectF1.height()>getHeight()+0.01)
{
if (getParent() instanceof ViewPager)
{
getParent().requestDisallowInterceptTouchEvent(true);
}
}
break;
case MotionEvent.ACTION_MOVE:
if (rectF1.width()>getWidth()+0.01||rectF1.height()>getHeight()+0.01)
{
if (getParent() instanceof ViewPager)
{
getParent().requestDisallowInterceptTouchEvent(true);
}
}
float dx=x-mLastX;
float dy=y-mLastY;
if (isMoveAction(dx,dy))
{
isCanDrag=true;
RectF rectF = getMatrixRectF();
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:
mLastPointCount=0;
break;
}
return true;
}
//判断是否是有效的move
private boolean isMoveAction(float dx, float dy) {
return Math.sqrt(dx*dx+dy*dy)>mTouchSlop;
}
}