Androdi-学习----- 图片的放大缩小(1)

1 篇文章 0 订阅
1 篇文章 0 订阅
最近在做图库的时候,遇到了图片放大缩小的问题,其实在今后的开发中图片的放大缩小移动的问题是很常见的.

ok 现在来详细探究一下放大缩小究竟要如何去实现, 实现的原理又是怎样

实现的方法其实很简单, 主要用到了一个对象Matrix  图片的缩放移动矩阵对象
Matirx 对象他是一个 3*3 的二位数组, 具体关于该对象的详细描述,后面再说,
先来谈谈如何利用该对象进行放大缩小和移动

大前提,你要想突破随你放大缩小移动, 就得让图片的缩放类型为 Matrix:
this  .setScaleType(ImageView.ScaleType. MATRIX );
或在xml 文件中 加入 android:scaleType  = "matrix"
第一步 :
private  Matrix  matrix  =  new  Matrix();// 先new 一个缩放大矩阵
matrix  =  this  .getImageMatrix(); 获取当前图片的缩放矩阵
第二部:处理该矩阵
1.移动:
matrix  .postTranslate(dx, dy);  表示想x轴移动dx 像素, y轴上移动dy像素
2.放大缩小:

matrix  .postScale(scaleX, scaleY, x, y);  //表示 scaleX 表示x方向上的缩放比例
                                         // scaleY 表示Y方向的缩放比例
                                         // 表示缩放说围绕的点
上面两个方法是可以叠加的, 就是你使用了   postTranslate 和  postScale 两者之间不会冲突

ok 上面只是把我们想要缩放处理的矩阵处理好了, 接下来样让图片说缩放和移动
家下来要做的就是把我们设置好的矩阵值 set到ImageView中
this  .setImageMatrix(   matrix  )

ok 缩放移动的 基本的实现方式就是上面讲的, 但是在实际工作共不可能这么简单,
我们对图片处理时,需要配合手势的 来进行放大缩小移动.  
而且移动不能太快,太慢,要完全根据你的手指移动比例 或 移动距离来计算,那就不是上面说的获取Matrix然后在设置相关值,在set会去那几行代码可以解决的
很多情况下我们是有通过自定义 ImageView 来更加方便的实现放大缩小移动操作的

下面我自己写的放大缩小的 部分源码
自定义ImageView

public class MyImageView extends ImageView {

	// 最大的缩放比
	private float maxSacle = 4f;
	// 最小的缩放比
	private float minScale = 0.25f;

	/**
	 * 构造方法
	 */
	public MyImageView(Context context) {
		super(context);
		init();
	}

	/**
	 * 构造方法 该View 若写在xml 中则调用从方法
	 */
	public MyImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
		this.getImageMatrix();
	}

	/**
	 * 初始化方法 , 初始化一些必要信息
	 */
	public void init() {
		// 设置缩放方式为 MATRIX
		this.setScaleType(ImageView.ScaleType.MATRIX);
		
		this.setImageMatrix(new Matrix());// 设置 Matrix 防止 ImageView自己缩放
	}

	private final float[] mMatrixValues = new float[9];

	public float getScale() {
		// TODO getScale 获取 Scale 暂时使用直接返回 x上的缩放比例 其实到最后进行缩放 的实收 xy 的比例都是一样的
		matrix.getValues(mMatrixValues);
		float scale = mMatrixValues[matrix.MSCALE_X];
		return scale;
	}

	private Matrix matrix = new Matrix();// 处理 Matrix 的中间项 改项一直在改变
	private Matrix baseMatrix = new Matrix();// 保持最初是的状态不变
	private final Matrix displayMatrix = new Matrix();// 在重画 显示是使用这个
														// 注入进ImageView的是这个值

	/**
	 * 缩放处理的方法 通过手势 触控 计算出来的缩放比例 在穿过来之前 和 之前的Scale想乘了得到了相对于图片本身的放大倍数
	 * 在手势缩放时使用该方法
	 * 
	 * @param scale
	 *            相对于图片本身的放大倍数
	 * 
	 * @param x
	 *            缩放 围绕点的坐标x
	 * @param y
	 *            缩放 围绕点的坐标y
	 */
	public void zoomSlowLy(float scale, float x, float y) {
		Log.i("scale", "scale*currentScale -- " + scale);

		// case1
		// 计算缩放比是否符合要求
		if (scale > maxSacle) {
			scale = maxSacle;
		}
		if (scale < minScale) {
			scale = minScale;
		}
		// 放入 Matrix 中变换比例 不能使我们计算好的, Matrix 他自己会去计算
		// 所以 为了 是 超出缩放范围的 也能正常的进行缩放 就把 之前 乘的getScale 在除回来
		// 这样 当 newScale > maxSacle 是 scale 就能等于 maxSacle / getScale();
		float purScale = getScale();
		scale = scale / purScale;
		Log.i("scale", "purScale -- " + purScale);
		Log.i("scale", "scale / purScale -- " + scale);

		matrix.postScale(scale, scale, x, y);// matrix 矩阵变换 处理缩放
		// 把变换完成后的 matrix 在注入回 ImageView中
		this.setImageMatrix(getImageViewMatrix());
		// TODO 处理图片 是的缩放后的图片能够正常显示在屏幕中央
		// center(true, true);
	}

	/**
	 * 将图片 垂直 或 水平 居中
	 * 
	 * @param horizontal
	 * @param vertical
	 */
	protected void center(boolean horizontal, boolean vertical) {
		if (bitmap == null) {// 如果图片为空直接返回
			return;
		}

		Matrix m = getImageViewMatrix();// 获得当前Matrix;
		// 建立与图片适配的矩形
		RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());

		m.mapRect(rect); // 与当前的matrix 想匹配
		// 匹配后的矩形 的 top button left right 分别表示上边 的Y坐标 ,底边的Y坐标 左边的X坐标 右边的X坐标
		float height = rect.height();// 匹配后的 矩形的高度
		float width = rect.width();// 匹配后 矩形的宽度

		float dx = 0f;
		float dy = 0f;
		if (vertical) {
			int viewHeight = getHeight();// 这个事获取 该View的高度,由于使用的fillparaent
											// 所以次数是 屏幕高度
			if (height < viewHeight) {
				dy = (viewHeight - height) / 2 - rect.top;
			} else if (rect.top > 0) {
				dy = -rect.top;
			} else if (rect.bottom < viewHeight) {
				dy = viewHeight - rect.bottom;
			}
		}

		if (horizontal) {
			int viewWidth = getWidth();
			if (width < viewWidth) {
				dx = (viewWidth - width) / 2 - rect.left;
			} else if (rect.left > 0) {
				dx = -rect.left;
			} else if (rect.right < viewWidth) {
				dx = viewWidth - rect.right;
			}
		}
		matrix.postTranslate(dx, dy);
		setImageMatrix(getImageViewMatrix());
	}

	/**
	 * 该方法是直接 让该图片准确的去放大多少倍 
	 * 在手势 缩放的过程中不适用该方法, 用该方法的话 体验不好,缩放的太快了
	 * 
	 * @param scale
	 *            相对当前已购的缩放倍数 上 在 缩放 scale倍
	 * @param x
	 * @param y
	 */
	public void zoomTure(float scale, float x, float y) {
		float curScale = getScale();
		float newScale = scale * curScale;
		Log.i("scale", "getScale();  	   -- " + getScale());
		Log.i("scale", "scale			   -- " + scale);
		Log.i("scale", "scale*currentScale -- " + newScale);
		if (newScale > maxSacle) {
			newScale = maxSacle;
		}
		if (newScale < minScale) {
			newScale = minScale;
		}
		float sureScale = newScale / curScale;
		Log.i("scale", "newScale / curScale -- " + scale);
		matrix.postScale(sureScale, sureScale, x, y);// matrix 矩阵变换 处理缩放
		// 把变换完成后的 matrix 在注入回 ImageView中
		this.setImageMatrix(getImageViewMatrix());
		// TODO 处理图片 是的缩放后的图片能够正常显示在屏幕中央

	}

	public void setImageMatrix(Matrix m) {
		super.setImageMatrix(m);
		this.matrix.set(m);
		Log.i("Matrix", "" + this.getImageMatrix());
	}

	/**
	 * 获取当前 的 一个 matrix值 为了保持matrix 一直可以自己变化, 所以不能认为的随意改变matrix值,所以引入了
	 * baseMatrix baseMatrix 作为处理 matrix 在整个的放大缩小过程中是一直在变动的
	 * 
	 * @return
	 */
	protected Matrix getImageViewMatrix() {
		displayMatrix.set(baseMatrix);
		displayMatrix.postConcat(matrix);
		return displayMatrix;
	}

	// 改ImageView 中的图片
	private Bitmap bitmap;

	/**
	 * 重写 setImageBitmap 方法
	 */
	@Override
	public void setImageBitmap(Bitmap bm) {
		super.setImageBitmap(bm);
		bitmap = bm;
		// 计算 图片载入是 的缩放比例
		// TODO 如果图片之前经过BitmapFactor 缩放处理过此处可能需要注意下
		float scale = calcueLoadScale();
		// 处理缩放
		zoomTure(scale, 0, 0);// 这个时候 图片应该在最顶端
		// 把图片移动到屏幕中央
		layoutToCenter();
	}

	/**
	 * 计算图片在载入时 的缩放比例
	 * 
	 * @return 缩放比例
	 */
	private float calcueLoadScale() {
		float scaleWidth = MyResource.screenWidth / (float) bitmap.getWidth();
		float scaleHeight = MyResource.screenHeight
				/ (float) bitmap.getHeight();
		float scale = Math.min(scaleWidth, scaleHeight);
		minScale = scale / 2;// 重新设置图片的缩放比例
		return scale;
	}

	/**
	 * 将图片放置在屏幕中央. 使用该方法的前提是 当前图片的 左上角 的坐标为 0,0
	 */
	public void layoutToCenter() {
		float width = (float) bitmap.getWidth() * getScale();
		float height = (float) bitmap.getHeight() * getScale();
		float fill_width = MyResource.screenWidth - width;
		float fill_height = MyResource.screenHeight - height;

		// 需要到移动的 x 和y 上的距离
		float tran_width = 0f;
		float tran_height = 0f;
		if (fill_width > 0)
			tran_width = fill_width / 2;
		if (fill_height > 0)
			tran_height = fill_height / 2;
		matrix.postTranslate(tran_width, tran_height);// 设置移动图片 的 Matrix矩阵
		setImageMatrix(getImageViewMatrix());// 将设置好的 matrix 注入Imageview 中
	}

	/**
	 * 移动 图片
	 * 
	 * @param dx
	 *            x轴的移动距离
	 * @param dy
	 *            y轴的移动距离
	 */
	public void move(float dx, float dy) {
		matrix.postTranslate(dx, dy);
		setImageMatrix(getImageViewMatrix());// 将设置好的 matrix 注入Imageview 中
	}
}



缩放移动的的时间监听
public class MyTouchListener implements OnTouchListener {

	private MyImageView myImageView;
	private float currentScale;

	private Matrix currentMatrix = new Matrix();
	private Matrix matrix = new Matrix();
	// 第一个按下的点 滑动 的开始点
	private PointF startPointF = new PointF();
	// 两点触控式时 两个点之间的初始距离
	private float startDistace;
	// 两点触控时 的 两点之间中间点
	private PointF midPointF = new PointF();

	/** 记录是拖拉照片模式还是放大缩小照片模式 */
	private int mode = 0;// 初始状态
	/** 拖拉照片模式 */
	private static final int MODE_DRAG = 1;
	/** 放大缩小照片模式 */
	private static final int MODE_ZOOM = 2;

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		if (v instanceof MyImageView) {
			myImageView = (MyImageView) v;
			/** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */
			switch (event.getAction() & MotionEvent.ACTION_MASK) {
			// 手指压下屏幕
			case MotionEvent.ACTION_DOWN:
				mode = MODE_DRAG;
				currentMatrix.set(myImageView.getImageMatrix());
				// 记录ImageView当前的移动位置
				startPointF.set(event.getX(), event.getY());
				break;
			// 手指在屏幕上移动,改事件会被不断触发
			case MotionEvent.ACTION_MOVE:
				// 拖拉图片
				if (mode == MODE_DRAG) {
					float dx = event.getX() - startPointF.x; // 得到x轴的移动距离
					float dy = event.getY() - startPointF.y; // 得到y轴的移动距离
					// 在没有移动之前的位置上进行移动
					matrix.set(currentMatrix); // 在不使用拖动效果较好
					// // currentMatrix的更新会有一个延时
					// // 如果在image 中使用 想缩放那样的方法这回导致 拖动距离很大, 那是成比例的一直 拖动
					matrix.postTranslate(dx, dy);
					myImageView.setImageMatrix(matrix);
				}
				// 放大缩小图片
				else if (mode == MODE_ZOOM) {
					float endDis = distance(event);// 结束距离
					if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10
						float scale = endDis / startDistace;// 得到缩放倍数=
						// myImageView 处理缩放事件scale * currentScale得到的是相对于图片本身的缩放倍数
						myImageView.zoomSlowLy(scale * currentScale,
								midPointF.x, midPointF.y);
					}
				}
				break;
			// 手指离开屏幕
			case MotionEvent.ACTION_UP:
				// 当触点离开屏幕,但是屏幕上还有触点(手指)
			case MotionEvent.ACTION_POINTER_UP:
				mode = 0;
				break;
			// 当屏幕上已经有触点(手指),再有一个触点压下屏幕
			case MotionEvent.ACTION_POINTER_DOWN:
				mode = MODE_ZOOM;
				/** 计算两个手指间的距离 */
				startDistace = distance(event);
				/** 计算两个手指间的中间点 */
				if (startDistace > 10f) { // 两个手指并拢在一起的时候像素大于10
					midPointF = mid(event);
					// 记录当前ImageView的缩放倍数
					currentScale = myImageView.getScale();
				}
				break;
			}
		}
		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);
	}

}


一些效果图片:
1.最开始 图片载入时

图片 缩小时

图片放大时


图片移动


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值