现在的位置: 首页 > 综合 > 正文

Android利用Matrix实现图片的放大,缩小以及移动控制

2013年10月05日 ⁄ 综合 ⁄ 共 4756字 ⁄ 字号 评论关闭

之前见到好多人是用替换ImageView或者用Bitmap的属性设置来实现图片的缩放和移动,这样做会很容易引起虚拟机的OOM错误和不易控制,最重要的是看起来很复杂。

如果直接用ImageView和Matrix呢?

废话不多说,直接上代码,大家看运行效果吧。

package sunbo.app.preview;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

public class ImagePreview extends Activity implements OnTouchListener
{
	private ImageView mImageView;

	private final Matrix matrix = new Matrix();
	private final Matrix savedMatrix = new Matrix();
	private DisplayMetrics mDisplyMetrcs;
	private Bitmap mBitmap;

	private float minScaleR;// 最小缩放比例
	private static final float MAX_SCALE = 4f;// 最大缩放比例

	private static final int NONE = 0;// 初始状态
	private static final int DRAG = 1;// 拖动
	private static final int ZOOM = 2;// 缩放
	private int mode = NONE;

	private final PointF prev = new PointF();
	private final PointF mid = new PointF();
	private float dist = 1f;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.main);
		this.setupViews();
	}

	private void setupViews()
	{
		// 实例化图片控件
		mImageView = (ImageView) this.findViewById(R.id.mImageView);
		// 引用本地资源文件创建位图对象
		mBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.image);
		// 将位图对象设置到图片控件中
		mImageView.setImageBitmap(mBitmap);
		// 为图片控件添加触控事件
		mImageView.setOnTouchListener(this);
		// 获取当前屏幕分辨率对象
		mDisplyMetrcs = new DisplayMetrics();
		this.getWindowManager().getDefaultDisplay().getMetrics(mDisplyMetrcs);
		this.setMinZoom();
		this.setCenter();
		new Handler().postDelayed(new Runnable()
		{
			public void run()
			{
				// 程序启动0.5秒以后设置图片控件的缩放属性
				// 如果在描述文件或一开始就设置,那么,图片就会出现在屏幕的左上角,而我们希望图片出现在屏幕的中间位置
				mImageView.setScaleType(ImageView.ScaleType.MATRIX);
			}
		}, 500);
		mImageView.setImageMatrix(matrix);
	}

	@Override
	public boolean onTouch(View v, MotionEvent event)
	{
		switch (event.getAction() & MotionEvent.ACTION_MASK)
		{
		// 主点按下
		case MotionEvent.ACTION_DOWN:
			savedMatrix.set(matrix);
			prev.set(event.getX(), event.getY());
			mode = DRAG;
			break;
		// 副点按下
		case MotionEvent.ACTION_POINTER_DOWN:
			dist = spacing(event);
			// 如果连续两点距离大于10,则判定为多点模式
			if (spacing(event) > 10f)
			{
				savedMatrix.set(matrix);
				midPoint(mid, event);
				mode = ZOOM;
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_POINTER_UP:
			mode = NONE;
			break;
		case MotionEvent.ACTION_MOVE:
			if (mode == DRAG)
			{
				matrix.set(savedMatrix);
				matrix.postTranslate(event.getX() - prev.x, event.getY() - prev.y);
			} else if (mode == ZOOM)
			{
				float newDist = spacing(event);
				if (newDist > 10f)
				{
					matrix.set(savedMatrix);
					float tScale = newDist / dist;
					matrix.postScale(tScale, tScale, mid.x, mid.y);
				}
			}
			break;
		}
		mImageView.setImageMatrix(matrix);
		this.checkView();
		return true;
	}

	private void checkView()
	{
		float p[] = new float[9];
		matrix.getValues(p);
		if (mode == ZOOM)
		{
			if (p[0] < minScaleR)
				matrix.setScale(minScaleR, minScaleR);
			if (p[0] > MAX_SCALE)
				matrix.set(savedMatrix);
		}
		this.setCenter();
	}

	/**
	 * 设置最小缩放比列,最大值和图片大小相等
	 */
	private void setMinZoom()
	{
		minScaleR = Math.min(
				(float) mDisplyMetrcs.widthPixels / (float) mBitmap.getWidth(),
				(float) mDisplyMetrcs.heightPixels
						/ (float) mBitmap.getHeight());
		if (minScaleR < 1.0)
			matrix.postScale(minScaleR, minScaleR);
	}

	private void setCenter()
	{
		this.setCenter(true, true);
	}

	private void setCenter(boolean horizontal, boolean vertical)
	{
		Matrix mMatrix = new Matrix();
		mMatrix.set(matrix);
		RectF mRectF = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
		mMatrix.mapRect(mRectF);

		float height = mRectF.height();
		float width = mRectF.width();

		float deltaX = 0, deltaY = 0;

		if (vertical)
		{
			// 图片小于屏幕大小,则居中显示。大于屏幕,上方留空则往上移,下方留空则往下移
			int screenHeight = mDisplyMetrcs.heightPixels;
			if (height < screenHeight)
				deltaY = (screenHeight - height) / 2 - mRectF.top;
			else if (mRectF.top > 0)
				deltaY = -mRectF.top;
			else if (mRectF.bottom < screenHeight)
				deltaY = mImageView.getHeight() - mRectF.bottom;
		}

		if (horizontal)
		{
			int screenWidth = mDisplyMetrcs.widthPixels;
			if (width < screenWidth)
				deltaX = (screenWidth - width) / 2 - mRectF.left;
			else if (mRectF.left > 0)
				deltaX = -mRectF.left;
			else if (mRectF.right < screenWidth)
				deltaX = screenWidth - mRectF.right;
		}
		matrix.postTranslate(deltaX, deltaY);
	}

	/**
	 * 两点的距离
	 * 
	 * @param event
	 * @return
	 */
	private float spacing(MotionEvent event)
	{
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	/**
	 * 两点的中点
	 * 
	 * @param point
	 * @param event
	 */
	private void midPoint(PointF point, MotionEvent event)
	{
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		point.set(x / 2, y / 2);
	}
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ImageView
        android:id="@+id/mImageView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

好了,到此结束。

程序源码下载

本程序是一个简单的Demo,所以只支持多点触摸进行缩放,测试需在真机中运行,所以,在这里向用模拟器测试的同学表示遗憾。

好了,至此,由于我也是在模拟器中运行,所以,缩放效果无法展示,请大家自测:


抱歉!评论已关闭.