大多android应用都会有“查看原图”的功能,因原图通常都大于手机屏幕,所以就需要有拖拽,缩放的操作来满足原图的预览。大图预览的一个原则就是:尽量在手机屏幕上居中呈现完整的原图。
由于原图大小和手机屏幕的大小不一,在初始化的时候会出现下面几种处理方式(绿色为手机屏幕,红色为图片)。
1.图片高小于屏幕,宽大于屏幕
2.图片高大于屏幕,宽小于屏幕
3.图片高和宽都小于屏幕
4.图片高和宽都大于屏幕(又分两种情况)
图片的高和宽的缩放都是同比例的,而且所有缩放后的图片都是居中显示的。对图片进行缩放,拖拽时还有校验操作,比如下面的情况:
如果图片和屏幕边框之间有空白,移动图片消除空白部分。
Android实现这些操作的两个必不可少的知识就是onTouch事件,Matrix。通过onTouch事件判断你的手势,通过matrix实现图片的拖拽缩放。
onTouch即屏幕触摸事件,必须知道的几个事件:
MotionEvent.ACTION_DOWN:第一根手指按下
MotionEvent.ACTION_POINTER_DOWN:第二根手指按下
MotionEvent.ACTION_UP:第一根手指离开
MotionEvent.ACTION_POINTER_UP:第二根手指离开
MotionEvent.ACTION_MOVE:手指滑动
拖拽效果的实现:通过MotionEvent.ACTION_DOWN获得第一根手指按下的坐标A,通过MotionEvent.ACTION_MOVE获得滑动后的坐标B,于是便有了A到B的一个偏移,通过这个偏移完成拖拽效果。
缩放效果的实现:通过MotionEvent.ACTION_DOWN获得第一根手指按下的坐标A,通过MotionEvent.ACTION_POINTER_DOWN获得第二根手指按下的坐标B,于是便有了两点间距AB1。通过MotionEvent.ACTION_MOVE会获得新的A,B两点间距AB2。通过AB2/AB1的值便得知缩放比例。
Matrix没什么可说的,看看API都明白了。有一点要注意的是matrix.postXXX方法指的是在当前matirx基础上进行相应操作,matrix.setXXX是初始化当前matrix后进行的相应操作。
实现代码:
XML:
android:scaleType="matrix",这句话是重点
JAVA:
package com.example.showimgdemo;
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.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
public class ShowImage extends Activity implements OnTouchListener {
private ImageView imgv;
private PointF point0 = new PointF();
private PointF pointM = new PointF();
private final float ZOOM_MIN_SPACE = 10f;
// 设定事件模式
private final int NONE = 0;
private final int DRAG = 1;
private final int ZOOM = 2;
private int mode = NONE;
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
// 获取屏幕分辨率。以480*320为例
private int displayHeight = 480;
private int displayWidth = 320;
private float minScale = 1f;
private float maxScale = 10f;
private float currentScale = 1f;
private float oldDist;
private Bitmap bm;
private int imgWidth;
private int imgHeight;
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.show_image);
init();
}
private void init() {
imgv = (ImageView) findViewById(R.id.imgv);
imgv.setOnTouchListener(this);
bm = BitmapFactory.decodeResource(getResources(),
R.drawable.test2);
imgWidth = bm.getWidth();
imgHeight = bm.getHeight();
imgv.setImageBitmap(bm);
minScale = getMinScale();
matrix.setScale(minScale, minScale);
center();
imgv.setImageMatrix(matrix);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView imgv = (ImageView) v;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
point0.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > ZOOM_MIN_SPACE) {
savedMatrix.set(matrix);
setMidPoint(event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
whenMove(event);
break;
}
imgv.setImageMatrix(matrix);
checkView();
return true;
}
private void whenMove(MotionEvent event) {
switch (mode) {
case DRAG:
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - point0.x, event.getY()
- point0.y);
break;
case ZOOM:
float newDist = spacing(event);
if (newDist > ZOOM_MIN_SPACE) {
matrix.set(savedMatrix);
float sxy = newDist / oldDist;
System.out.println(sxy + "<==放大缩小倍数");
matrix.postScale(sxy, sxy, pointM.x, pointM.y);
}
break;
}
}
// 两个触点的距离
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
private void setMidPoint(MotionEvent event) {
float x = event.getX(0) + event.getY(1);
float y = event.getY(0) + event.getY(1);
pointM.set(x / 2, y / 2);
}
// 图片居中
private void center() {
RectF rect = new RectF(0, 0, imgWidth, imgHeight);
matrix.mapRect(rect);
float width = rect.width();
float height = rect.height();
float dx = 0;
float dy = 0;
if (width < displayWidth)
dx = displayWidth / 2 - width / 2 - rect.left;
else if (rect.left > 0)
dx = -rect.left;
else if (rect.right < displayWidth)
dx = displayWidth - rect.right;
if (height < displayHeight)
dy = displayHeight / 2 - height / 2 - rect.top;
else if (rect.top > 0)
dy = -rect.top;
else if (rect.bottom < displayHeight)
dy = displayHeight - rect.bottom;
matrix.postTranslate(dx, dy);
}
// 获取最小缩放比例
private float getMinScale() {
float sx = (float) displayWidth / imgWidth;
float sy = (float) displayHeight / imgHeight;
float scale = sx < sy ? sx : sy;
if (scale > 1) {
scale = 1f;
}
return scale;
}
// 检查约束条件,是否居中,空间显示是否合理
private void checkView() {
currentScale = getCurrentScale();
if (mode == ZOOM) {
if (currentScale < minScale) {
matrix.setScale(minScale, minScale);
}
if (currentScale > maxScale) {
matrix.set(savedMatrix);
}
}
center();
}
// 图片当前的缩放比例
private float getCurrentScale() {
float[] values = new float[9];
matrix.getValues(values);
return values[Matrix.MSCALE_X];
}
}