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

來源:互聯網
上載者:User

標籤:多點觸控   手勢縮放圖片   

本文是自己學習所做筆記,歡迎轉載,但請註明出處: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方法,那麼通過手勢(多點)來縮放圖片當然也不例外,區別就是通過手指的滑動來判斷縮放的比例及中心位置,具體做法如下:

手勢縮放圖片的步驟:
1. 設定ImageView的scaleType屬性為matrix。

         因為實現圖片的縮放要用到Matrix,所以這個屬性是前提,可以在xml裡設定ImageView設定,android:scaleType="matrix",或者在代碼裡設定imageView.setScaleType(ScaleType.matrix)

2. 給ImageView綁定觸摸監聽器      

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

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

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

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;@Overrideprotected 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() {@Overridepublic 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{@Overridepublic 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);

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

       

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.