Android实现图片多点触控自由伸缩

来源:互联网 发布:随机迷宫生成算法 编辑:程序博客网 时间:2024/05/22 04:40

简介

作为Android开发者,我们经常需要自定义控件,比如下面我们说的实现图片的多点触控和伸缩释放,这也是由于用户已经有这样的常识了,那就是看见有图片的地方就可以点击查看大图,并且可以通过手指对图片进行伸缩和移动,如果应用没有实现这一点,那么对用户来说将会是很糟糕的体验,用户很“愤怒”。所以作为Android开发者,我们的任务就是让用户“爽”。哈哈哈。。。。下面我们将通过自定义ImageView实现以上功能。

涉及技术

一、Matrix(矩阵),Android是通过Matrix去控制图片的伸缩和平移的。

二、ScaleGestureDetector(伸缩手势探测器),实现对用户操作图片伸缩的监听。

实现原理

一、创建ZoomImageView类,通过重写ImageViewOnAttachedToWindow()注册全局布局监听器getViewTreeObserver().addOnGlobalLayoutListener(this),实现对图片的初始化控制

二、在全局布局监听器里面通过Matrix控制初始化图片居中显示和缩放。

三、注册onTouch()事件,通过代码mScaleGestureDetector.onTouchEvent(motionEvent) ;实现ScaleGestureDetector监听手势。

四、在ScaleGestureDetector的回调方法onScale(ScaleGestureDetector detector)里面实现对根据用户的操作,实现对图片的伸缩和移动。

 

代码

package com.liujun.liujunzoomimagedemo.view;import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.ScaleGestureDetector.OnScaleGestureListener;import android.view.View;import android.view.View.OnTouchListener;import android.widget.ImageView;import android.view.ViewTreeObserver;public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener {private ScaleGestureDetector mScaleGestureDetector = null;private Matrix matrix;private float[] matrixValues = new float[9];// 矩阵的九个值private boolean isOnceLayout = true;// 设置缩放比private float initScale = 1.0f;private float midScale = 2.0f;private float maxScale = 4.0f;public ZoomImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);super.setScaleType(ScaleType.MATRIX);// 设置图片通过矩阵控制// 实例化伸缩手势探测器mScaleGestureDetector = new ScaleGestureDetector(context, this);matrix = new Matrix();this.setOnTouchListener(this);}public ZoomImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ZoomImageView(Context context) {this(context, null);}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();// 注册全局布局监听器getViewTreeObserver().addOnGlobalLayoutListener(this);}@SuppressWarnings("deprecation")@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();// 取消全局布局监听器getViewTreeObserver().removeGlobalOnLayoutListener(this);}/** * 获取布局的参数(这个方法会调用两次) */@Overridepublic void onGlobalLayout() {if (isOnceLayout) {// 将图片居中显示,并且伸缩图片Drawable drawable = this.getDrawable();if (drawable == null) {return;}// 获取父控制的宽高int parentWidgetWidth = this.getWidth();int parentWidgetHeight = this.getHeight();// 获取图片的宽高int drawableHeight = drawable.getIntrinsicHeight();int drawableWidth = drawable.getIntrinsicWidth();// 定义缩放比float scale = 1.0f;// 当图片宽度大于父控件的宽度,当高度小于父控件高度时(缩小)if (drawableWidth > parentWidgetWidth && drawableHeight <= parentWidgetHeight) {scale = parentWidgetWidth * 1.0f / drawableWidth;}// 当图片高度高于父控件,但宽度小于父控件时(缩小)if (drawableHeight > parentWidgetHeight && drawableWidth <= parentWidgetWidth) {scale = parentWidgetHeight * 1.0f / drawableHeight;}// 当图片宽度和高度都大于父控件时(缩小)if (drawableHeight > parentWidgetHeight && drawableWidth > parentWidgetWidth) {scale = Math.min(parentWidgetHeight * 1.0f / drawableHeight, parentWidgetWidth * 1.0f / drawableWidth);}// 当图片宽度和高度都小于父控件(扩大)if (drawableHeight < parentWidgetHeight && drawableWidth < parentWidgetWidth) {scale = Math.min(parentWidgetHeight * 1.0f / drawableHeight, parentWidgetWidth * 1.0f / drawableWidth);}// 设置初始化的缩放比initScale = scale;// 将图片缩放并且将图片移动到父控件中间float dx = (parentWidgetWidth - drawableWidth) / 2;float dy = (parentWidgetHeight - drawableHeight) / 2;matrix.postTranslate(dx, dy);// 将图片缩放matrix.postScale(scale, scale, parentWidgetWidth / 2, parentWidgetHeight / 2);// 将矩阵运用到图片中this.setImageMatrix(matrix);isOnceLayout = false;}}/** * 获取图片当前的缩放比 *  * @return */private float getCurrentImageScale() {matrix.getValues(matrixValues);return matrixValues[Matrix.MSCALE_X];}@Overridepublic boolean onScale(ScaleGestureDetector detector) {// 获取图片当前的缩放比float currentScalse = getCurrentImageScale();// 拿到图片将要的缩放比例float scaleFactor = detector.getScaleFactor();if (this.getDrawable() == null) {return true;}// 用户将要放大图片或者用户将要缩小图片if ((scaleFactor > 1.0f && currentScalse < maxScale) || (scaleFactor < 1.0f && currentScalse > initScale)) {// 缩小时if (scaleFactor * currentScalse < initScale) {scaleFactor = initScale / currentScalse;}// 放大时if (scaleFactor * currentScalse > maxScale) {scaleFactor = maxScale / currentScalse;}matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());// 检查边界和中心点checkBorderAndCenterWhenScale();setImageMatrix(matrix);}return true;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {return true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {}/** * 当在缩放的时候,对图片的边界和中心进行控制 */private void checkBorderAndCenterWhenScale() {// 获取当前缩放过程中的图片的矩形RectF rectF = getMatrixRectF();float deltaX = 0;float deltaY = 0;// 获取父控件的宽高int parentWidth = getWidth();int parentHeight = getHeight();// 如果宽度大于屏幕宽度if (rectF.width() >= parentWidth) {if (rectF.left > 0) {// 左边出现了空白deltaX = -rectF.left;// 往左移动}if (rectF.right < parentWidth) {// 右边出现了空白deltaX = parentWidth - rectF.right;// 往右移动}}// 如果高度大于屏幕高度if (rectF.height() >= parentHeight) {if (rectF.top > 0) {// 上边出现了空白deltaY = -rectF.top;// 往下移动}if (rectF.bottom < parentHeight) {// 下面出现了空白deltaY = parentHeight - rectF.bottom;// 往下移动}}// 如果宽度小于父控件的宽度if (rectF.width() < parentWidth) {// 要基中显示deltaX = parentWidth * 0.5f - rectF.right + 0.5f * rectF.width();}// 如果高度消息小于父控件的高度if (rectF.height() < parentHeight) {// 需要基中显示deltaY = parentHeight * 0.5f - rectF.bottom + 0.5f * rectF.height();}// 将图片移动到父控件中心matrix.postTranslate(deltaX, deltaY);}/** * 获取图片通过矩阵控制缩放之后的矩形 *  * @return */private RectF getMatrixRectF() {Matrix matrix2 = matrix;RectF rectF = new RectF();Drawable drawable = this.getDrawable();if (drawable != null) {rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());matrix2.mapRect(rectF);}return rectF;}@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {// 用户缩放手机探测器处理触摸事件mScaleGestureDetector.onTouchEvent(motionEvent);return true;}}
实现效果图


总结

对图片的伸缩释放已经实现了,但是相信读者也能够知道,单单只实现多点触控自由伸缩是不够的,用户还希望可以左右滑动实现图片切换浏览,该部分的实现将在近期发布,尽情期待。


代码下载地址






0 0
原创粉丝点击