Android實現ImageView圖片縮放和拖動

Android實現ImageView圖片縮放和拖動

今天我們來編寫一個縮放效果的ImageView ,網上有很多人都講了這些。但有許多人都直接使用了庫檔案,

那麼我們今天做的是直接上程式碼編寫一個拖動和縮放的ImageView,具體看效果圖

那麼簡單了分析一下。在手機上縮放圖片和拖動要用到什麼?手指對不對

那麼控制元件上什麼事件和手機有關。View.OnTouchListener 對不對。

ok,那麼先新建一個Class
···
public class BaseDragZoomImageView extends ImageView implements View.OnTouchListener
···

沒錯,繼承ImageView 並且新增View.OnTouchListener事件

然後我們看看建構函式


public BaseDragZoomImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setOnTouchListener(this);
}
public BaseDragZoomImageView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
setOnTouchListener(this);
}
public BaseDragZoomImageView(Context context)
{
this(context, null);
setOnTouchListener(this);
}

對,在建構函式中,我們將setOnTouchListener 設定了。
那麼這個setOnTouchListener 具體怎麼做。我們就先分析一下onTouch 中MotionEvent。
我們都知道手指的操作有很多,那麼Andorid自然也將這種情況分了很多種case。
– MotionEvent.ACTION_DOWN 手指按下螢幕
– MotionEvent.ACTION_MOVE 手指在螢幕上移動
– MotionEvent.ACTION_UP 手指離開螢幕
– MotionEvent.ACTION_POINTER_UP 當觸點離開螢幕,但是螢幕上還有觸點(手指)
– MotionEvent.ACTION_POINTER_DOWN 當螢幕上已經有觸點(手指),再有一個觸點壓下螢幕

很顯然,這些看起來好像都能夠用得到。


public boolean onTouch(View v, MotionEvent event) {
/** 通過與運算保留最後八位 MotionEvent.ACTION_MASK = 255 */
switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 手指壓下螢幕
case MotionEvent.ACTION_DOWN:
mode = MODE_DRAG;
// 記錄ImageView當前的移動位置
currentMatrix.set(getImageMatrix());
startPoint.set(event.getX(), event.getY());
break;
// 手指在螢幕上移動,改事件會被不斷觸發
case MotionEvent.ACTION_MOVE:
// 拖拉圖片
if (mode == MODE_DRAG) {
float dx = event.getX() - startPoint.x; // 得到x軸的移動距離
float dy = event.getY() - startPoint.y; // 得到x軸的移動距離
// 在沒有移動之前的位置上進行移動
matrix.set(currentMatrix);
matrix.postTranslate(dx, dy);
}
// 放大縮小圖片
else if (mode == MODE_ZOOM) {
float endDis = distance(event);// 結束距離
if (endDis > 10f) { // 兩個手指併攏在一起的時候畫素大於10
float scale = endDis / startDis;// 得到縮放倍數
matrix.set(currentMatrix);
matrix.postScale(scale, scale,midPoint.x,midPoint.y);
}
}
break;
// 手指離開螢幕
case MotionEvent.ACTION_UP:
// 當觸點離開螢幕,但是螢幕上還有觸點(手指)
case MotionEvent.ACTION_POINTER_UP:
mode = 0;
break;
// 當螢幕上已經有觸點(手指),再有一個觸點壓下螢幕
case MotionEvent.ACTION_POINTER_DOWN:
mode = MODE_ZOOM;
/** 計算兩個手指間的距離 */
startDis = distance(event);
/** 計算兩個手指間的中間點 */
if (startDis > 10f) { // 兩個手指併攏在一起的時候畫素大於10
midPoint = mid(event);
//記錄當前ImageView的縮放倍數
currentMatrix.set(getImageMatrix());
}
break;
}
setImageMatrix(matrix);
return true;
}

ok,收工。具體就只有這麼多。我們來看下整個class


package com.jonkming.easyui.image.dragzoom.ui;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.util.FloatMath;
/**
* 圖片縮放和拖動類
* @Title: BaseDragZoomImageView.java
* @Package com.jonkming.easyui.image.dragzoom.ui
* @author HuangMingming
* @date 2016/11/7 17:40
* @version V1.0
*/
public class BaseDragZoomImageView extends ImageView implements View.OnTouchListener{
/** 記錄是拖拉照片模式還是放大縮小照片模式 */
private int mode = 0;// 初始狀態
/** 拖拉照片模式 */
private static final int MODE_DRAG = 1;
/** 放大縮小照片模式 */
private static final int MODE_ZOOM = 2;
/** 用於記錄開始時候的座標位置 */
private PointF startPoint = new PointF();
/** 用於記錄拖拉圖片移動的座標位置 */
private Matrix matrix = new Matrix();
/** 用於記錄圖片要進行拖拉時候的座標位置 */
private Matrix currentMatrix = new Matrix();
/** 兩個手指的開始距離 */
private float startDis;
/** 兩個手指的中間點 */
private PointF midPoint;
public BaseDragZoomImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setOnTouchListener(this);
}
public BaseDragZoomImageView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
setOnTouchListener(this);
}
public BaseDragZoomImageView(Context context)
{
this(context, null);
setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
/** 通過與運算保留最後八位 MotionEvent.ACTION_MASK = 255 */
switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 手指壓下螢幕
case MotionEvent.ACTION_DOWN:
mode = MODE_DRAG;
// 記錄ImageView當前的移動位置
currentMatrix.set(getImageMatrix());
startPoint.set(event.getX(), event.getY());
break;
// 手指在螢幕上移動,改事件會被不斷觸發
case MotionEvent.ACTION_MOVE:
// 拖拉圖片
if (mode == MODE_DRAG) {
float dx = event.getX() - startPoint.x; // 得到x軸的移動距離
float dy = event.getY() - startPoint.y; // 得到x軸的移動距離
// 在沒有移動之前的位置上進行移動
matrix.set(currentMatrix);
matrix.postTranslate(dx, dy);
}
// 放大縮小圖片
else if (mode == MODE_ZOOM) {
float endDis = distance(event);// 結束距離
if (endDis > 10f) { // 兩個手指併攏在一起的時候畫素大於10
float scale = endDis / startDis;// 得到縮放倍數
matrix.set(currentMatrix);
matrix.postScale(scale, scale,midPoint.x,midPoint.y);
}
}
break;
// 手指離開螢幕
case MotionEvent.ACTION_UP:
// 當觸點離開螢幕,但是螢幕上還有觸點(手指)
case MotionEvent.ACTION_POINTER_UP:
mode = 0;
break;
// 當螢幕上已經有觸點(手指),再有一個觸點壓下螢幕
case MotionEvent.ACTION_POINTER_DOWN:
mode = MODE_ZOOM;
/** 計算兩個手指間的距離 */
startDis = distance(event);
/** 計算兩個手指間的中間點 */
if (startDis > 10f) { // 兩個手指併攏在一起的時候畫素大於10
midPoint = mid(event);
//記錄當前ImageView的縮放倍數
currentMatrix.set(getImageMatrix());
}
break;
}
setImageMatrix(matrix);
return true;
}
/** 計算兩個手指間的距離 */
private float distance(MotionEvent event) {
float dx = event.getX(1) - event.getX(0);
float dy = event.getY(1) - event.getY(0);
/** 使用勾股定理返回兩點之間的距離 */
return (float) Math.sqrt(dx * dx   dy * dy);
}
/** 計算兩個手指間的中間點 */
private PointF mid(MotionEvent event) {
float midX = (event.getX(1)   event.getX(0)) / 2;
float midY = (event.getY(1)   event.getY(0)) / 2;
return new PointF(midX, midY);
}
}

然後看佈局檔案


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.jonkming.easyui.image.dragzoom.ui.BaseDragZoomImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/dragandzoombase"
android:scaleType="matrix"
/>
</LinearLayout>

就是這麼簡單。

程式碼所在位置: GitHub