由于项目需求的原因,最近一直在研究可缩放性ImageView,用本文来记录一下最近所学:
该ImageView的实现功能有:
1)初步打开时,图片按比例满屏(填充ImageView)显示。
2)在放大缩小过程中,可以控制最大放大比例和最小缩小比例。
3)在缩放过程中,若图片的宽或高小于ImageView,则在图片在宽或高居中显示。
4)在放大后,可以移动图片,并且限制好移动的边界,不会超出图片。
5)实现双击放大或缩小的功能。(若当前图片显示为最大的比例则缩小为最小比例,若不是最小比例则放大了最大比例)
在讲代码之前,首先应该说说一个类,Matrix。因为我们在处理图片的过程中,需要图片的位移,缩放等等,而Matrix刚好就是帮我们封装好了这些数据,具体的,大家可以看看这篇文章:android 从matrix获取处理过的图片的实际宽度重点是了解Matrix里面数组的含义。
上代码,具体的说明代码都有一一介绍:
MyZoomImageView.java文件:
package com.xiaoyan.doubletouch; import android.content.Context; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.FloatMath; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; /** * 缩放ImageView * * @author xiejinxiong * */ public class MyZoomImageView extends ImageView { /** ImageView高度 */ private int imgHeight; /** ImageView宽度 */ private int imgWidth; /** 图片高度 */ private int intrinsicHeight; /** 图片宽度 */ private int intrinsicWidth; /** 最大缩放级别 */ private float mMaxScale = 2.0f; /** 最小缩放级别 */ private float mMinScale = 0.2f; /** 用于记录拖拉图片移动的坐标位置 */ private Matrix matrix = new Matrix(); /** 用于记录图片要进行拖拉时候的坐标位置 */ private Matrix currentMatrix = new Matrix(); /** 记录第一次点击的时间 */ private long firstTouchTime = 0; /** 时间点击的间隔 */ private int intervalTime = 250; /** 第一次点完坐标 */ private PointF firstPointF; public MyZoomImageView(Context context) { super(context); initUI(); } public MyZoomImageView(Context context, AttributeSet attrs) { super(context, attrs); initUI(); } public MyZoomImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initUI(); } /** * 初始化UI */ private void initUI() { this.setScaleType(ScaleType.FIT_CENTER); this.setOnTouchListener(new TouchListener()); getImageViewWidthHeight(); getIntrinsicWidthHeight(); } /** * 获得图片内在宽高 */ private void getIntrinsicWidthHeight() { Drawable drawable = this.getDrawable(); // 初始化bitmap的宽高 intrinsicHeight = drawable.getIntrinsicHeight(); intrinsicWidth = drawable.getIntrinsicWidth(); } private final class TouchListener implements OnTouchListener { /** 记录是拖拉照片模式还是放大缩小照片模式 */ private int mode = 0;// 初始状态 /** 拖拉照片模式 */ private static final int MODE_DRAG = 1; /** 放大缩小照片模式 */ private static final int MODE_ZOOM = 2; /** 用于记录开始时候的坐标位置 */ private PointF startPoint = new PointF(); /** 两个手指的开始距离 */ private float startDis; /** 两个手指的中间点 */ private PointF midPoint; public boolean onTouch(View v, MotionEvent event) { /** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */ switch (event.getAction() & MotionEvent.ACTION_MASK) {// 单点监听和多点触碰监听 // 手指压下屏幕 case MotionEvent.ACTION_DOWN: mode = MODE_DRAG; // 记录ImageView当前的移动位置 currentMatrix.set(getImageMatrix()); startPoint.set(event.getX(), event.getY()); matrix.set(currentMatrix); makeImageViewFit(); break; // 手指在屏幕上移动,改事件会被不断触发 case MotionEvent.ACTION_MOVE: // 拖拉图片 if (mode == MODE_DRAG) { // System.out.println("ACTION_MOVE_____MODE_DRAG"); float dx = event.getX() - startPoint.x; // 得到x轴的移动距离 float dy = event.getY() - startPoint.y; // 得到x轴的移动距离 // 在没有移动之前的位置上进行移动z matrix.set(currentMatrix); float[] values = new float[9]; matrix.getValues(values); dx = checkDxBound(values, dx); dy = checkDyBound(values, dy); matrix.postTranslate(dx, dy); } // 放大缩小图片 else if (mode == MODE_ZOOM) { float endDis = distance(event);// 结束距离 if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10 float scale = endDis / startDis;// 得到缩放倍数 matrix.set(currentMatrix); float[] values = new float[9]; matrix.getValues(values); scale = checkFitScale(scale, values); matrix.postScale(scale, scale, midPoint.x, midPoint.y); } } break; // 手指离开屏幕 case MotionEvent.ACTION_UP: setDoubleTouchEvent(event); case MotionEvent.ACTION_POINTER_UP: // System.out.println("ACTION_POINTER_UP"); mode = 0; // matrix.set(currentMatrix); float[] values = new float[9]; matrix.getValues(values); makeImgCenter(values); break; // 当屏幕上已经有触点(手指),再有一个触点压下屏幕 case MotionEvent.ACTION_POINTER_DOWN: // System.out.println("ACTION_POINTER_DOWN"); mode = MODE_ZOOM; /** 计算两个手指间的距离 */ startDis = distance(event); /** 计算两个手指间的中间点 */ if (startDis > 10f) { // 两个手指并拢在一起的时候像素大于10 midPoint = mid(event); // 记录当前ImageView的缩放倍数 currentMatrix.set(getImageMatrix()); } break; } setImageMatrix(matrix); return true; } /** 计算两个手指间的距离 */ private float distance(MotionEvent event) { float dx = event.getX(1) - event.getX(0); float dy = event.getY(1) - event.getY(0); /** 使用勾股定理返回两点之间的距离 */ return FloatMath.sqrt(dx * dx + dy * dy); } /** 计算两个手指间的中间点 */ private PointF mid(MotionEvent event) { float midX = (event.getX(1) + event.getX(0)) / 2; float midY = (event.getY(1) + event.getY(0)) / 2; return new PointF(midX, midY); } /** * 和当前矩阵对比,检验dy,使图像移动后不会超出ImageView边界 * * @param values * @param dy * @return */ private float checkDyBound(float[] values, float dy) { float height = imgHeight; if (intrinsicHeight * values[Matrix.MSCALE_Y] < height) return 0; if (values[Matrix.MTRANS_Y] + dy > 0) dy = -values[Matrix.MTRANS_Y]; else if (values[Matrix.MTRANS_Y] + dy < -(intrinsicHeight * values[Matrix.MSCALE_Y] - height)) dy = -(intrinsicHeight * values[Matrix.MSCALE_Y] - height) - values[Matrix.MTRANS_Y]; return dy; } /** * 和当前矩阵对比,检验dx,使图像移动后不会超出ImageView边界 * * @param values * @param dx * @return */ private float checkDxBound(float[] values, float dx) { float width = imgWidth; if (intrinsicWidth * values[Matrix.MSCALE_X] < width) return 0; if (values[Matrix.MTRANS_X] + dx > 0) dx = -values[Matrix.MTRANS_X]; else if (values[Matrix.MTRANS_X] + dx < -(intrinsicWidth * values[Matrix.MSCALE_X] - width)) dx = -(intrinsicWidth * values[Matrix.MSCALE_X] - width) - values[Matrix.MTRANS_X]; return dx; } /** * MSCALE用于处理缩放变换 * * * MSKEW用于处理错切变换 * * * MTRANS用于处理平移变换 */ /** * 检验scale,使图像缩放后不会超出最大倍数 * * @param scale * @param values * @return */ private float checkFitScale(float scale, float[] values) { if (scale * values[Matrix.MSCALE_X] > mMaxScale) scale = mMaxScale / values[Matrix.MSCALE_X]; if (scale * values[Matrix.MSCALE_X] < mMinScale) scale = mMinScale / values[Matrix.MSCALE_X]; return scale; } /** * 促使图片居中 * * @param values * (包含着图片变化信息) */ private void makeImgCenter(float[] values) { // 缩放后图片的宽高 float zoomY = intrinsicHeight * values[Matrix.MSCALE_Y]; float zoomX = intrinsicWidth * values[Matrix.MSCALE_X]; // 图片左上角Y坐标 float leftY = values[Matrix.MTRANS_Y]; // 图片左上角X坐标 float leftX = values[Matrix.MTRANS_X]; // 图片右下角Y坐标 float rightY = leftY + zoomY; // 图片右下角X坐标 float rightX = leftX + zoomX; // 使图片垂直居中 if (zoomY < imgHeight) { float marY = (imgHeight - zoomY) / 2.0f; matrix.postTranslate(0, marY - leftY); } // 使图片水平居中 if (zoomX < imgWidth) { float marX = (imgWidth - zoomX) / 2.0f; matrix.postTranslate(marX - leftX, 0); } // 使图片缩放后上下不留白(即当缩放后图片的大小大于imageView的大小,但是上面或下面留出一点空白的话,将图片移动占满空白处) if (zoomY >= imgHeight) { if (leftY > 0) {// 判断图片上面留白 matrix.postTranslate(0, -leftY); } if (rightY < imgHeight) {// 判断图片下面留白 matrix.postTranslate(0, imgHeight - rightY); } } // 使图片缩放后左右不留白 if (zoomX >= imgWidth) { if (leftX > 0) {// 判断图片左边留白 matrix.postTranslate(-leftX, 0); } if (rightX < imgWidth) {// 判断图片右边不留白 matrix.postTranslate(imgWidth - rightX, 0); } } } } /** * 获取ImageView的宽高 */ private void getImageViewWidthHeight() { ViewTreeObserver vto2 = getViewTreeObserver(); vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @SuppressWarnings("deprecation") public void onGlobalLayout() { getViewTreeObserver().removeGlobalOnLayoutListener(this); imgWidth = getWidth(); imgHeight = getHeight(); } }); } /** * 使得ImageView一开始便显示最适合的宽高比例,便是刚好容下的样子 */ private void makeImageViewFit() { if (getScaleType() != ScaleType.MATRIX) { setScaleType(ScaleType.MATRIX); matrix.postScale(1.0f, 1.0f, imgWidth / 2, imgHeight / 2); } } /** * 双击事件触发 * * @param values */ private void setDoubleTouchEvent(MotionEvent event) { float values[] = new float[9]; matrix.getValues(values); // 存储当前时间 long currentTime = System.currentTimeMillis(); // 判断两次点击间距时间是否符合 if (currentTime - firstTouchTime >= intervalTime) { firstTouchTime = currentTime; firstPointF = new PointF(event.getX(), event.getY()); } else { // 判断两次点击之间的距离是否小于30f if (Math.abs(event.getX() - firstPointF.x) < 30f && Math.abs(event.getY() - firstPointF.y) < 30f) { // 判断当前缩放比例与最大最小的比例 if (values[Matrix.MSCALE_X] < mMaxScale) { matrix.postScale(mMaxScale / values[Matrix.MSCALE_X], mMaxScale / values[Matrix.MSCALE_X], event.getX(), event.getY()); } else { matrix.postScale(mMinScale / values[Matrix.MSCALE_X], mMinScale / values[Matrix.MSCALE_X], event.getX(), event.getY()); } } } } /** * 设置图片的最大和最小的缩放比例 * * @param mMaxScale * @param mMinScale */ public void setPicZoomHeightWidth(float mMaxScale, float mMinScale) { this.mMaxScale = mMaxScale; this.mMinScale = mMinScale; } }xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.xiaoyan.doubletouch.MainActivity" > <com.xiaoyan.doubletouch.MyZoomImageView android:id="@+id/imageView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:src="@drawable/a" /> </RelativeLayout>
最初显示:
放大显示:
版权声明:本文为博主原创文章,未经博主允许不得转载。
[Android]可缩放性ImageView(可以放大缩小)
原文地址:http://blog.csdn.net/u011596810/article/details/47173785