您的位置:首页 > 其它

多点触控图片 自定义View

2016-03-26 02:05 288 查看
多点触控图片  查看自定义View

详情看hongyang大牛的视频

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
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 warden on 16/3/24.
* <p/>
* 1.初始化,缩小放大图片 {@link #onGlobalLayout}
*/
public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, View.OnTouchListener
{
/**
* 控制初始化操作的变量
*/
private boolean isFirst;
/**
* 初始化显示的值,also 显示最小值
*/
private float mInitScale;
/**
* 双击放大的值
*/
private float mMidScale;
/**
* 显示的最大值
*/
private float mMaxScale;

/**
* 矩阵
*/
private Matrix mScaleMartix;

/**
* 捕获多地触控的缩放比例
*/
private ScaleGestureDetector mScaleGestureDetector;
/**
* 缩放监听
*/
private SimpleOnScaleGesture mOnScaleGesture;

//------------ 分割线
/**
* 记录上一次多点触控的 数量
*/
private int mLastPointerCount;

/**
* 记录上一次手指触控的中心点 x 坐标
*/
private float mLastX;

/**
* 记录上一次手指触控的中心点 y 坐标
*/
private float mLastY;

/**
* 获取系统的值,用于比较是移动的值达到了 移动图片的需求
*/
private int mTouchSlop;

private boolean isCanDrag;

//检查用
/**
* 检查是否 left 或者 right
*/
private boolean isCheckLeftAndRight;
/**
* 检查是否  top or bottom
*/
private boolean isCheckTopBottom;

//------双击放大
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);
mScaleMartix = new Matrix();
setScaleType(ScaleType.MATRIX);
mScaleGestureDetector = new ScaleGestureDetector(context, mOnScaleGesture = new SimpleOnScaleGesture());
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)
//                {
//                    mScaleMartix.postScale(mMidScale / getScale(), mMidScale / getScale(), x, y);
//                } else
//                {
//                    //反之就是大于等于中心值,缩小
//                    mScaleMartix.postScale(mInitScale / getScale(), mInitScale / getScale(), x, y);
//                }
//                setImageMatrix(mScaleMartix);
//获取当前的缩放比例
//如果小于 缩放中心值,就放大到中心值
if (getScale() < mMidScale)
{
postDelayed(new AutoScaleRunnable(mMidScale, x, y), 18);
isAutoScale = true;
} else
{
//反之就是大于等于中心值,缩小
postDelayed(new AutoScaleRunnable(mInitScale, x, y), 18);
isAutoScale = true;
}

return true;
}
});
}

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;

public AutoScaleRunnable(float mTargetScale, float x, float y)
{
this.mTargetScale = mTargetScale;
this.x = x;
this.y = y;
//比目标小 就放大
if (getScale() < mTargetScale)
{
tmpScale = BIGGER;
} else if (getScale() > mTargetScale)
{
//比目标大就缩小
tmpScale = SMALL;
}
}

@Override
public void run()
{
mScaleMartix.postScale(tmpScale, tmpScale, x, y);
checkBorderAndCenterWhenScale();
setImageMatrix(mScaleMartix);
float currentScale = getScale();

if ((tmpScale > 1.0f && currentScale < mTargetScale)
|| (tmpScale < 1.0f && currentScale > mTargetScale))
{
postDelayed(this, 18);
} else
{
//达到效果
float scale = mTargetScale / currentScale;
mScaleMartix.postScale(scale, scale, x, y);
checkBorderAndCenterWhenScale();
setImageMatrix(mScaleMartix);
isAutoScale = false;
}
}
}

/**
* 添加到绘图
*/
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}

/**
* 移除绘图
*/
@Override
protected void onDetachedFromWindow()
{
super.onDetachedFromWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else
{
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}

/**
* 绘图完成,全局的布局完成之后会回调这个方法
* <p/>
* OnGlobalLayoutListener
*/
@Override
public void onGlobalLayout()
{
if (isFirst) return;

/**
* 获取控件的宽度和高度,
* 为什么没有使用屏幕的宽度和高度.
* 比如你又一个ToolBar
*/
int width = getWidth();
int height = getHeight();

//获取图片
Drawable d = getDrawable();

if (d == null) return;

int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();

float scale = 0;

//需要缩小
if (dw > width || dh > height)
{
float w = width * 1.0f / dw;
float h = height * 1.0f / dh;
scale = Math.min(w, h);
}

//放大
if (dw < width || dh < height)
{
float w2 = width * 1.0f / dw;
float h2 = height * 1.0f / dh;
scale = Math.min(w2, h2);
}

//初始化值
mInitScale = scale;
mMidScale = scale * 2;
mMaxScale = scale * 4;

//移动坐标
int dx = getWidth() / 2 - dw / 2;
int dy = getHeight() / 2 - dh / 2;

//移动到中心点
mScaleMartix.postTranslate(dx, dy);
//放大缩小 后两个参数为 缩放中心点的坐标
mScaleMartix.postScale(mInitScale, mInitScale, width / 2, height / 2);
setImageMatrix(mScaleMartix);

isFirst = true;
}

@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=x/pointerCount
x /= pointerCount;
y /= pointerCount;

//触控数量发生了改变
if (mLastPointerCount != pointerCount)
{
//记录 数量
mLastX = x;
mLastY = y;
mLastPointerCount = pointerCount;
isCanDrag = false;
}
RectF rectF = getMatrixRectF();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
//这个状态下不希望 父控件拦截我们的事件
if (rectF.width() > getWidth() + 0.01 || rectF.height() > getHeight() + 0.01)
{
if (getParent() != null)
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_MOVE:
//这个状态下不希望 父控件拦截我们的事件
if (rectF.width() > getWidth() + 0.01 || rectF.height() > getHeight() + 0.01)
{
if (getParent() != null)
getParent().requestDisallowInterceptTouchEvent(true);
}

//计算偏移量
//当前中心点的位置 - 记录中心点的位置
float dx = x - mLastX;
float dy = y - mLastY;

if (!isCanDrag)
{
isCanDrag = isMoveAction(dx, dy);
}

if (isCanDrag)
{
//图片的移动
// RectF rectF = getMatrixRectF();
if (getDrawable() != null)
{
isCheckLeftAndRight = isCheckTopBottom = true;
//如果 下雨空间的宽度,不允许横向移动
if (rectF.width() < getWidth())
{
//这种情况就不需要检查了, 应为图片已经小宇空间的
isCheckLeftAndRight = false;
dx = 0;
}
// 同理,条件不满足不允许纵向移动
if (rectF.height() < getHeight())
{
isCheckTopBottom = false;
dy = 0;
}

mScaleMartix.postTranslate(dx, dy);
checkBorderWhenTranslate();
setImageMatrix(mScaleMartix);

}

}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mLastPointerCount = 0;
break;
}

return true;
}

private void checkBorderWhenTranslate()
{
RectF rectF = getMatrixRectF();

float deltaX = 0;
float deltaY = 0;

int width = getWidth();
int height = getHeight();

if (rectF.top > 0 && isCheckTopBottom)
{
deltaY = -rectF.top;
}

if (rectF.bottom < height && isCheckTopBottom)
{
deltaY = height - rectF.bottom;
}

if (rectF.left > 0 && isCheckLeftAndRight)
{
deltaX = -rectF.left;

}

if (rectF.right < width && isCheckLeftAndRight)
{
deltaX = width - rectF.right;
}
mScaleMartix.postTranslate(deltaX, deltaY);

}

/**
* 判断是否达到了移动图片的 需求
*
* @param dx
* @param dy
* @return
*/
private boolean isMoveAction(float dx, float dy)
{
return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
}

/**
* 获取图片的缩放值
*
* @return
*/
public float getScale()
{
float[] values = new float[9];
mScaleMartix.getValues(values);
return values[Matrix.MSCALE_X];//理论上x 和 y是一样的 TODO断点
}

/**
* 获取图片放大 或者 缩小以后的宽和高,以及left,right,top,bottom等属性
*
* @return
*/
public RectF getMatrixRectF()
{
RectF rectF = new RectF();
Drawable d = getDrawable();
if (d != null)
{
rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
mScaleMartix.mapRect(rectF);
}

return rectF;
}

/**
* 缩放监听
*/
private class SimpleOnScaleGesture implements ScaleGestureDetector.OnScaleGestureListener
{
/**
* 缩放 进行中
*
* @param detector
* @return
*/
@Override
public boolean onScale(ScaleGestureDetector detector)
{
if (getDrawable() == null) return true;//return true ?//TODO

//缩放值
float scaleFactor = detector.getScaleFactor();
//缩放区间 mInitScale ~ mMaxScale
//获取当前的 Scale * 缩放比例 在进行判断
float scale = getScale();
//当前的缩放值 没有 放大到 最大值 并且 缩放比例 大于 1.0,证明想放大,批准
//当前的缩放值 没有 缩小到 最小值 且   缩小比例 小  1.0 ,批准
if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f))
{
//缩小比例超过了 最小值
if (scale * scaleFactor < mInitScale)
{
//让他等于最小值
//mInitScale=scale*scaleFactor 最小值
//mInitScale 和 scale 都是固定的,而scaleFactor是手势改变的。
scaleFactor = mInitScale / scale;
}
//超过最大值
if (scale * scaleFactor > mMaxScale)
{
scaleFactor = mMaxScale / scale;
}
Log.d("warden", this.getClass().getName() + "scaleFactor:" + scaleFactor);
mScaleMartix.postScale(scaleFactor, scaleFactor, detector.getFocusX() / 2, detector.getFocusY() / 2);
checkBorderAndCenterWhenScale();
setImageMatrix(mScaleMartix);
}
return true;
}

/**
* 缩放开始时
*
* @param detector
* @return
*/
@Override
public boolean onScaleBegin(ScaleGestureDetector detector)
{
return true;
}

/**
* 缩放结束
*
* @param detector
*/
@Override
public void onScaleEnd(ScaleGestureDetector detector)
{

}

}

private void checkBorderAndCenterWhenScale()
{
RectF rect = getMatrixRectF();
float deltaX = 0;
float deltaY = 0;

int width = getWidth();
int height = getHeight();

//万恶的数学
if (rect.width() >= width)
{
if (rect.left > 0)
{
deltaX = -rect.left;
}
if (rect.right < width)
{
deltaX = width - rect.right;
}
}

if (rect.height() >= height)
{
if (rect.top > 0)
{
deltaY = -rect.top;
}

if (rect.bottom < height)
{
deltaY = height - rect.bottom;
}
}

//居中
if (rect.width() < width)
{
deltaX = width / 2 - rect.width() / 2 - rect.left;
//        deltaX=width/2-rect.right+rect.width()/2;
}

if (rect.height() < height)
{

deltaY = height / 2 - rect.bottom + rect.height() / 2;
}
//TODO 下和右边?这种算法 ?

mScaleMartix.postTranslate(deltaX, deltaY);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: