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中简单实现页面翻转和自动翻转的功能