码迷,mamicode.com
首页 > 移动开发 > 详细

[Android]可缩放性ImageView(可以放大缩小)

时间:2015-07-31 20:25:59      阅读:482      评论:0      收藏:0      [点我收藏+]

标签:imageview   移动   matrix   自定义   位图   

由于项目需求的原因,最近一直在研究可缩放性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(可以放大缩小)

标签:imageview   移动   matrix   自定义   位图   

原文地址:http://blog.csdn.net/u011596810/article/details/47173785

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!