您的位置:首页 > 移动开发 > Android开发

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

2012-06-27 10:00 776 查看
之前见到好多人是用替换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,所以只支持多点触摸进行缩放,测试需在真机中运行,所以,在这里向用模拟器测试的同学表示遗憾。
好了,至此,由于我也是在模拟器中运行,所以,缩放效果无法展示,请大家自测:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: