touchImageVIEw继承自ImageVIEw具有ImageVIEw的所有功能;除此之外,还有缩放、拖拽、双击放大等功能,支持vIEwpager和scaletype,并伴有动画效果。
sharedConstructingprivate voID sharedConstructing(Context context) {super.setClickable(true);this.context = context;mScaleDetector = new ScaleGestureDetector(context,new ScaleListener());mGestureDetector = new GestureDetector(context,new GestureListener());matrix = new Matrix();prevMatrix = new Matrix();m = new float[9];normalizedScale = 1;if (mScaleType == null) {mScaleType = ScaleType.FIT_CENTER;}minScale = 1;maxScale = 3;superMinScale = SUPER_MIN_MulTIPLIER * minScale;superMaxScale = SUPER_MAX_MulTIPLIER * maxScale;setimageMatrix(matrix);setScaleType(ScaleType.MATRIX);setState(State.NONE);onDrawReady = false;super.setontouchListener(new PrivateOntouchListener());}
初始化,设置ScaleGestureDetector的监听器为ScaleListener,这是用来处理缩放手势的,设置GestureDetector的监听器为GestureListener,这是用来处理双击和fling手势的,前两个手势都会引起图片的缩放,而fling会引起图片的移动。
mScaleDetector = new ScaleGestureDetector(context,new GestureListener());
最后设置自定义view的touch事件监听器为PrivateOntouchListener,这是touch事件的入口。
super.setontouchListener(new PrivateOntouchListener());PrivateOntouchListenerprivate class PrivateOntouchListener implements OntouchListener {//// Remember last point position for dragging//private PointF last = new PointF();@OverrIDepublic boolean ontouch(VIEw v,MotionEvent event) {mScaleDetector.ontouchEvent(event);mGestureDetector.ontouchEvent(event);PointF curr = new PointF(event.getX(),event.getY());if (state == State.NONE || state == State.DRAG || state == State.FliNG) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:last.set(curr);if (fling != null)fling.cancelFling();setState(State.DRAG);break;case MotionEvent.ACTION_MOVE:if (state == State.DRAG) {float deltaX = curr.x - last.x;float deltaY = curr.y - last.y;float fixTransX = getFixDragTrans(deltaX,vIEwWIDth,getimageWIDth());float fixTransY = getFixDragTrans(deltaY,vIEwHeight,getimageHeight());matrix.postTranslate(fixTransX,fixTransY);fixTrans();last.set(curr.x,curr.y);}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_POINTER_UP:setState(State.NONE);break;}}setimageMatrix(matrix);//// user-defined OntouchListener//if(usertouchListener != null) {usertouchListener.ontouch(v,event);}//// OntouchImageVIEwListener is set: touchImageVIEw dragged by user.//if (touchImageVIEwListener != null) {touchImageVIEwListener.onMove();}//// indicate event was handled//return true;}}
触摸时会走到PrivateOntouchListener的ontouch,它又会将捕捉到的MotionEvent交给mScaleDetector和mGestureDetector来分析是否有合适的callback函数来处理用户的手势。
mScaleDetector.ontouchEvent(event);mGestureDetector.ontouchEvent(event);
同时在当前状态是DRAG时将X、Y移动的距离赋值给变换矩阵
matrix.postTranslate(fixTransX,fixTransY);
给ImageVIEw设置矩阵,完成X、Y的移动,即实现单指拖拽动作
setimageMatrix(matrix);
ScaleListener
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {@OverrIDepublic boolean onScaleBegin(ScaleGestureDetector detector) {setState(State.ZOOM);return true;}@OverrIDepublic boolean onScale(ScaleGestureDetector detector) {scaleImage(detector.getScaleFactor(),detector.getFocusX(),detector.getFocusY(),true);//// OntouchImageVIEwListener is set: touchImageVIEw pinch zoomed by user.//if (touchImageVIEwListener != null) {touchImageVIEwListener.onMove();}return true;}@OverrIDepublic voID onScaleEnd(ScaleGestureDetector detector) {super.onScaleEnd(detector);setState(State.NONE);boolean animatetoZoomBoundary = false;float targetZoom = normalizedScale;if (normalizedScale > maxScale) {targetZoom = maxScale;animatetoZoomBoundary = true;} else if (normalizedScale < minScale) {targetZoom = minScale;animatetoZoomBoundary = true;}if (animatetoZoomBoundary) {DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom,vIEwWIDth / 2,vIEwHeight / 2,true);compatPostOnAnimation(doubleTap);}}}
两指缩放动作会走到ScaleListener的回调,在它的onScale回调中会处理图片的缩放
scaleImage(detector.getScaleFactor(),true);
scaleImage
private voID scaleImage(double deltaScale,float focusX,float focusY,boolean stretchImagetoSuper) {float lowerScale,upperScale;if (stretchImagetoSuper) {lowerScale = superMinScale;upperScale = superMaxScale;} else {lowerScale = minScale;upperScale = maxScale;}float origScale = normalizedScale;normalizedScale *= deltaScale;if (normalizedScale > upperScale) {normalizedScale = upperScale;deltaScale = upperScale / origScale;} else if (normalizedScale < lowerScale) {normalizedScale = lowerScale;deltaScale = lowerScale / origScale;}matrix.postscale((float) deltaScale,(float) deltaScale,focusX,focusY);fixScaleTrans();}
这里会将多次缩放的缩放比累积,并设置有最大和最小缩放比,不能超出范围
normalizedScale *= deltaScale;
最后把X、Y的缩放比和焦点传给变换矩阵,通过矩阵关联到ImageVIEw,完成缩放动作
matrix.postscale((float) deltaScale,focusY);
在onScaleEnd回调中,我们会判断是否当前缩放比超出最大缩放比或者小于最小缩放比,如果是,会有一个动画回到最大或最小缩放比状态
DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom,true);compatPostOnAnimation(doubleTap);
这里的动画DoubleTapZoom就是双击动画,关于DoubleTapZoom我们下面会讲到。至此两指缩放动作就完成了,下面继续看双击缩放动作。
GestureListener
private class GestureListener extends GestureDetector.SimpleOnGestureListener {@OverrIDepublic boolean onSingleTapConfirmed(MotionEvent e){if(doubleTapListener != null) {return doubleTapListener.onSingleTapConfirmed(e);}return performClick();}@OverrIDepublic voID onLongPress(MotionEvent e){performlongClick();}@OverrIDepublic boolean onFling(MotionEvent e1,MotionEvent e2,float veLocityX,float veLocityY){if (fling != null) {//// If a prevIoUs fling is still active,it should be cancelled so that two flings// are not run simultaenously.//fling.cancelFling();}fling = new Fling((int) veLocityX,(int) veLocityY);compatPostOnAnimation(fling);return super.onFling(e1,e2,veLocityX,veLocityY);}@OverrIDepublic boolean onDoubleTap(MotionEvent e) {boolean consumed = false;if(doubleTapListener != null) {consumed = doubleTapListener.onDoubleTap(e);}if (state == State.NONE) {float targetZoom = (normalizedScale == minScale) ? maxScale : minScale;DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom,e.getX(),e.getY(),false);compatPostOnAnimation(doubleTap);consumed = true;}return consumed;}@OverrIDepublic boolean onDoubleTapEvent(MotionEvent e) {if(doubleTapListener != null) {return doubleTapListener.onDoubleTapEvent(e);}return false;}}
在onDoubleTap回调中,设置双击缩放比,如果当前无缩放,则设置缩放比为最大值,如果已经是最大值,则设置为无缩放
float targetZoom = (normalizedScale == minScale) ? maxScale : minScale;
然后将当前点击坐标做为缩放中心,连同缩放比一起交给DoubleTapZoom,完成缩放动画
DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom,false);compatPostOnAnimation(doubleTap);
DoubleTapZoom
private class DoubleTapZoom implements Runnable {private long startTime;private static final float ZOOM_TIME = 500;private float startZoom,targetZoom;private float bitmapX,bitmapY;private boolean stretchImagetoSuper;private AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();private PointF starttouch;private PointF endtouch;DoubleTapZoom(float targetZoom,boolean stretchImagetoSuper) {setState(State.ANIMATE_ZOOM);startTime = System.currentTimeMillis();this.startZoom = normalizedScale;this.targetZoom = targetZoom;this.stretchImagetoSuper = stretchImagetoSuper;PointF bitmapPoint = transformCoordtouchToBitmap(focusX,focusY,false);this.bitmapX = bitmapPoint.x;this.bitmapY = bitmapPoint.y;//// Used for translating image during scaling//starttouch = transformCoordBitmapTotouch(bitmapX,bitmapY);endtouch = new PointF(vIEwWIDth / 2,vIEwHeight / 2);}@OverrIDepublic voID run() {float t = interpolate();double deltaScale = calculateDeltaScale(t);scaleImage(deltaScale,bitmapX,bitmapY,stretchImagetoSuper);translateImagetoCentertouchposition(t);fixScaleTrans();setimageMatrix(matrix);//// OntouchImageVIEwListener is set: double tap runnable updates Listener// with every frame.//if (touchImageVIEwListener != null) {touchImageVIEwListener.onMove();}if (t < 1f) {//// We haven't finished zooming//compatPostOnAnimation(this);} else {//// Finished zooming//setState(State.NONE);}}/*** Interpolate between where the image should start and end in order to translate* the image so that the point that is touched is what ends up centered at the end* of the zoom.* @param t*/private voID translateImagetoCentertouchposition(float t) {float targetX = starttouch.x + t * (endtouch.x - starttouch.x);float targetY = starttouch.y + t * (endtouch.y - starttouch.y);PointF curr = transformCoordBitmapTotouch(bitmapX,bitmapY);matrix.postTranslate(targetX - curr.x,targetY - curr.y);}/*** Use interpolator to get t* @return*/private float interpolate() {long currTime = System.currentTimeMillis();float elapsed = (currTime - startTime) / ZOOM_TIME;elapsed = Math.min(1f,elapsed);return interpolator.getInterpolation(elapsed);}/*** Interpolate the current targeted zoom and get the delta* from the current zoom.* @param t* @return*/private double calculateDeltaScale(float t) {double zoom = startZoom + t * (targetZoom - startZoom);return zoom / normalizedScale;}}
DoubleTapZoom其实是一个线程,实现了Runnable,我们直接看它的Run方法吧,这里定义了一个时间t
float t = interpolate();
其实t在500ms内通过一个加速差值器从0到1加速增长
private float interpolate() {long currTime = System.currentTimeMillis();float elapsed = (currTime - startTime) / ZOOM_TIME;elapsed = Math.min(1f,elapsed);return interpolator.getInterpolation(elapsed);}
通过t计算出当前缩放比
double deltaScale = calculateDeltaScale(t);
实现缩放
scaleImage(deltaScale,stretchImagetoSuper);
然后根据当前t的值判断动画是否结束,如果t小于1,表示动画还未结束,重新执行本线程,否则设置状态完成。这里就是通过在这500ms内多次执行线程,多次重绘ImageVIEw实现动画效果的。
if (t < 1f) {compatPostOnAnimation(this);} else {setState(State.NONE);}
同时在GestureListener的onFling回调中,设置Fling的X、Y速度,然后执行Fling的位移动画
fling = new Fling((int) veLocityX,(int) veLocityY);compatPostOnAnimation(fling);
Fling
private class Fling implements Runnable {CompatScroller scroller;int currX,currY;Fling(int veLocityX,int veLocityY) {setState(State.FliNG);scroller = new CompatScroller(context);matrix.getValues(m);int startX = (int) m[Matrix.MTRANS_X];int startY = (int) m[Matrix.MTRANS_Y];int minX,maxX,minY,maxY;if (getimageWIDth() > vIEwWIDth) {minX = vIEwWIDth - (int) getimageWIDth();maxX = 0;} else {minX = maxX = startX;}if (getimageHeight() > vIEwHeight) {minY = vIEwHeight - (int) getimageHeight();maxY = 0;} else {minY = maxY = startY;}scroller.fling(startX,startY,(int) veLocityX,(int) veLocityY,minX,maxY);currX = startX;currY = startY;}public voID cancelFling() {if (scroller != null) {setState(State.NONE);scroller.forceFinished(true);}}@OverrIDepublic voID run() {//// OntouchImageVIEwListener is set: touchImageVIEw Listener has been flung by user.// Listener runnable updated with each frame of fling animation.//if (touchImageVIEwListener != null) {touchImageVIEwListener.onMove();}if (scroller.isFinished()) {scroller = null;return;}if (scroller.computeScrollOffset()) {int newX = scroller.getCurrX();int newY = scroller.getCurrY();int transX = newX - currX;int transY = newY - currY;currX = newX;currY = newY;matrix.postTranslate(transX,transY);fixTrans();setimageMatrix(matrix);compatPostOnAnimation(this);}}}
Fling其实也是一个线程,实现了Runnable,根据Fling手势的X、Y速度我们会执行Scroller的fling函数,并且将当前位置设置为起始位置
scroller.fling(startX,maxY);currX = startX;currY = startY;
再来看看Run函数,根据scroller当前滚动位置计算出新的位置信息,与旧位置相减得出在X、Y轴平移距离,实现平移
if (scroller.computeScrollOffset()) {int newX = scroller.getCurrX();int newY = scroller.getCurrY();int transX = newX - currX;int transY = newY - currY;currX = newX;currY = newY;matrix.postTranslate(transX,transY);fixTrans();setimageMatrix(matrix);compatPostOnAnimation(this);}
最后延时一段时间再次调用线程完成新的平移绘图,如此往复,直到scroller停止滚动,多次重绘ImageVIEw实现了fling动画效果。
private voID compatPostOnAnimation(Runnable runnable) {if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {postOnAnimation(runnable);} else {postDelayed(runnable,1000/60);}}
下面看一看显示效果吧:
单个图片
图片加载到VIEwPager中
镜像图片
点击可改变图片
点击可改变ScaleType
以上所述是小编给大家介绍的AndroID使用ImageVIEw实现支持手势缩放效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!
总结以上是内存溢出为你收集整理的Android使用ImageView实现支持手势缩放效果全部内容,希望文章能够帮你解决Android使用ImageView实现支持手势缩放效果所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)