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