Android图片手势缩放功能的实现

android开发中常常需要对图片放大查看,要实现这一功能,在有限大小的手机屏幕上,手势缩放图片必不可少。

下面为大家提供简单的实现ImageView手势缩放功能

  • 方法1: 自定义一个ImageView

使用用法:

Activity文件中直接使用:

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ZoomImageView img = (ZoomImageView) findViewById(R.id.snoop);
img.setImageResource(R.drawable.snoopy);
img.setMaxZoom(4f);
}
}

下面是自定义代码:ZoomImageView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

public class ZoomImageView extends ImageView {

Matrix matrix;

// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;

// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;


int viewWidth, viewHeight;
static final int CLICK = 3;
float saveScale = 1f;
protected float origWidth, origHeight;
int oldMeasuredWidth, oldMeasuredHeight;


ScaleGestureDetector mScaleDetector;

Context context;

public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}

public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}

private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);

setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
start.set(last);
mode = DRAG;
break;

case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;

case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;

case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}

setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}

});
}

public void setMaxZoom(float x) {
maxScale = x;
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}

@Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}

if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
else
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());

fixTrans();
return true;
}
}

void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];

float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);

if (fixTransX != 0 || fixTransY != 0)
matrix.postTranslate(fixTransX, fixTransY);
}

float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;

if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}

if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}

float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);

//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;

if (saveScale == 1) {
//Fit to screen.
float scale;

Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();

Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);

float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);

// Center the image
float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;

matrix.postTranslate(redundantXSpace, redundantYSpace);

origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
}
  • 方法2: 另一篇自定义一个ImageView

可根据个人使用情况修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

/**
* 自定义的ImageView控制,可对图片进行多点触控缩放和拖动
*/
public class ZoomImageView extends ImageView {

/**
* 初始化状态常量
*/
public static final int STATUS_INIT = 1;

/**
* 图片放大状态常量
*/
public static final int STATUS_ZOOM_OUT = 2;

/**
* 图片缩小状态常量
*/
public static final int STATUS_ZOOM_IN = 3;

/**
* 图片拖动状态常量
*/
public static final int STATUS_MOVE = 4;

/**
* 用于对图片进行移动和缩放变换的矩阵
*/
private Matrix matrix = new Matrix();

/**
* 待展示的Bitmap对象
*/
private Bitmap sourceBitmap;

/**
* 记录当前操作的状态,可选值为STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE
*/
private int currentStatus;

/**
* ZoomImageView控件的宽度
*/
private int width;

/**
* ZoomImageView控件的高度
*/
private int height;

/**
* 记录两指同时放在屏幕上时,中心点的横坐标值
*/
private float centerPointX;

/**
* 记录两指同时放在屏幕上时,中心点的纵坐标值
*/
private float centerPointY;

/**
* 记录当前图片的宽度,图片被缩放时,这个值会一起变动
*/
private float currentBitmapWidth;

/**
* 记录当前图片的高度,图片被缩放时,这个值会一起变动
*/
private float currentBitmapHeight;

/**
* 记录上次手指移动时的横坐标
*/
private float lastXMove = -1;

/**
* 记录上次手指移动时的纵坐标
*/
private float lastYMove = -1;

/**
* 记录手指在横坐标方向上的移动距离
*/
private float movedDistanceX;

/**
* 记录手指在纵坐标方向上的移动距离
*/
private float movedDistanceY;

/**
* 记录图片在矩阵上的横向偏移值
*/
private float totalTranslateX;

/**
* 记录图片在矩阵上的纵向偏移值
*/
private float totalTranslateY;

/**
* 记录图片在矩阵上的总缩放比例
*/
private float totalRatio;

/**
* 记录手指移动的距离所造成的缩放比例
*/
private float scaledRatio;

/**
* 记录图片初始化时的缩放比例
*/
private float initRatio;

/**
* 记录上次两指之间的距离
*/
private double lastFingerDis;

/**
* ZoomImageView构造函数,将当前操作状态设为STATUS_INIT。
*
* @param context
* @param attrs
*/
public ZoomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
currentStatus = STATUS_INIT;
}

/**
* 将待展示的图片设置进来。
*
* @param bitmap
* 待展示的Bitmap对象
*/
public void setImageBitmap(Bitmap bitmap) {
sourceBitmap = bitmap;
invalidate();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
// 分别获取到ZoomImageView的宽度和高度
width = getWidth();
height = getHeight();
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (initRatio == totalRatio) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
getParent().requestDisallowInterceptTouchEvent(true);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 2) {
// 当有两个手指按在屏幕上时,计算两指之间的距离
lastFingerDis = distanceBetweenFingers(event);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() == 1) {
// 只有单指按在屏幕上移动时,为拖动状态
float xMove = event.getX();
float yMove = event.getY();
if (lastXMove == -1 && lastYMove == -1) {
lastXMove = xMove;
lastYMove = yMove;
}
currentStatus = STATUS_MOVE;
movedDistanceX = xMove - lastXMove;
movedDistanceY = yMove - lastYMove;
// 进行边界检查,不允许将图片拖出边界
if (totalTranslateX + movedDistanceX > 0) {
movedDistanceX = 0;
} else if (width - (totalTranslateX + movedDistanceX) > currentBitmapWidth) {
movedDistanceX = 0;
}
if (totalTranslateY + movedDistanceY > 0) {
movedDistanceY = 0;
} else if (height - (totalTranslateY + movedDistanceY) > currentBitmapHeight) {
movedDistanceY = 0;
}
// 调用onDraw()方法绘制图片
invalidate();
lastXMove = xMove;
lastYMove = yMove;
} else if (event.getPointerCount() == 2) {
// 有两个手指按在屏幕上移动时,为缩放状态
centerPointBetweenFingers(event);
double fingerDis = distanceBetweenFingers(event);
if (fingerDis > lastFingerDis) {
currentStatus = STATUS_ZOOM_OUT;
} else {
currentStatus = STATUS_ZOOM_IN;
}
// 进行缩放倍数检查,最大只允许将图片放大4倍,最小可以缩小到初始化比例
if ((currentStatus == STATUS_ZOOM_OUT && totalRatio < 4 * initRatio)
|| (currentStatus == STATUS_ZOOM_IN && totalRatio > initRatio)) {
scaledRatio = (float) (fingerDis / lastFingerDis);
totalRatio = totalRatio * scaledRatio;
if (totalRatio > 4 * initRatio) {
totalRatio = 4 * initRatio;
} else if (totalRatio < initRatio) {
totalRatio = initRatio;
}
// 调用onDraw()方法绘制图片
invalidate();
lastFingerDis = fingerDis;
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerCount() == 2) {
// 手指离开屏幕时将临时值还原
lastXMove = -1;
lastYMove = -1;
}
break;
case MotionEvent.ACTION_UP:
// 手指离开屏幕时将临时值还原
lastXMove = -1;
lastYMove = -1;
break;
default:
break;
}
return true;
}

/**
* 根据currentStatus的值来决定对图片进行什么样的绘制操作。
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (currentStatus) {
case STATUS_ZOOM_OUT:
case STATUS_ZOOM_IN:
zoom(canvas);
break;
case STATUS_MOVE:
move(canvas);
break;
case STATUS_INIT:
initBitmap(canvas);
default:
if (sourceBitmap != null) {
canvas.drawBitmap(sourceBitmap, matrix, null);
}
break;
}
}

/**
* 对图片进行缩放处理。
*
* @param canvas
*/
private void zoom(Canvas canvas) {
matrix.reset();
// 将图片按总缩放比例进行缩放
matrix.postScale(totalRatio, totalRatio);
float scaledWidth = sourceBitmap.getWidth() * totalRatio;
float scaledHeight = sourceBitmap.getHeight() * totalRatio;
float translateX = 0f;
float translateY = 0f;
// 如果当前图片宽度小于屏幕宽度,则按屏幕中心的横坐标进行水平缩放。否则按两指的中心点的横坐标进行水平缩放
if (currentBitmapWidth < width) {
translateX = (width - scaledWidth) / 2f;
} else {
translateX = totalTranslateX * scaledRatio + centerPointX
* (1 - scaledRatio);
// 进行边界检查,保证图片缩放后在水平方向上不会偏移出屏幕
if (translateX > 0) {
translateX = 0;
} else if (width - translateX > scaledWidth) {
translateX = width - scaledWidth;
}
}
// 如果当前图片高度小于屏幕高度,则按屏幕中心的纵坐标进行垂直缩放。否则按两指的中心点的纵坐标进行垂直缩放
if (currentBitmapHeight < height) {
translateY = (height - scaledHeight) / 2f;
} else {
translateY = totalTranslateY * scaledRatio + centerPointY
* (1 - scaledRatio);
// 进行边界检查,保证图片缩放后在垂直方向上不会偏移出屏幕
if (translateY > 0) {
translateY = 0;
} else if (height - translateY > scaledHeight) {
translateY = height - scaledHeight;
}
}
// 缩放后对图片进行偏移,以保证缩放后中心点位置不变
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
currentBitmapWidth = scaledWidth;
currentBitmapHeight = scaledHeight;
canvas.drawBitmap(sourceBitmap, matrix, null);
}

/**
* 对图片进行平移处理
*
* @param canvas
*/
private void move(Canvas canvas) {
matrix.reset();
// 根据手指移动的距离计算出总偏移值
float translateX = totalTranslateX + movedDistanceX;
float translateY = totalTranslateY + movedDistanceY;
// 先按照已有的缩放比例对图片进行缩放
matrix.postScale(totalRatio, totalRatio);
// 再根据移动距离进行偏移
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
canvas.drawBitmap(sourceBitmap, matrix, null);
}

/**
* 对图片进行初始化操作,包括让图片居中,以及当图片大于屏幕宽高时对图片进行压缩。
*
* @param canvas
*/
private void initBitmap(Canvas canvas) {
if (sourceBitmap != null) {
matrix.reset();
int bitmapWidth = sourceBitmap.getWidth();
int bitmapHeight = sourceBitmap.getHeight();
if (bitmapWidth > width || bitmapHeight > height) {
if (bitmapWidth - width > bitmapHeight - height) {
// 当图片宽度大于屏幕宽度时,将图片等比例压缩,使它可以完全显示出来
float ratio = width / (bitmapWidth * 1.0f);
matrix.postScale(ratio, ratio);
float translateY = (height - (bitmapHeight * ratio)) / 2f;
// 在纵坐标方向上进行偏移,以保证图片居中显示
matrix.postTranslate(0, translateY);
totalTranslateY = translateY;
totalRatio = initRatio = ratio;
} else {
// 当图片高度大于屏幕高度时,将图片等比例压缩,使它可以完全显示出来
float ratio = height / (bitmapHeight * 1.0f);
matrix.postScale(ratio, ratio);
float translateX = (width - (bitmapWidth * ratio)) / 2f;
// 在横坐标方向上进行偏移,以保证图片居中显示
matrix.postTranslate(translateX, 0);
totalTranslateX = translateX;
totalRatio = initRatio = ratio;
}
currentBitmapWidth = bitmapWidth * initRatio;
currentBitmapHeight = bitmapHeight * initRatio;
} else {
// 当图片的宽高都小于屏幕宽高时,直接让图片居中显示
float translateX = (width - sourceBitmap.getWidth()) / 2f;
float translateY = (height - sourceBitmap.getHeight()) / 2f;
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
totalRatio = initRatio = 1f;
currentBitmapWidth = bitmapWidth;
currentBitmapHeight = bitmapHeight;
}
canvas.drawBitmap(sourceBitmap, matrix, null);
}
}

/**
* 计算两个手指之间的距离。
*
* @param event
* @return 两个手指之间的距离
*/
private double distanceBetweenFingers(MotionEvent event) {
float disX = Math.abs(event.getX(0) - event.getX(1));
float disY = Math.abs(event.getY(0) - event.getY(1));
return Math.sqrt(disX * disX + disY * disY);
}

/**
* 计算两个手指之间中心点的坐标。
*
* @param event
*/
private void centerPointBetweenFingers(MotionEvent event) {
float xPoint0 = event.getX(0);
float yPoint0 = event.getY(0);
float xPoint1 = event.getX(1);
float yPoint1 = event.getY(1);
centerPointX = (xPoint0 + xPoint1) / 2;
centerPointY = (yPoint0 + yPoint1) / 2;
}

}
  • 方法3: 自定义OnTouchListener

使用用法:

Activity文件中:imageView.setOnTouchListener(new MulitPointTouchListener ());

xml文件中:android:scaleType=”matrix”

下面是自定义监听代码:MulitPointTouchListener.java

public class MulitPointTouchListener implements OnTouchListener {   
    private static final String TAG = "MulitPointTouchListener";   
    // These matrices will be used to move and zoom image   
    Matrix matrix = new Matrix();   
    Matrix savedMatrix = new Matrix();   

    // We can be in one of these 3 states   
    static final int NONE = 0;   
    static final int DRAG = 1;   
    static final int ZOOM = 2;   
    int mode = NONE;   

    // Remember some things for zooming   
    PointF start = new PointF();   
    PointF mid = new PointF();   
    float oldDist = 1f;   

    @Override  
    public boolean onTouch(View v, MotionEvent event) {   

            ImageView view = (ImageView) v;   
            // Log.e("view_width",   
            // view.getImageMatrix()..toString()+"*"+v.getWidth());   
            // Dump touch event to log   
            dumpEvent(event);   

            // Handle touch events here...   
            switch (event.getAction() & MotionEvent.ACTION_MASK) {   
            case MotionEvent.ACTION_DOWN:   

                    matrix.set(view.getImageMatrix());   
                    savedMatrix.set(matrix);   
                    start.set(event.getX(), event.getY());   
                    //Log.d(TAG, "mode=DRAG");   
                    mode = DRAG;   


                    //Log.d(TAG, "mode=NONE");   
                    break;   
            case MotionEvent.ACTION_POINTER_DOWN:   
                    oldDist = spacing(event);   
                    //Log.d(TAG, "oldDist=" + oldDist);   
                    if (oldDist > 10f) {   
                            savedMatrix.set(matrix);   
                            midPoint(mid, event);   
                            mode = ZOOM;   
                            //Log.d(TAG, "mode=ZOOM");   
                    }   
                    break;   
            case MotionEvent.ACTION_UP:   
            case MotionEvent.ACTION_POINTER_UP:   
                    mode = NONE;   
                    //Log.e("view.getWidth", view.getWidth() + "");   
                    //Log.e("view.getHeight", view.getHeight() + "");   

                    break;   
            case MotionEvent.ACTION_MOVE:   
                    if (mode == DRAG) {   
                            // ...   
                            matrix.set(savedMatrix);   
                            matrix.postTranslate(event.getX() - start.x, event.getY()   
                                            - start.y);   
                    } else if (mode == ZOOM) {   
                            float newDist = spacing(event);   
                            //Log.d(TAG, "newDist=" + newDist);   
                            if (newDist > 10f) {   
                                    matrix.set(savedMatrix);   
                                    float scale = newDist / oldDist;   
                                    matrix.postScale(scale, scale, mid.x, mid.y);   
                            }   
                    }   
                    break;   
            }   

            view.setImageMatrix(matrix);   
            return true; // indicate event was handled   
    }   

    private void dumpEvent(MotionEvent event) {   
            String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",   
                            "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };   
            StringBuilder sb = new StringBuilder();   
            int action = event.getAction();   
            int actionCode = action & MotionEvent.ACTION_MASK;   
            sb.append("event ACTION_").append(names[actionCode]);   
            if (actionCode == MotionEvent.ACTION_POINTER_DOWN   
                            || actionCode == MotionEvent.ACTION_POINTER_UP) {   
                    sb.append("(pid ").append(   
                                    action >> MotionEvent.ACTION_POINTER_ID_SHIFT);   
                    sb.append(")");   
            }   
            sb.append("[");   
            for (int i = 0; i < event.getPointerCount(); i++) {   
                    sb.append("#").append(i);   
                    sb.append("(pid ").append(event.getPointerId(i));   
                    sb.append(")=").append((int) event.getX(i));   
                    sb.append(",").append((int) event.getY(i));   
                    if (i + 1 < event.getPointerCount())   
                            sb.append(";");   
            }   
            sb.append("]");   
            //Log.d(TAG, sb.toString());   
    }   


    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);   
    }   


    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);   
    }   
}