第三部分 應用
在這一部分,我們會將前面兩部分所瞭解到的內容和Android手勢結合起來,利用各種不同的手勢對圖像進行平移、縮放和旋轉,前面兩項都是在實踐中經常需要用到的功能,後一項據說蘋果也是最近才加上的,而實際上在Android中,咱們通過自己的雙手,也可以很輕鬆地實現之。
首先創建一個Android項目PatImageView,同時創建一個Activity:PatImageViewActivity。完成這一步後, 記得在AndroidManifest.xml中增加如下許可:
<uses-permissionandroid:name="android.permission.VIBRATE"/>
因爲我們將要通過短按還是長按,來確定將圖片到底是縮放還是旋轉。
現在來創建一個ImageView的派生類:PatImageView,其代碼(PatImageView.java)如下(2011-11-22 revised):
package com.pat.imageview;
import android.app.Service;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Vibrator;
import android.util.FloatMath;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
public class PatImageView extends ImageView
{
private Matrix matrix;
private Matrix savedMatrix;
private boolean long_touch = false;
private static int NONE = 0;
private static int DRAG = 1; // 拖動
private static int ZOOM = 2; // 縮放
private static int ROTA = 3; // 旋轉
private int mode = NONE;
private PointF startPoint;
private PointF middlePoint;
private float oldDistance;
private float oldAngle;
private Vibrator vibrator;
private GestureDetector gdetector;
public PatImageView(final Context context)
{
super(context);
matrix = new Matrix();
savedMatrix = new Matrix();
matrix.setTranslate(0f, 0f);
setScaleType(ScaleType.MATRIX);
setImageMatrix(matrix);
startPoint = new PointF();
middlePoint = new PointF();
oldDistance = 1f;
gdetector = new GestureDetector(context, new GestureDetector.OnGestureListener()
{
@Override
public boolean onSingleTapUp(MotionEvent e)
{
return true;
}
@Override
public void onShowPress(MotionEvent e)
{
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
return true;
}
@Override
public void onLongPress(MotionEvent e)
{
long_touch = true;
vibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
// 振動50ms,提示後續的操作將是旋轉圖片,而非縮放圖片
vibrator.vibrate(50);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
return true;
}
@Override
public boolean onDown(MotionEvent e)
{
return true;
}
});
setOnTouchListener(new OnTouchListener()
{
public boolean onTouch(View view, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN: // 第一個手指touch
savedMatrix.set(matrix);
startPoint.set(event.getX(), event.getY());
mode = DRAG;
long_touch = false;
break;
case MotionEvent.ACTION_POINTER_DOWN: // 第二個手指touch
oldDistance = getDistance(event); // 計算第二個手指touch時,兩指之間的距離
oldAngle = getDegree(event); // 計算第二個手指touch時,兩指所形成的直線和x軸的角度
if(oldDistance > 10f)
{
savedMatrix.set(matrix);
middlePoint = midPoint(event);
if(!long_touch)
{
mode = ZOOM;
}
else
{
mode = ROTA;
}
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(vibrator != null) vibrator.cancel();
if(mode == DRAG)
{
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
}
if(mode == ZOOM)
{
float newDistance = getDistance(event);
if(newDistance > 10f)
{
matrix.set(savedMatrix);
float scale = newDistance / oldDistance;
matrix.postScale(scale, scale, middlePoint.x, middlePoint.y);
}
}
if(mode == ROTA)
{
float newAngle = getDegree(event);
matrix.set(savedMatrix);
float degrees = newAngle - oldAngle;
matrix.postRotate(degrees, middlePoint.x, middlePoint.y);
}
break;
}
setImageMatrix(matrix);
invalidate();
gdetector.onTouchEvent(event);
return true;
}
});
}
// 計算兩個手指之間的距離
private float getDistance(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);
}
// 計算兩個手指所形成的直線和x軸的角度
private float getDegree(MotionEvent event)
{
return (float)(Math.atan((event.getY(1) - event.getY(0)) / (event.getX(1) - event.getX(0))) * 180f);
}
// 計算兩個手指之間,中間點的座標
private PointF midPoint( MotionEvent event)
{
PointF point = new PointF();
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
return point;
}
}
下面完善PatImageViewActivity.java的代碼,使之如下:
package com.pat.imageview;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class PatImageViewActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
PatImageView piv = new PatImageView(this);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
piv.setImageBitmap(bmp);
setContentView(piv);
}
}
由於有些手勢在模擬器上無法模擬,所以就不上運行結果的圖片了。本人在真機上運行後(照片就不拍了,有點累啦),可以輕鬆做到:
1. 很方便地拖動圖片(比如,單指按住屏幕進行拖動)
2. 很方便地縮放圖片(比如,雙指按住屏幕進行分開或者併攏操作,可分別實現放大或者縮小圖片的功能)
3. 長按出現振動後,可以很方便地旋轉圖片(一個手指固定,另外一個手指圍繞那個固定的手指運動)。