1.1 双指旋转


1.2 单指拖拽


1.3 双指推压、双指拉取


1.4 双指缩放


2.准备工作

也可直接参考项目代码
文章示例代码

2.1 step1:在项目级build.gradle中添加jitpack仓库

//step-1.1:添加jitpack仓库
maven { url 'https://jitpack.io' }

两个repositories闭包均添加,如:

buildscript {
    repositories {
    ...
    //step-1.1:添加jitpack仓库
    maven { url 'https://jitpack.io' }
    }
    ...
}

allprojects {
    repositories {
        google()
        jcenter()
        //step-1.2:添加jitpack仓库
        maven { url 'https://jitpack.io' }
    }
}

2.2 step2:moudule的build.gradle导包

//step-2:导包
implementation 'com.github.Almeros:android-gesture-detectors:v1.0.1'

2.3 step3:布局代码

res/layout/activity_rotate.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="matrix"
        android:src="@mipmap/ic_football"
        />
</RelativeLayout>

3.旋转手势

用法&思路:

  1. 声明RotateGestureDetector对象,初始化,设置监听器等;
  2. 获取操作对象如ImageView对象,设置setOnTouchListener;
  3. 在OnTouchListener的onTouch回调中,RotateGestureDetector实例对象接手触摸事件;
  4. 旋转回调中,设置变化后的参数值;
  5. ontouch()回调中处理变化结果,并设置给操作对象如ImageView

代码及要点说明:

package com.cupster.easydetector;

import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

import com.almeros.android.multitouch.MoveGestureDetector;
import com.almeros.android.multitouch.RotateGestureDetector;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public  class RotateActivity extends AppCompatActivity implements View.OnTouchListener {

    private float mFocusX = 0.0f;
    private float mFocusY = 0.0f;

    private int mImageHeight, mImageWidth;

    private Matrix mMatrix = new Matrix();
    private float mRotationDegrees = 0.0f;//旋转角度
    private float mScaleFactor = 0.5f;//初始缩放系数

    private RotateGestureDetector mRotateDetector;
    private MoveGestureDetector mMoveDetector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rotate);
        ImageView img = (ImageView) findViewById(R.id.image);
        img.setOnTouchListener(this);

        mFocusX = getResources().getDisplayMetrics().widthPixels/2f;
        mFocusY = getResources().getDisplayMetrics().heightPixels/2f;

        mImageWidth = img.getDrawable().getIntrinsicWidth();
        mImageHeight = img.getDrawable().getIntrinsicHeight();

        float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
        float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;

        //初始缩放、位置居中
        mMatrix.postScale(mScaleFactor, mScaleFactor);
        mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
        img.setImageMatrix(mMatrix);

        mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateGestureDetector.SimpleOnRotateGestureListener(){
            @Override
            public boolean onRotate(RotateGestureDetector detector) {
                mRotationDegrees -= detector.getRotationDegreesDelta();
                return true;
            }
        });
        mMoveDetector 	= new MoveGestureDetector(getApplicationContext(), new MoveGestureDetector.SimpleOnMoveGestureListener(){
            @Override
            public boolean onMove(MoveGestureDetector detector) {
                PointF d = detector.getFocusDelta();
                mFocusX += d.x;
                mFocusY += d.y;
                return true;
            }
        });
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        mRotateDetector.onTouchEvent(motionEvent);
        mMoveDetector.onTouchEvent(motionEvent);

        float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
        float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;

        mMatrix.reset();
        mMatrix.postRotate(mRotationDegrees,  scaledImageCenterX, scaledImageCenterY);
        mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);

        ( (ImageView) view).setImageMatrix(mMatrix);
        return true;//true:消费掉事件,不继续分发
    }
}

4.拖拽手势

用法&思路:

  1. 声明MoveGestureDetector对象,初始化,设置监听器等;
  2. 获取操作对象如ImageView对象,设置setOnTouchListener;
  3. 在OnTouchListener的onTouch回调中,MoveGestureDetector实例对象接手触摸事件;
  4. 旋转回调中,设置变化后的参数值;
  5. ontouch()回调中处理变化结果,并设置给操作对象如ImageView

代码及要点说明:

package com.cupster.easydetector;

import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

import com.almeros.android.multitouch.MoveGestureDetector;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public  class MoveActivity extends AppCompatActivity implements View.OnTouchListener {

    private float mFocusX = 0.0f;
    private float mFocusY = 0.0f;

    private int mImageHeight, mImageWidth;

    private Matrix mMatrix = new Matrix();

    private MoveGestureDetector mMoveDetector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rotate);
        ImageView img = (ImageView) findViewById(R.id.image);
        img.setOnTouchListener(this);

        mFocusX = getResources().getDisplayMetrics().widthPixels/2f;
        mFocusY = getResources().getDisplayMetrics().heightPixels/2f;

        mImageWidth = img.getDrawable().getIntrinsicWidth();
        mImageHeight = img.getDrawable().getIntrinsicHeight();

        //初始位置居中
        mMatrix.postTranslate(mFocusX - mImageWidth/2f, mFocusY - mImageHeight/2f);
        img.setImageMatrix(mMatrix);

        mMoveDetector 	= new MoveGestureDetector(getApplicationContext(), new MoveGestureDetector.SimpleOnMoveGestureListener(){
            @Override
            public boolean onMove(MoveGestureDetector detector) {
                PointF d = detector.getFocusDelta();
                mFocusX += d.x;
                mFocusY += d.y;
                return true;
            }
        });
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        mMoveDetector.onTouchEvent(motionEvent);

        mMatrix.reset();
        mMatrix.postTranslate(mFocusX - mImageWidth/2f, mFocusY - mImageHeight/2f);

        ( (ImageView) view).setImageMatrix(mMatrix);
        return true;//true:消费掉事件,不继续分发
    }
}

5.双指推拉手势

用法&思路:

  1. 声明ShoveGestureDetector对象,初始化,设置监听器等;
  2. 获取操作对象如ImageView对象,设置setOnTouchListener;
  3. 在OnTouchListener的onTouch回调中,ShoveGestureDetector实例对象接手触摸事件;
  4. 旋转回调中,设置变化后的参数值;
  5. ontouch()回调中处理变化结果,并设置给操作对象如ImageView

代码及要点说明:

package com.cupster.easydetector;

import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

import com.almeros.android.multitouch.MoveGestureDetector;
import com.almeros.android.multitouch.ShoveGestureDetector;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public  class PressPushActivity extends AppCompatActivity implements View.OnTouchListener {

    private float mFocusX = 0.0f;
    private float mFocusY = 0.0f;

    private int mImageHeight, mImageWidth;

    private int mAlpha = 255;
    private Matrix mMatrix = new Matrix();
    private MoveGestureDetector mMoveDetector;
    private ShoveGestureDetector mShoveDetector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rotate);
        ImageView img = (ImageView) findViewById(R.id.image);
        img.setOnTouchListener(this);

        mFocusX = getResources().getDisplayMetrics().widthPixels/2f;
        mFocusY = getResources().getDisplayMetrics().heightPixels/2f;

        mImageWidth = img.getDrawable().getIntrinsicWidth();
        mImageHeight = img.getDrawable().getIntrinsicHeight();

        //初始缩放、位置居中
        mMatrix.postTranslate(mFocusX - mImageWidth/2f, mFocusY - mImageHeight/2f);
        img.setImageMatrix(mMatrix);

        mMoveDetector 	= new MoveGestureDetector(getApplicationContext(), new MoveGestureDetector.SimpleOnMoveGestureListener(){
            @Override
            public boolean onMove(MoveGestureDetector detector) {
                PointF d = detector.getFocusDelta();
                mFocusX += d.x;
                mFocusY += d.y;
                return true;
            }
        });
        mShoveDetector 	= new ShoveGestureDetector(getApplicationContext(), new ShoveGestureDetector.SimpleOnShoveGestureListener(){
            @Override
            public boolean onShove(ShoveGestureDetector detector) {
                mAlpha += detector.getShovePixelsDelta();
                if (mAlpha > 255){
                    mAlpha = 255;
                } else if (mAlpha < 0){
                    mAlpha = 0;
                }
                return true;
            }
        });
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        mMoveDetector.onTouchEvent(motionEvent);
        mShoveDetector.onTouchEvent(motionEvent);

        mMatrix.reset();
        mMatrix.postTranslate(mFocusX - mImageWidth/2f, mFocusY - mImageHeight/2f);

        ( (ImageView) view).setImageMatrix(mMatrix);
        ( (ImageView) view).getDrawable().setAlpha(mAlpha);
        return true;//true:消费掉事件,不继续分发
    }
}

5.缩放手势

用法&思路:

  1. 声明ScaleGestureDetector对象,初始化,设置监听器等;
  2. 获取操作对象如ImageView对象,设置setOnTouchListener;
  3. 在OnTouchListener的onTouch回调中,ScaleGestureDetector实例对象接手触摸事件;
  4. 旋转回调中,设置变化后的参数值;
  5. ontouch()回调中处理变化结果,并设置给操作对象如ImageView

代码及要点说明:

package com.cupster.easydetector;

import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

import com.almeros.android.multitouch.MoveGestureDetector;
import com.almeros.android.multitouch.RotateGestureDetector;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public  class ScaleActivity extends AppCompatActivity implements View.OnTouchListener {

    private float mFocusX = 0.0f;
    private float mFocusY = 0.0f;

    private int mImageHeight, mImageWidth;

    private Matrix mMatrix = new Matrix();
    private float mScaleFactor = 0.5f;//初始缩放系数
    private float mMinScale = 0.2f;//min缩放系数
    private float mMaxScale = 10.0f;//max缩放系数

    private ScaleGestureDetector mScaleDetector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rotate);
        ImageView img = (ImageView) findViewById(R.id.image);
        img.setOnTouchListener(this);

        mFocusX = getResources().getDisplayMetrics().widthPixels/2f;
        mFocusY = getResources().getDisplayMetrics().heightPixels/2f;

        mImageWidth = img.getDrawable().getIntrinsicWidth();
        mImageHeight = img.getDrawable().getIntrinsicHeight();

        float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
        float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;

        //初始缩放、位置居中
        mMatrix.postScale(mScaleFactor, mScaleFactor);
        mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
        img.setImageMatrix(mMatrix);

        mScaleDetector 	= new ScaleGestureDetector(getApplicationContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener(){
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                mScaleFactor *= detector.getScaleFactor();
                mScaleFactor = Math.max(mMinScale, Math.min(mScaleFactor, mMaxScale));
                return true;
            }
        });
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        mScaleDetector.onTouchEvent(motionEvent);

        float scaledImageCenterX = (mImageWidth*mScaleFactor)/2;
        float scaledImageCenterY = (mImageHeight*mScaleFactor)/2;

        mMatrix.reset();
        mMatrix.postScale(mScaleFactor, mScaleFactor);
        mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);

        ( (ImageView) view).setImageMatrix(mMatrix);
        return true;//true:消费掉事件,不继续分发
    }
}

6. 其他次要代码

代码.zip

activity_main.xml

<?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:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_rotate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="0dp"
        android:text="旋转"
        android:textAllCaps="false" />
    <Button
        android:id="@+id/btn_pull"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="0dp"
        android:layout_marginTop="10dp"
        android:text="拖拽"
        android:textAllCaps="false" />
    <Button
        android:id="@+id/btn_press_push"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="0dp"
        android:layout_marginTop="10dp"
        android:text="推压"
        android:textAllCaps="false" />
    <Button
        android:id="@+id/btn_scale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="0dp"
        android:layout_marginTop="10dp"
        android:text="缩放"
        android:textAllCaps="false" />

</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cupster.easydetector">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <activity android:name=".RotateActivity" android:screenOrientation="portrait"/>
        <activity android:name=".MoveActivity" android:screenOrientation="portrait"/>
        <activity android:name=".PressPushActivity" android:screenOrientation="portrait"/>
        <activity android:name=".ScaleActivity" android:screenOrientation="portrait"/>
    </application>

</manifest>