Android中實現雙指縮放的功能
安卓中實現將圖片縮放的功能提供了一個很好的類:ScaleGestureDetector,本章就介紹使用canvas和ScaleGestureDetector類實現縮放的功能,如果要想詳細瞭解ScaleGestureDetector請參考博文Android的ScaleGestureDetector縮放類詳解
1. 先初始化縮放比和圖片居中繪製的座標點
float posX, posY; //圖片的座標
int viewWidth, viewHeight; //屏幕的寬高
float widthScale, heightScale; //寬高縮放比
boolean hasInitViewSize; //是否已經初始化視圖
public void initSize() {
viewWidth = getWidth(); //得到屏幕寬度
viewHeight = getHeight(); //得到屏幕高度
if (viewWidth < 0 && viewHeight < 0) {
return;
}
hasInitViewSize = true;
widthScale = viewWidth / imgWidth; //寬高縮放比=屏幕的寬高/屏幕的寬高
heightScale = viewHeight / imgHeight;
scaleFactor = Math.min(widthScale, heightScale); //總縮放比取寬高縮放比中最小的
posX = viewWidth / 2 - imgWidth / 2; //使圖片居中繪製
posY = viewHeight / 2 - imgHeight / 2;
}
2. 創建兩個內部類分別繼承SimpleOnScaleGestureListener和SimpleOnGestureListener來動態獲取縮放比和座標
class MySimpleOnGestureDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
posX -= distanceX; //X軸的座標=X軸的座標-在X軸方向的移動距離
posY -= distanceY; //y軸的座標=y軸的座標-在y軸方向的移動距離
invalidate(); //刷新view
return true;
}
}
class MySimpleScaleOnGestureDetector extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor(); //縮放比=縮放比*動態獲取的縮放比
scaleFactor = scaleFactor < 0.75 ? (float) 0.75 : scaleFactor > 3 ? 3 : scaleFactor; //控制縮放倍率在0.75-3之間
invalidate(); //刷新view
return true;
}
}
3. 爲了移動圖片時不超出屏幕,還得進行控制,原則是:圖片較小時任意一條邊都不能出了邊界,圖片較大任意一條邊都不能進入邊界。寬度和高度分別獨立計算。
public void checkBounds() {
if (scaleFactor > widthScale) { //寬度方向已經填滿
posX = Math.min(posX, (scaleFactor - 1) * (imgWidth / 2));
posX = Math.max(posX, viewWidth - imgWidth - (scaleFactor - 1) * (imgWidth / 2));
} else {
posX = Math.max(posX, (scaleFactor - 1) * (imgWidth / 2));
posX = Math.min(posX, viewWidth - imgWidth - (scaleFactor - 1) * (imgWidth / 2));
}
if (scaleFactor > heightScale) { //高度方向已經填滿
posY = Math.min(posY, (scaleFactor - 1) * (imgHeight / 2));
posY = Math.max(posY, viewHeight - imgHeight - (scaleFactor - 1) * (imgHeight / 2));
} else {
posY = Math.max(posY, (scaleFactor - 1) * (imgHeight / 2));
posY = Math.min(posY, viewHeight - imgHeight - (scaleFactor - 1) * (imgHeight / 2));
}
}
4. 在類中實現onDraw方法進行繪製縮放
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap == null) {
return;
}
if (!hasInitViewSize) {
initSize();
}
canvas.save(); //畫布保存
checkBounds(); //檢查邊界,使圖片不能超出屏幕
canvas.scale(scaleFactor, scaleFactor, posX + imgWidth / 2, posY + imgHeight / 2); ///以圖片的中心爲基點進行縮放
canvas.drawBitmap(bitmap, posX, posY, paint); //繪製圖片
canvas.restore(); //畫布重繪
}
5. 關鍵點已經差不多寫完了,現在將完整的類寫上
package com.example.mygesture;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
public class MyScaleView extends View {
ScaleGestureDetector scaleGestureDetector;
float scaleFactor;
float posX, posY;
int viewWidth, viewHeight;
float widthScale, heightScale;
boolean hasInitViewSize;
Paint paint = new Paint();
Bitmap bitmap;
float imgWidth, imgHeight;
GestureDetector gestureDetector;
public MyScaleView(Context context, @Nullable AttributeSet attrs) { //注意:得在有兩個參數的構造函數中實例化ScaleGestureDetector 和GestureDetector
super(context, attrs);
init(context);
}
private void init(Context context) {
scaleGestureDetector = new ScaleGestureDetector(context, new MySimpleScaleOnGestureDetector());
gestureDetector = new GestureDetector(context, new MySimpleOnGestureDetector());
}
class MySimpleOnGestureDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
posX -= distanceX;
posY -= distanceY;
invalidate();
return true;
}
}
class MySimpleScaleOnGestureDetector extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = scaleFactor < 0.75 ? (float) 0.75 : scaleFactor > 3 ? 3 : scaleFactor;
invalidate();
return true;
}
}
public void initSize() {
viewWidth = getWidth();
viewHeight = getHeight();
if (viewWidth < 0 && viewHeight < 0) {
return;
}
hasInitViewSize = true;
widthScale = viewWidth / imgWidth;
heightScale = viewHeight / imgHeight;
scaleFactor = Math.min(widthScale, heightScale);
posX = viewWidth / 2 - imgWidth / 2;
posY = viewHeight / 2 - imgHeight / 2;
}
public void checkBounds() { //檢查邊界
if (scaleFactor > widthScale) {
posX = Math.min(posX, (scaleFactor - 1) * (imgWidth / 2));
posX = Math.max(posX, viewWidth - imgWidth - (scaleFactor - 1) * (imgWidth / 2));
} else {
posX = Math.max(posX, (scaleFactor - 1) * (imgWidth / 2));
posX = Math.min(posX, viewWidth - imgWidth - (scaleFactor - 1) * (imgWidth / 2));
}
if (scaleFactor > heightScale) {
posY = Math.min(posY, (scaleFactor - 1) * (imgHeight / 2));
posY = Math.max(posY, viewHeight - imgHeight - (scaleFactor - 1) * (imgHeight / 2));
} else {
posY = Math.max(posY, (scaleFactor - 1) * (imgHeight / 2));
posY = Math.min(posY, viewHeight - imgHeight - (scaleFactor - 1) * (imgHeight / 2));
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
scaleGestureDetector.onTouchEvent(event); //雙指縮放
gestureDetector.onTouchEvent(event); //單指移動
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap == null) {
return;
}
if (!hasInitViewSize) {
initSize();
}
canvas.save();
checkBounds();
canvas.scale(scaleFactor, scaleFactor, posX + imgWidth / 2, posY + imgHeight / 2);
canvas.drawBitmap(bitmap, posX, posY, paint);
canvas.restore();
}
public void setImageResouse(int resID) { //設置圖片
bitmap = BitmapFactory.decodeResource(getResources(), resID);
imgWidth = bitmap.getWidth();
imgHeight = bitmap.getHeight();
initSize();
invalidate();
}
}
6. 類已經寫好了,現在對其使用,創建一個新的Activity
6.1 構造佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".text.Main8Activity">
<com.example.mygesture.MyScaleView
android:id="@+id/activity_main8_myScaleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
6.2 對應的類中
package com.example.mygesture.text;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.example.mygesture.MyScaleView;
import com.example.mygesture.R;
public class Main8Activity extends AppCompatActivity {
MyScaleView myScaleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main8);
myScaleView = findViewById(R.id.activity_main8_myScaleView);
myScaleView.setImageResouse(R.drawable.q4);
}
}
7. 所以使用還是很簡單的,因爲縮放操作模擬器上不好演示,效果圖就省略了。
Tip: 小白,寫得不好請見諒。若有不對的地方請留言。
關於手勢方面的功能點還可以參考Android中實現自定義手勢和識別手勢的功能,Android中簡單實現頁面翻轉和自動翻轉的功能