編輯:關於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);
如果大家有更好的辦法,能使圖片初始時居中,請分享下。
Fragment是Android honeycomb 3.0開始新增的概念,Fragment名為碎片不過卻和Activity十分相似,下面介紹下Androi
之前關於如何實現屏幕頁面切換,寫過一篇博文《Android中使用ViewFlipper實現屏幕切換》,相比ViewFlipper,ViewPager更適用復雜的視圖切換,
Android-自定義Dialog2014年4月27日 星期天 天氣晴朗 心情平靜 本篇博文來分享一個也是開發中經常需要用到的功能-自定義對話框,這裡我用到了Androi
// 表示事件是否攔截, 返回false表示不攔截 @Override public boolean onInterceptTouchEvent(Motion