忘記從哪個網站下載的程式碼, 我改了一下, 變成自己的版本。
如果要處理一個圖片的滑動跟縮放, 要怎麼寫呢?
一開始正常狀態
然後用兩根手指拉開或者按下放大鍵
兩根手指縮近或者按下縮小鍵
單一手指點圖片, 往左拉
會自動彈回最初的位置
一開始我們就布局好, 整個畫面只有一個ImageView, 以及兩個放大縮小的按鈕。
接下來把這些元件從Activity的類別拿出來。
再來就是最後一個元件, 也是我們的主角, ImageView加入事件,
一開始mode變數來存放目前是屬於什麼模式,
當手指按下去, 先讓模式變成拖曳(Drag),
而移動的時候, 假設模式是拖曳, 則我們讓matrix的x,y軸進行變化。
如果兩點觸控, 就會變成縮放的模式, 根據兩指的距離來判斷是放大還是縮小。
而上面有spacing()是計算兩點距離, midPoint()是測量兩點的中點。
最後在建構子裡面, 對一開始的圖片進行一些初始化的動作。
minZoom()是為了將圖片跟螢幕做比較好的調整。
最後回到MainActivity.java, 將ImageViewHelper.java所需要的參數傳進去,
就可以把一張圖進行一些操作了。
程式碼
http://uploadingit.com/file/naplkvdnpsrkmqry/DragImageDemo.zip
一開始正常狀態
然後用兩根手指拉開或者按下放大鍵
兩根手指縮近或者按下縮小鍵
單一手指點圖片, 往左拉
會自動彈回最初的位置
一開始我們就布局好, 整個畫面只有一個ImageView, 以及兩個放大縮小的按鈕。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_gravity="center"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_gravity="center"
android:id="@+id/image_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="matrix"
android:adjustViewBounds="true" />
<LinearLayout
android:layout_gravity="bottom|right|center"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:height="20.0dip">
<ImageButton
android:id="@+id/zoomInButton"
android:background="@null"
android:paddingRight="5.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/zoom_in"
android:text="In" />
<ImageButton
android:id="@+id/zoomOutButton"
android:background="@null"
android:paddingLeft="5.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/zoom_out"
android:text="Out" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
接下來把這些元件從Activity的類別拿出來。
public class MainActivity extends Activity {
private ImageView imageView;
private ImageButton zoomInButton;
private ImageButton zoomOutButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView)findViewById(R.id.image_view);
zoomInButton = (ImageButton)findViewById(R.id.zoomInButton);
zoomOutButton = (ImageButton)findViewById(R.id.zoomOutButton);
}
}
接著我們來新增一個類別叫做ImageViewHelper.java, 用來處理ImageView的移動跟縮放,
把Activity的三個元件由建構子傳入。
由於之後要操作圖片, 這時候需要判斷螢幕大小, 以及圖片的大小, 所以我們在建構子多加傳入兩個參數,分別是DisplayMetrics以及Bitmap的物件。
public ImageViewHelper(DisplayMetrics dm,ImageView imageView,Bitmap bitmap, ImageButton zoomInButton, ImageButton zoomOutButton){
this.dm = dm;
this.imageView = imageView;
this.zoomInButton = zoomInButton;
this.zoomOutButton = zoomOutButton;
this.bitmap = bitmap;
}
在操作一張圖片, 最簡單的方式就是利用Matrix這個類別所產生的物件來操作,
一開始我們就先寫放大跟縮小的方法。
public void setZoomIn(){
minScaleR = Math.min(
(float) dm.widthPixels / (float) bitmap.getWidth(),
(float) dm.heightPixels / (float) bitmap.getHeight());
if (minScaleR < 1.0) {
matrix.postScale(minScaleR+1f, minScaleR+1f);
}
else{
matrix.postScale(minScaleR, minScaleR);
}
}
public void setZoomOut(){
minScaleR = Math.max(
(float) dm.widthPixels / (float) bitmap.getWidth(),
(float) dm.heightPixels / (float) bitmap.getHeight());
if (minScaleR > 1.0) {
matrix.postScale((minScaleR-(int)minScaleR),(minScaleR-(int)minScaleR));
}
else{
matrix.postScale(minScaleR, minScaleR);
}
}
然後圖片如果超出螢幕範圍, 則自動跳回中心點。
//橫向、縱向置中
public void center(boolean horizontal, boolean vertical) {
Matrix m = new Matrix();
m.set(matrix);
RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
m.mapRect(rect);
float height = rect.height();
float width = rect.width();
float deltaX = 0, deltaY = 0;
if (vertical) {
// 圖片小於螢幕大小,則置中顯示。
//大於螢幕,上方則留空白則往上移,下方留空白則往下移
int screenHeight = dm.heightPixels;
if (height < screenHeight) {
deltaY = (screenHeight - height) / 2 - rect.top;
} else if (rect.top > 0) {
deltaY = -rect.top;
} else if (rect.bottom < screenHeight) {
deltaY = imageView.getHeight() - rect.bottom;
}
}
if (horizontal) {
int screenWidth = dm.widthPixels;
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);
}
接著將傳入的zoomin,zoomout的元件加入事件。
zoomInButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
setZoomIn();
center();
imageView.setImageMatrix(matrix);
}
});
zoomOutButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
setZoomOut();
center();
imageView.setImageMatrix(matrix);
}
});
你會發現center()這個方法從哪來的, 其實是呼叫center(true,true)的。
public void center(){
center(true, true);
}
再來就是最後一個元件, 也是我們的主角, ImageView加入事件,
一開始mode變數來存放目前是屬於什麼模式,
當手指按下去, 先讓模式變成拖曳(Drag),
而移動的時候, 假設模式是拖曳, 則我們讓matrix的x,y軸進行變化。
如果兩點觸控, 就會變成縮放的模式, 根據兩指的距離來判斷是放大還是縮小。
imageView.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View arg0, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
prev.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
dist = spacing(event);
// 如果兩點距離超過10, 就判斷為多點觸控模式 即為縮放模式
if (spacing(event) > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - prev.x, event.getY()
- prev.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);//偵測兩根手指移動的距離
if (newDist > 10f) {
matrix.set(savedMatrix);
float tScale = newDist / dist;
matrix.postScale(tScale, tScale, mid.x, mid.y);
}
}
break;
}
imageView.setImageMatrix(matrix);
center();
return true;
}
});
而上面有spacing()是計算兩點距離, midPoint()是測量兩點的中點。
//兩點的距離
public float spacing(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);
}
//兩點的中點
public void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
最後在建構子裡面, 對一開始的圖片進行一些初始化的動作。
public ImageViewHelper(DisplayMetrics dm,ImageView imageView,Bitmap bitmap, ImageButton zoomInButton, ImageButton zoomOutButton){
this.dm = dm;
this.imageView = imageView;
this.zoomInButton = zoomInButton;
this.zoomOutButton = zoomOutButton;
this.bitmap = bitmap;
setImageSize();
minZoom();
center();
imageView.setImageMatrix(matrix);
}
minZoom()是為了將圖片跟螢幕做比較好的調整。
//取得最小的比例, 假設圖片比螢幕大
//則螢幕(寬/長)/圖片(寬/長)會小於1 那麼也就是將圖片進行縮小
//反之 則進行放大 而圖片越小 放大倍數則會越大
//如果螢幕跟圖片大小相同 則倍數會為1 即不變
public void minZoom() {
minScaleR = Math.min(
(float) dm.widthPixels / (float) bitmap.getWidth(),
(float) dm.heightPixels / (float) bitmap.getHeight());
if (minScaleR <= 1.0) {
matrix.postScale(minScaleR, minScaleR);
}
else{
matrix.postScale(1.5f, 1.5f);
}
}
最後回到MainActivity.java, 將ImageViewHelper.java所需要的參數傳進去,
就可以把一張圖進行一些操作了。
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
imageView = (ImageView)findViewById(R.id.image_view);
zoomInButton = (ImageButton)findViewById(R.id.zoomInButton);
zoomOutButton = (ImageButton)findViewById(R.id.zoomOutButton);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cat);
imageView.setImageBitmap(bitmap);
new ImageViewHelper(dm,imageView,bitmap,zoomInButton,zoomOutButton);
}
程式碼
http://uploadingit.com/file/naplkvdnpsrkmqry/DragImageDemo.zip