實現Android圖片預覽的拖拽縮放

大多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];
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章