Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android項目 之 記事本(14) ----- 手勢縮放與拖拽圖片

android項目 之 記事本(14) ----- 手勢縮放與拖拽圖片

編輯:關於Android編程

本文是自己學習所做筆記,歡迎轉載,但請注明出處:http://blog.csdn.net/jesson20121020

上節實現了查看圖片及錄音的功能,其中查看圖片,可以調用系統的圖庫來查看圖片,也可以自定義Activity來查看圖片,今天就在上節的基礎上,實現手勢縮放與拖拽圖片。

想必大家都用過系統的圖庫,浏覽圖片時,可以通過手勢放大或縮小圖片,旋轉圖片,拖拽圖片等功能,我們也為自已定義的查看圖片的Activity增加手勢縮放與拖拽圖片的功能,效果如下圖:

\\\\

上面四幅圖中,演示了通過手勢(多點觸控)來縮小,放大,拖拽圖片。

這裡主要是用到了多點觸控,所以我們首先要知道多點和單點的區別。

單手指操作過程: ACTION_DOWN-ACTION_MOVE-ACTIOIN_UP

多手指操作過程:ACTION_DOWN-ACTION_POINTER_DOWN-ACTION_MOVE-ACTION_POINTER_UP-ACTION_UP

一般實現圖片的縮放都是用Matrix的postScale方法,那麼通過手勢(多點)來縮放圖片當然也不例外,區別就是通過手指的滑動來判斷縮放的比例及中心位置,具體做法如下:
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPGg0PsrWysbL9bfFzbzGrLXEsr3W6KO6PC9oND4KPGJsb2NrcXVvdGU+CjxoNT4xLiDJ6NbDSW1hZ2VWaWV3tcRzY2FsZVR5cGXK9NDUzqptYXRyaXihozwvaDU+CjwvYmxvY2txdW90ZT4KPHA+ICAgICAgICAg0vLOqsq1z9bNvMastcTL9bfF0qrTw7W9TWF0cml4o6zL+dLU1eK49sr00NTKx8ewzOGjrL/J0tTU2nhtbMDvyejWw0ltYWdlVmlld8no1sOjrGFuZHJvaWQ6c2NhbGVUeXBlPQ=="matrix",或者在代碼裡設置imageView.setScaleType(ScaleType.matrix)

2. 給ImageView綁定觸摸監聽器

 //觸摸事件
img.setOnTouchListener(new TouchEvent());

3. 在觸摸事件中實現多手指縮放及拖拽圖片

這是本節的核心,主要是要先判斷MotionEvent的類型,是單手指還是多手指,可以通過event.getActionMasked()來獲得,並設立三個標志,分別用於判斷當前操作是拖拽,縮放還是無操作,如果是拖拽,則需要記錄手指的起始位置及終點位置,然後利用Matrix的postTranslate方法來實現圖片的移動。如果是縮放,則需要先計算圖片縮放的比例及位置,在計算縮放比例時,又需要先知道多手指移動的直徑,通過多手指移動前後的比例來得到縮放的比例;而要計算圖片縮放的位置,只需要計算出手指移動前後的中點即可。

4. 控制縮放比例

其實,完成前3步就已經能實現通過手勢來控制圖片的縮放和移動,但是你會發現,這時,圖片可以放大的無限大,也可以縮小到無限小,而且,不管圖片是在放大狀態,還是縮小狀態,都會隨你的手指的移動而移動到任何地方。這顯然是不符合實際使用的,所以這就需要控制圖片的縮放的比例,主要代碼如下:

        //控制縮放比例
	private void controlScale(){
		float values[] = new float[9];
		matrix.getValues(values);
		if(mode == ZOOM){
			if(values[0] < MINSCALER)
				matrix.setScale(MINSCALER, MINSCALER);
			else if(values[0] > MAXSCALER)
				matrix.setScale(MAXSCALER, MAXSCALER);
		}
	}

5. 設置圖片居中顯示

通過第4步,可以控制圖片的縮放比例,這樣,圖片就會有一個最大的和最小的綻放比例,當計算出的縮放比例小於最小的縮放比例時,就會設置當前的縮放比例為最小的縮放比例,當大於最大的縮放比例時,就設置當前圖片的縮放比例為最大的縮放比例。

但是,還有一個問題,就是,這時不管圖片的放大還是縮小狀態,都會隨著你的手指移動,但在實際過程中,我們往往需要圖片的縮放後都是在控件的中心位置,即,設置圖片居中顯示,代碼如下:

    //自動居中  左右及上下都居中  
    protected void center()  
    {  
        center(true,true);  
    }  
  
    private void center(boolean horizontal, boolean vertical)  
    {  
        Matrix m = new Matrix();  
        m.set(matrix);  
        RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());  
        m.mapRect(rect);  
        float height = rect.height();  
        float width = rect.width();  
        float deltaX = 0, deltaY = 0;  
        if (vertical)  
        {  
            int screenHeight = dm.heightPixels;  //手機屏幕分辨率的高度  
            //int screenHeight = 400;  
            if (height < screenHeight)  
            {  
                deltaY = (screenHeight - height)/2 - rect.top;  
            }else if (rect.top > 0)  
            {  
                deltaY = -rect.top;  
            }else if (rect.bottom < screenHeight)  
            {  
                deltaY = screenHeight - rect.bottom;  
            }  
        }  
          
        if (horizontal)  
        {  
            int screenWidth = dm.widthPixels;  //手機屏幕分辨率的寬度  
            //int screenWidth = 400;  
            if (width < screenWidth)  
            {  
                deltaX = (screenWidth - width)/2 - rect.left;  
            }else if (rect.left > 0)  
            {  
                deltaX = -rect.left;      
            }else if (rect.right < screenWidth)  
            {  
                deltaX = screenWidth - rect.right;  
            }  
        }  
        matrix.postTranslate(deltaX, deltaY);  
    }  
通過居中設置後,這樣圖片在未占滿屏幕時,是不能進行將其移動到其他位置的,只有在圖片大於屏幕時,也可以移動圖片從而查看圖片的不同位置。

基本上,通過這5步,就已經可以實現圖片手勢(多點)縮放的功能,而且也可以控制圖片的縮放比例及居中顯示。下面給出整個代碼:

public class ShowPicture extends Activity {
	private ImageView img;
	private Bitmap bm;
	private DisplayMetrics dm;  
	private Matrix matrix = new Matrix();
	private Matrix savedMatrix = new Matrix();
	private PointF mid = new PointF();
	private PointF start = new PointF();
	private static int DRAG = 2;
	private static int ZOOM = 1;
	private static int NONE = 0;
	private int mode = 0;
	private float oldDist = 1f;
	private static float MINSCALER = 0.3f;
	private static float MAXSCALER = 3.0f;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
		setContentView(R.layout.activity_show_picture);
		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_add);
		//設置標題
		TextView tv_title = (TextView)findViewById(R.id.tv_title);
		tv_title.setText("查看圖片");
		Button bt_back = (Button)findViewById(R.id.bt_back);
		bt_back.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				ShowPicture.this.finish();
			}
		});
		Button bt_del = (Button)findViewById(R.id.bt_save);
		bt_del.setBackgroundResource(R.drawable.paint_icon_delete);
		
		dm = new DisplayMetrics();  
        getWindowManager().getDefaultDisplay().getMetrics(dm); //獲取分辨率  
		
		
		
		img = (ImageView)findViewById(R.id.iv_showPic);
		
		Intent intent = this.getIntent();
		String imgPath = intent.getStringExtra("imgPath");
		bm = BitmapFactory.decodeFile(imgPath);
		//設置居中顯示
		savedMatrix.setTranslate((dm.widthPixels - bm.getWidth())/2 , (dm.heightPixels - bm.getHeight()) / 2);
		img.setImageMatrix(savedMatrix);
		//綁定圖片
		img.setImageBitmap(bm);
		//觸摸事件
		img.setOnTouchListener(new TouchEvent());
	}
	
	//添加觸摸事件,實現圖片的手勢縮放
	class TouchEvent implements OnTouchListener{
		@Override
		public boolean onTouch(View view, MotionEvent event) {
			switch(event.getActionMasked()){
				//單擊觸控,用於拖動
			   	case MotionEvent.ACTION_DOWN :
			   		matrix.set(img.getImageMatrix());
			   		savedMatrix.set(matrix);
			   		start.set(event.getX(), event.getY());
			   		mode = DRAG;
			   		break;
				//多點觸控,按下時
				case MotionEvent.ACTION_POINTER_DOWN :
					oldDist = getSpacing(event);
					savedMatrix.set(matrix);
					getMidPoint(mid,event);
					mode = ZOOM;
					break;
				//多點觸控,抬起時
				case MotionEvent.ACTION_POINTER_UP :
					mode = NONE;
					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){
						//取得多指移動的直徑,如果大於10,則認為是縮放手勢
						float newDist = getSpacing(event);
						if(newDist > 10){
							matrix.set(savedMatrix);
							float scale = newDist / oldDist;
							
							matrix.postScale(scale, scale,mid.x,mid.y);
						}
					}
					break;
			}
			img.setImageMatrix(matrix);
			controlScale();
			//setCenter();
			center();
			return true;
		}
	}
	
	//求距離
	private float getSpacing(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 getMidPoint(PointF mid,MotionEvent event){
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		mid.set(x / 2, y / 2);
	}
	//控制縮放比例
	private void controlScale(){
		float values[] = new float[9];
		matrix.getValues(values);
		if(mode == ZOOM){
			if(values[0] < MINSCALER)
				matrix.setScale(MINSCALER, MINSCALER);
			else if(values[0] > MAXSCALER)
				matrix.setScale(MAXSCALER, MAXSCALER);
		}
	}
	//自動居中  左右及上下都居中  
    protected void center()  
    {  
        center(true,true);  
    }  
  
    private void center(boolean horizontal, boolean vertical)  
    {  
        Matrix m = new Matrix();  
        m.set(matrix);  
        RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());  
        m.mapRect(rect);  
        float height = rect.height();  
        float width = rect.width();  
        float deltaX = 0, deltaY = 0;  
        if (vertical)  
        {  
            int screenHeight = dm.heightPixels;  //手機屏幕分辨率的高度  
            //int screenHeight = 400;  
            if (height < screenHeight)  
            {  
                deltaY = (screenHeight - height)/2 - rect.top;  
            }else if (rect.top > 0)  
            {  
                deltaY = -rect.top;  
            }else if (rect.bottom < screenHeight)  
            {  
                deltaY = screenHeight - rect.bottom;  
            }  
        }  
          
        if (horizontal)  
        {  
            int screenWidth = dm.widthPixels;  //手機屏幕分辨率的寬度  
            //int screenWidth = 400;  
            if (width < screenWidth)  
            {  
                deltaX = (screenWidth - width)/2 - rect.left;  
            }else if (rect.left > 0)  
            {  
                deltaX = -rect.left;      
            }else if (rect.right < screenWidth)  
            {  
                deltaX = screenWidth - rect.right;  
            }  
        }  
        matrix.postTranslate(deltaX, deltaY);  
    }  
}

注:

另外還有一個問題就是,圖片的初始居中問題,在ImageView中可以設置屬性android:scaleType="fitCenter"來實現圖片的居中顯示,但是這裡是要實現手勢縮放圖片,所以需要將該屬性設置為android:scaleType="matrix",但同時,這也帶來了一個問題,就是在初始時,圖片不能居中。

在網上找過解決辦法,第一種方法,就是在XML文件裡先設置ImageVIew的scaleType屬性為fitCenter,然後在ImageVIew的setOnTouchListener()方法之前設置setScaleType(ScaleType.MATRIX); 但是這種方法我沒有成功,圖片初始還是在左上角;另一種方法,就是先計算手機屏幕的高和寬,求出使圖片居中的點(x,y),然後通過Matrix的平移到該位置,最後通過setImageMatrix()為ImageView綁定該Matrix,以實現圖片初始居中顯示。而我最後采用的也就是第二種方法,代碼如下:

private DisplayMetrics dm;  

... ... 

dm = new DisplayMetrics();  
getWindowManager().getDefaultDisplay().getMetrics(dm); //獲取分辨率  

... ...

//設置居中顯示
savedMatrix.setTranslate((dm.widthPixels - bm.getWidth())/2 , (dm.heightPixels - bm.getHeight()) / 2);
img.setImageMatrix(savedMatrix);

如果大家有更好的辦法,能使圖片初始時居中,請分享下。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved