Android中圖像變換Matrix的原理、代碼驗證和應用(三)

第三部分 應用

在這一部分,我們會將前面兩部分所瞭解到的內容和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.     長按出現振動後,可以很方便地旋轉圖片(一個手指固定,另外一個手指圍繞那個固定的手指運動)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章