大多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:
<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=".ShowImage" >
<ImageView
android:id="@+id/imgv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix" />
</RelativeLayout>
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];
}
}