多点触控图片 自定义View
2016-03-26 02:05
288 查看
多点触控图片 查看自定义View
详情看hongyang大牛的视频
详情看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); } }
相关文章推荐
- c语言:单向循环链表的实现
- Linux系统安装ftp服务器,jdk以及tomcat
- FragmentTabHost切换Fragment时避免UI重新加载,Fragment保存状态
- 【python小练】0005
- org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
- 《STL源码剖析》之容器
- 如何写出杀手级简历(针对程序员)
- Java学习笔记之输入输出
- lintcode-medium-Longest Common Prefix
- 点击elevator楼梯跳转效果
- 如何克服拖延症
- 国外程序员是如何准备面试的?
- 谢欣伦 - 化繁为简系列原创软件 - 工具软件 - 网络摄像机WebCamera
- SDUT 3513 皮卡丘的梦想 (二进制+线段树) -- 解题报告
- 欧拉函数
- MyDynamicFEM项目之一git
- C#扫盲之:String字符串的常用方法和冷知识
- 强迫症的自我恢复
- rvm 安装 ruby 之后,必须执行 /bin/bash --login 才能执行 ruby 命令
- java 线程创建的方法