Android 图片随着手势缩放,平移,并且支持多点触控
效果图:
现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~
想要做到图片支持多点触控,自由的进行缩放、平移,需要了解几个知识点:Matrix , GestureDetector , ScaleGestureDetector 以及事件分发机制
1、Matrix
参考:http://geek.csdn.net/news/detail/89873 http://blog.csdn.net/flash129/article/details/8234599 http://blog.csdn.net/cquwentao/article/details/51445269
矩阵,看深入了都是3维矩阵的乘啊什么的,怪麻烦的~~
其实这么了解下就行了:
Matrix
数据结构:3维矩阵;
内部存储:new Float[9] ; 内部就是个一维数组,内部9个元素;可以进行setValues(float[] values)进行初始化
每个元素代表的意思:
{ MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2
};
字面上,应该能看出来哪个代表x方向缩放,哪个代表垂直方向的偏移量吧~~有不认识的3个,没事,请无视。
操作
比如你想要设置matrix的偏移量为200,100
你可以这么写:
Matrix transMatrix = new Matrix(); float[] values = new float[] { 1.0, 0, 200, 0, 1.0, 100, 0, 0, 1.0 }; transMatrix.setValues(values);
如何获取值:
当然了,我们对一个Matrix进行了各种操作,一会postScale,一会postTranslate;那么现在如何获得当前的缩放比例:
前面说setValues可以初始化,那么getValues就能拿到当前矩阵的值,拿到的是个一维数组,9个元素;再通过下标取对应值就可以。
比如我想知道现在x方向缩放比例:
public final float getScale() { scaleMatrix.getValues(matrixValues); return matrixValues[Matrix.MSCALE_X]; }
好了,知道这些就够了~~
2、GestureDetector
参考:http://blog.csdn.net/xyz_lmn/article/details/16826669 http://blog.csdn.net/hpk1994/article/details/51224228
嗯,自己看API,能够捕捉到长按、双击什么的;用法会在例子中
3、ScaleGestureDetector
参考:http://blog.csdn.net/u010410408/article/details/39577399 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0211/2467.html
http://blog.csdn.net/true100/article/details/51141496
嗯,有点像继承来的,其实不是的,独立的一个类~用于检测缩放的手势~~~用法会在例子中
4、完整代码:
public class ZoomImageView extends ImageView implements OnScaleGestureListener, OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener {private static final String TAG = ZoomImageView.class.getSimpleName();public static final float SCALE_MAX = 3.0f;private static final float SCALE_MID = 1.5f;/*** 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0*/private float initScale = 1.0f;private boolean once = true;/*** 用于存放矩阵的9个值*/private final float[] matrixValues = new float[9];/*** 缩放的手势检测*/private ScaleGestureDetector mScaleGestureDetector = null;private final Matrix mScaleMatrix = new Matrix();/*** 用于双击检测*/private GestureDetector mGestureDetector;private boolean isAutoScale;private int mTouchSlop;private float mLastX;private float mLastY;private boolean isCanDrag;private int lastPointerCount;private boolean isCheckTopAndBottom = true;private boolean isCheckLeftAndRight = true;public ZoomImageView(Context context) {this(context, null);}public ZoomImageView(Context context, AttributeSet attrs) {super(context, attrs);super.setScaleType(ScaleType.MATRIX);mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() {@Overridepublic boolean onDoubleTap(MotionEvent e) {if (isAutoScale == true)return true;float x = e.getX();float y = e.getY();Log.e("DoubleTap", getScale() + " , " + initScale);if (getScale() < SCALE_MID) {//postDelayed(); 16 :多久实现一次的定时器操作ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MID, x, y), 16);isAutoScale = true;} /*else if (getScale() >= SCALE_MID //连续双击放大 可放开&& getScale() < SCALE_MAX) {ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MAX, x, y), 16);isAutoScale = true;}*/ else {ZoomImageView.this.postDelayed(new AutoScaleRunnable(initScale, x, y), 16);isAutoScale = true;}return true;}});mScaleGestureDetector = new ScaleGestureDetector(context, this);this.setOnTouchListener(this);}/*** 自动缩放的任务* @author zhy*/private class AutoScaleRunnable implements Runnable {static final float BIGGER = 1.07f;static final float SMALLER = 0.93f;private float mTargetScale;private float tmpScale;/*** 缩放的中心*/private float x;private float y;/*** 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小* @param targetScale*/public AutoScaleRunnable(float targetScale, float x, float y) {this.mTargetScale = targetScale;this.x = x;this.y = y;if (getScale() < mTargetScale) {tmpScale = BIGGER;} else {tmpScale = SMALLER;}}@Overridepublic void run() {// 进行缩放mScaleMatrix.postScale(tmpScale, tmpScale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);final float currentScale = getScale();// 如果值在合法范围内,继续缩放if (((tmpScale > 1f) && (currentScale < mTargetScale)) || ((tmpScale < 1f) && (mTargetScale < currentScale))) {ZoomImageView.this.postDelayed(this, 16);} else {// 设置为目标的缩放比例final float deltaScale = mTargetScale / currentScale;mScaleMatrix.postScale(deltaScale, deltaScale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);isAutoScale = false;}}}/*** 对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值* @param detector* @return*/@SuppressLint("NewApi")@Overridepublic boolean onScale(ScaleGestureDetector detector) {float scale = getScale();float scaleFactor = detector.getScaleFactor();if (getDrawable() == null)return true;/*** 缩放的范围控制*/if ((scale < SCALE_MAX && scaleFactor > 1.0f) || (scale > initScale && scaleFactor < 1.0f)) {/*** 最大值最小值判断*/if (scaleFactor * scale < initScale) {scaleFactor = initScale / scale;}if (scaleFactor * scale > SCALE_MAX) {scaleFactor = SCALE_MAX / scale;}/*** 设置缩放比例*/mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);}return true;}/*** 在缩放时,进行图片显示范围的控制*/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 * 0.5f - rect.right + 0.5f * rect.width();}if (rect.height() < height) {deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();}Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);mScaleMatrix.postTranslate(deltaX, deltaY);}/*** 根据当前图片的Matrix获得图片的范围* @return*/private RectF getMatrixRectF() {Matrix matrix = mScaleMatrix;RectF rect = new RectF();Drawable d = getDrawable();if (null != d) {rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());matrix.mapRect(rect);}return rect;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {return true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {}/*** 我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理* public boolean onTouch(View v, MotionEvent event){* return mScaleGestureDetector.onTouchEvent(event);* }*/@Overridepublic boolean onTouch(View v, MotionEvent event) {if (mGestureDetector.onTouchEvent(event))return true;mScaleGestureDetector.onTouchEvent(event);float x = 0, y = 0;// 拿到触摸点的个数final int pointerCount = event.getPointerCount();// 得到多个触摸点的x与y均值for (int i = 0; i < pointerCount; i++) {x += event.getX(i);y += event.getY(i);}x = x / pointerCount;y = y / pointerCount;/*** 每当触摸点发生变化时,重置mLasX , mLastY*/if (pointerCount != lastPointerCount) {isCanDrag = false;mLastX = x;mLastY = y;}lastPointerCount = pointerCount;RectF rectF = getMatrixRectF();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (rectF.width() > getWidth() || rectF.height() > getHeight()) {getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_MOVE:if (rectF.width() > getWidth() || rectF.height() > getHeight()) {getParent().requestDisallowInterceptTouchEvent(true);}Log.e(TAG, "ACTION_MOVE");float dx = x - mLastX;float dy = y - mLastY;if (!isCanDrag) {isCanDrag = isCanDrag(dx, dy);}if (isCanDrag) {if (getDrawable() != null) {// if (getMatrixRectF().left == 0 && dx > 0)// {// getParent().requestDisallowInterceptTouchEvent(false);// }//// if (getMatrixRectF().right == getWidth() && dx < 0)// {// getParent().requestDisallowInterceptTouchEvent(false);// }isCheckLeftAndRight = isCheckTopAndBottom = true;// 如果宽度小于屏幕宽度,则禁止左右移动if (rectF.width() < getWidth()) {dx = 0;isCheckLeftAndRight = false;}// 如果高度小雨屏幕高度,则禁止上下移动if (rectF.height() < getHeight()) {dy = 0;isCheckTopAndBottom = false;}//设置偏移量mScaleMatrix.postTranslate(dx, dy);//再次校验checkMatrixBounds();setImageMatrix(mScaleMatrix);}}mLastX = x;mLastY = y;break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:Log.e(TAG, "ACTION_UP");lastPointerCount = 0;break;}return true;}/*** 获得当前的缩放比例* @return*/public final float getScale() {mScaleMatrix.getValues(matrixValues);return matrixValues[Matrix.MSCALE_X];}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();getViewTreeObserver().addOnGlobalLayoutListener(this);}@SuppressWarnings("deprecation")@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();getViewTreeObserver().removeGlobalOnLayoutListener(this);}/*** 根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。* 如果图片很小,那就正常显示,不放大了~*/@Overridepublic void onGlobalLayout() {if (once) {Drawable d = getDrawable();if (d == null)return;Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());int width = getWidth();int height = getHeight();// 拿到图片的宽和高int dw = d.getIntrinsicWidth();int dh = d.getIntrinsicHeight();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);}initScale = scale;Log.e(TAG, "initScale = " + initScale);mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);mScaleMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);// 图片移动至屏幕中心setImageMatrix(mScaleMatrix);once = false;}}/*** 移动时,进行边界判断,主要判断宽或高大于屏幕的*/private void checkMatrixBounds() {RectF rect = getMatrixRectF();float deltaX = 0, deltaY = 0;final float viewWidth = getWidth();final float viewHeight = getHeight();// 判断移动或缩放后,图片显示是否超出屏幕边界if (rect.top > 0 && isCheckTopAndBottom) {deltaY = -rect.top;}if (rect.bottom < viewHeight && isCheckTopAndBottom) {deltaY = viewHeight - rect.bottom;}if (rect.left > 0 && isCheckLeftAndRight) {deltaX = -rect.left;}if (rect.right < viewWidth && isCheckLeftAndRight) {deltaX = viewWidth - rect.right;}mScaleMatrix.postTranslate(deltaX, deltaY);}/*** 是否是推动行为** @param dx* @param dy* @return*/private boolean isCanDrag(float dx, float dy) {return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;}}
1、我们在onGlobalLayout的回调中,根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。如果图片很小,那就正常显示,不放大了~
2、我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理
@Override public boolean onTouch(View v, MotionEvent event) { return mScaleGestureDetector.onTouchEvent(event); }
3、在onScale的回调中对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值
现在的效果:
1、小于屏幕的宽和高
2、大于屏幕的宽和高
可是,可是,存在问题:
1、缩放的中心点,我们设置是固定的,屏幕中间
2、放大后,无法移动~
下面,我们先解决缩放的中心点问题,不能一直按屏幕中心么,像我这样的,我比较关注妹子的眼睛,我要放大那一块~~~
2、设置缩放中心
1、单纯的设置缩放中心
仅仅是设置中心很简单,直接修改下中心点 :
/** * 设置缩放比例 */ mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusX()); setImageMatrix(mScaleMatrix);
但是,随意的中心点放大、缩小,会导致图片的位置的变化,最终导致,图片宽高大于屏幕时,图片与屏幕间出现白边;图片小于屏幕,但是不居中。
2、控制缩放时图片显示的范围
所以我们在缩放的时候需要手动控制下范围:
/** * 在缩放时,进行图片显示范围的控制 */ 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 * 0.5f - rect.right + 0.5f * rect.width(); } if (rect.height() < height) { deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height(); } Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY); mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 根据当前图片的Matrix获得图片的范围 * * @return */ private RectF getMatrixRectF() { Matrix matrix = mScaleMatrix; RectF rect = new RectF(); Drawable d = getDrawable(); if (null != d) { rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rect); } return rect; }
在onScale里面记得调用:
/** * 设置缩放比例 */ mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix);
这样就好了,可以自由的放大任何地方,并且不会出现边界出现白边,也能很好的让图片显示在屏幕中间(当图片宽或高小于屏幕);
下面介绍:多点触控时移动,双击变大,双击变小
1.自由的进行移动
我们在onTouchEvent里面,加上移动的代码,当然了,必须长或宽大于屏幕才可以移动~~~
@Override public boolean onTouch(View v, MotionEvent event) { mScaleGestureDetector.onTouchEvent(event); float x = 0, y = 0; // 拿到触摸点的个数 final int pointerCount = event.getPointerCount(); // 得到多个触摸点的x与y均值 for (int i = 0; i < pointerCount; i++) { x += event.getX(i); y += event.getY(i); } x = x / pointerCount; y = y / pointerCount; /** * 每当触摸点发生变化时,重置mLasX , mLastY */ if (pointerCount != lastPointerCount) { isCanDrag = false; mLastX = x; mLastY = y; } lastPointerCount = pointerCount; switch (event.getAction()) { case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE"); float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag) { isCanDrag = isCanDrag(dx, dy); } if (isCanDrag) { RectF rectF = getMatrixRectF(); if (getDrawable() != null) { isCheckLeftAndRight = isCheckTopAndBottom = true; // 如果宽度小于屏幕宽度,则禁止左右移动 if (rectF.width() < getWidth()) { dx = 0; isCheckLeftAndRight = false; } // 如果高度小雨屏幕高度,则禁止上下移动 if (rectF.height() < getHeight()) { dy = 0; isCheckTopAndBottom = false; } mScaleMatrix.postTranslate(dx, dy); checkMatrixBounds(); setImageMatrix(mScaleMatrix); } } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: Log.e(TAG, "ACTION_UP"); lastPointerCount = 0; break; } return true; }
首先我们拿到触摸点的数量,然后求出多个触摸点的平均值,设置给我们的mLastX , mLastY , 然后在移动的时候,得到dx ,dy 进行范围检查以后,调用mScaleMatrix.postTranslate进行设置偏移量,当然了,设置完成以后,还需要再次校验一下,不能把图片移动的与屏幕边界出现白边,校验完成后,调用setImageMatrix.
这里:需要注意一下,我们没有复写ACTION_DOWM,是因为,ACTION_DOWN在多点触控的情况下,只要有一个手指按下状态,其他手指按下不会再次触发ACTION_DOWN,但是多个手指以后,触摸点的平均值会发生很大变化,所以我们没有用到ACTION_DOWN。每当触摸点的数量变化,我们就会跟新当前的mLastX,mLastY.
下面是上面用到的两个私有方法,一个用于检查边界,一个用于判断是否是拖动的操作:
/** * 移动时,进行边界判断,主要判断宽或高大于屏幕的 */ private void checkMatrixBounds() { RectF rect = getMatrixRectF(); float deltaX = 0, deltaY = 0; final float viewWidth = getWidth(); final float viewHeight = getHeight(); // 判断移动或缩放后,图片显示是否超出屏幕边界 if (rect.top > 0 && isCheckTopAndBottom) { deltaY = -rect.top; } if (rect.bottom < viewHeight && isCheckTopAndBottom) { deltaY = viewHeight - rect.bottom; } if (rect.left > 0 && isCheckLeftAndRight) { deltaX = -rect.left; } if (rect.right < viewWidth && isCheckLeftAndRight) { deltaX = viewWidth - rect.right; } mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 是否是推动行为 * * @param dx * @param dy * @return */ private boolean isCanDrag(float dx, float dy) { return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; }
这样,我们就可以快乐的放大、缩小加移动了~~~
2.双击放大缩小
谈到双击事件,我们的GestureDetector终于要登场了,这哥们可以捕获双击事件~~
1.GestureDetector的使用:
因为GestureDetector设置监听器的话,方法一大串,而我们只需要onDoubleTap这个回调,所以我们准备使用它的一个内部类SimpleOnGestureListener,对接口的其他方法实现了空实现。
不过还有几个问题需要讨论下,才能开始我们的代码:
1、我们双击尺寸如何变化?
我是这样的,根据当前的缩放值,如果是小于2的,我们双击直接到变为原图的2倍;如果是2,4之间的,我们双击直接为原图的4倍;其他状态也就是4倍,双击后还原到最初的尺寸。
如果你觉得这样不合适,可以根据自己的爱好调整。
2、我们双击变化,需要一个动画~~比如我们上例的演示图,图片很大,全屏显示的时候initScale=0.5左后,如果双击后变为2,也就是瞬间大了四倍,没有一个过渡的效果的话,给用户的感觉会特别差。所以,我们准备使用postDelay执行一个Runnable,Runnable中再次根据的当然的缩放值继续执行。
首先我们在构造方法中,完成对GestureDetector的初始化,以及设置onDoubleTap监听
public ZoomImageView(Context context, AttributeSet attrs) { super(context, attrs); mScaleGestureDetector = new ScaleGestureDetector(context, this); mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { if (isAutoScale == true) return true; float x = e.getX(); float y = e.getY(); Log.e("DoubleTap", getScale() + " , " + initScale); if (getScale() < SCALE_MID) { ZoomImageView.this.postDelayed( new AutoScaleRunnable(SCALE_MID, x, y), 16); isAutoScale = true; } else if (getScale() >= SCALE_MID && getScale() < SCALE_MAX) { ZoomImageView.this.postDelayed( new AutoScaleRunnable(SCALE_MAX, x, y), 16); isAutoScale = true; } else { ZoomImageView.this.postDelayed( new AutoScaleRunnable(initScale, x, y), 16); isAutoScale = true; } return true; } }); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); super.setScaleType(ScaleType.MATRIX); this.setOnTouchListener(this); }
1、当双击的时候,首先判断是否正在自动缩放,如果在,直接retrun ;
2、然后就进入了我们的if,如果当然是scale小于2,则通过view.发送一个Runnable进行执行;其他类似;
下面看我们的Runnable的代码:
/** * 自动缩放的任务 */ private class AutoScaleRunnable implements Runnable { static final float BIGGER = 1.07f; static final float SMALLER = 0.93f; private float mTargetScale; private float tmpScale; /** * 缩放的中心 */ private float x; private float y; /** * 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小 * * @param targetScale */ public AutoScaleRunnable(float targetScale, float x, float y) { this.mTargetScale = targetScale; this.x = x; this.y = y; if (getScale() < mTargetScale) { tmpScale = BIGGER; } else { tmpScale = SMALLER; } } @Override public void run() { // 进行缩放 mScaleMatrix.postScale(tmpScale, tmpScale, x, y); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); final float currentScale = getScale(); //如果值在合法范围内,继续缩放 if (((tmpScale > 1f) && (currentScale < mTargetScale)) || ((tmpScale < 1f) && (mTargetScale < currentScale))) { ZoomImageView.this.postDelayed(this, 16); } else//设置为目标的缩放比例 { final float deltaScale = mTargetScale / currentScale; mScaleMatrix.postScale(deltaScale, deltaScale, x, y); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); isAutoScale = false; } } }
代码写完了,我们依然需要把我们的event传给它,依然是在onTouch方法:
@Override public boolean onTouch(View v, MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) return true;
连续双击放大,感觉不爽,代码已经上传,我就不重传了,如果你也觉得不爽,可以自行注释。
// else if (getScale() >= SCALE_MID
// && getScale() < SCALE_MAX)
// {
// ZoomImageView.this.postDelayed(
// new AutoScaleRunnable(SCALE_MAX, x, y), 16);
// isAutoScale = true;
// }
源码:http://download.csdn.net/download/lijinweii/9957495
Android 图片随着手势缩放,平移,并且支持多点触控相关推荐
- Android 自定义密码键盘组件,支持多点触控
前言 现在对于前端APP的安全要求越来越高,所以很多APP都开发了自己的数字键盘,这么做对于安全性的提升个人总结有如下几点: 防止第三方键盘出现BUG,泄露用户支付密码. 自定义键盘可以根据自己的算法 ...
- Android笔记:触摸事件的分析与总结----多点触控
其他相关博文: Android笔记:触摸事件的分析与总结----MotionEvent对象 Android笔记:触摸事件的分析与总结----TouchEvent处理机制 An ...
- linux多点触摸屏驱动程序,Linux系统实现支持多点触控操作[视频]
虽然大多数人目前还不会购买拥有触控屏的设备,不过在Windows 7的大力推动下,触控操作已经开始流行了起来.然而Linux目前却无法支持这一功能,这让很多用户十分郁闷,不过来自ENAC Intera ...
- android 多个手指同时点击,处理多点触控手势 | Android 开发者 | Android Developers...
多点触控手势是指多个指针(手指)同时轻触屏幕.这节课介绍了如何检测涉及多个指针的手势. 请参阅以下相关资源: 跟踪多个指针 当多个指针同时轻触屏幕时,系统会生成以下触摸事件: 您可以通过每个指针的索引 ...
- android 图片 居中 手势缩放 设置缩放比例,ImageView通过matrix实现手势缩放
关于ImageView的手势缩放,有很多种方法,绝大多数开源自定义缩放都是修改了ondraw函数来实现的.但是ImageView本身有scaleType属性,通过设置android:scaleType ...
- Android App接管手势处理TouchEvnet中单点触摸和多点触控的讲解及实战(附源码 超简单实用)
运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.单点触摸 dispatchTouchEvent onInterceptTouchEvent onTouchEvent三个方法的输入参数都是手势 ...
- Android多点触控详解
本文转载自GcsSloop的 安卓自定义View进阶-多点触控详解 的文章 Android 多点触控详解,在前面的几篇文章中我们大致了解了 Android 中的事件处理流程和一些简单的处理方案,本次带 ...
- Android中的多点触控(装载)
Android多点触控技术 1 简介 Android多点触控在本质上需要LCD驱动和程序本身设计上支持,目前市面上HTC.Motorola和Samsung等知名厂商只要使用电容屏触控原理的手机均可以支 ...
- Android多点触控技术
1 简介 Android多点触控在本质上需要LCD驱动和程序本身设计上支持,目前市面上HTC.Motorola和Samsung等知名厂商只要使用电容屏触控原理的手机均可以支持多点触控Multitouc ...
最新文章
- AI开发者们,2017年你们过得好吗?
- 从根因入手,更有效率,效果也更好
- mysql 8.0 自定义函数_PHP+Mysql防止SQL注入的方法(life)
- 40 | 案例篇:网络请求延迟变大了,我该怎么办?
- 常见的权限访问控制模型
- 线程组之间的JMeter传递变量
- 华为云牵手同舟共济伙伴 持续发力构建云核心生态圈
- 每天进步一点点《ML - 逻辑回归》
- ADO.Net之SqlConnection、 Sqlcommand的应用(读书笔记1)
- 水晶報表:金額轉換大寫12/8
- 兰州大学2016年初试成绩基本要求
- bzoj 3262: 陌上花开(cdq分治)
- 【Magick++透明图层合并】
- php小小通讯录,小小通讯录
- 360大战QQ演义之一:一场腾讯可能连底裤都输掉的战争!
- 本悟法师:信仰,让孤独走开
- NCRE考试感想 三级信息安全(下)
- type是python保留字_Python保留字总结
- 北航软件测评中心 招聘FPGA测试工程师
- 2017 ACM Arabella Collegiate Programming Contest A. Sherlock Bones GYM101350A