Android手势识别GestureDetector和ScaleGestureDetector介绍与使用,以自定义一个可拖拽拉伸的ImageView为例

本文主要是介绍Android手势识别GestureDetector和ScaleGestureDetector介绍与使用,以自定义一个可拖拽拉伸的ImageView为例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、GestureDetector

1. 简介

GestureDetector主要用于检测单指手势,例如单击、长按、滑动等,不支持多指手势。

2. SimpleOnGestureListener 内部类

GestureDetector.SimpleOnGestureListener 是用于处理手势事件的辅助类,它包含了一系列回调方法,用于处理不同类型的单指手势事件。下面是对每个回调方法的简要介绍:

  • onDown(MotionEvent e): 当用户按下(Down)手指时触发。这个方法返回 true 表示事件被消费了,false 表示未被消费。

  • onShowPress(MotionEvent e): 当用户按下并保持按压一段时间时触发。它表示按下动作已被识别,但尚未发生其它任何行为。

  • onSingleTapUp(MotionEvent e): 当用户轻击屏幕时触发。这个方法返回 true 表示事件被消费了,false 表示未被消费。

  • onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY): 当用户在屏幕上滚动时触发。它提供了滚动开始和结束时的事件信息,以及在X和Y方向上的距离差。

  • onLongPress(MotionEvent e): 当用户长按屏幕时触发。

  • onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY): 当用户迅速滑动手指并松开时触发。它提供了滑动开始和结束时的事件信息,以及在X和Y方向上的速度。

  • onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY): 当用户在屏幕上滚动时触发。与第四个回调方法不同,这个方法在滚动过程中持续触发,而不仅仅是在滚动结束时触发。

  • onDoubleTap(MotionEvent e): 当用户双击屏幕时触发。

  • onDoubleTapEvent(MotionEvent e): 当双击事件包含按下、移动和抬起动作时触发。通常与 onDoubleTap() 结合使用,以处理更复杂的双击手势。

  • onSingleTapConfirmed(MotionEvent e): 当确认发生了单击事件时触发。与 onSingleTapUp() 不同的是,这个方法确保了事件是单击事件而不是双击事件。

这些回调方法提供了处理各种类型手势事件的灵活性,可以根据需求选择实现相应的方法来处理手势事件。

3. 示例

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;public class MyView extends View {private GestureDetector gestureDetector;public MyView(Context context, AttributeSet attrs) {super(context, attrs);// 实例化 GestureDetector,并传入 SimpleOnGestureListener 对象gestureDetector = new GestureDetector(context, new MyGestureListener());}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 将触摸事件传递给 GestureDetectorreturn gestureDetector.onTouchEvent(event) || super.onTouchEvent(event);}private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {@Overridepublic boolean onDown(MotionEvent e) {// 用户按下屏幕时触发return true; // 返回 true 表示事件被消费}@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {// 单击事件确认时触发return true;}@Overridepublic boolean onDoubleTap(MotionEvent e) {// 双击事件时触发return true;}@Overridepublic void onLongPress(MotionEvent e) {// 长按事件时触发}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {// 滚动事件时触发return true;}// 其他手势事件处理方法...}
}

二、ScaleGestureDetector

1. 简介

用于检测缩放手势,即双指捏合或者扩张的手势。它提供了 onScale() 和 onScaleBegin() 等回调方法来处理缩放手势的开始、进行中和结束时的事件。

2. SimpleOnGestureListener 内部类

ScaleGestureDetector.SimpleOnGestureListener 用于处理手势事件的辅助类,它包含了一系列回调方法,用于处理不同类型的双指手势事件。下面是对每个回调方法的简要介绍:

  • onScale(ScaleGestureDetector detector): 当缩放手势进行中时调用。这个方法会在缩放手势进行过程中持续调用,每次缩放都会触发。参数 detector 提供了有关缩放手势的信息,如当前的缩放因子等。

  • onScaleBegin(ScaleGestureDetector detector): 当缩放手势开始时调用。这个方法在缩放手势的第一次触发时调用,可以用来初始化缩放相关的状态。参数 detector 提供了有关缩放手势的信息。

  • onScaleEnd(ScaleGestureDetector detector): 当缩放手势结束时调用。这个方法在缩放手势结束后调用,可以用来清理缩放相关的状态。参数 detector 提供了有关缩放手势的信息。

3. 示例

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;public class MyScaleView extends View {private ScaleGestureDetector scaleGestureDetector;private float scaleFactor = 1.0f;public MyScaleView(Context context, AttributeSet attrs) {super(context, attrs);scaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener());}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 将触摸事件传递给 ScaleGestureDetectorscaleGestureDetector.onTouchEvent(event);return true;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 在画布上绘制内容,并根据 scaleFactor 进行缩放canvas.scale(scaleFactor, scaleFactor, getWidth() / 2f, getHeight() / 2f);// 绘制内容...}private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {@Overridepublic boolean onScale(ScaleGestureDetector detector) {// 缩放因子的变化scaleFactor *= detector.getScaleFactor();// 限制缩放因子的范围(可选)scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 5.0f));// 重绘 Viewinvalidate();return true;}}
}

三、自定义一个可拖拽和拉伸的ImageView

1. 思路整理

  • 首先,我们可以直接继承 ImageView,并通过 Matrix 来控制图片的移动和拉伸。
  • 其次,使用 GestureDetector 监听移动的相关事件,使用 ScaleGestureDetector 监听拉伸的相关事件。
  • 最后,我们可能需要控制图片最大和最小缩放的比例。实际应用中还会考虑一些图片边界、双击放大、动画等,可根据需求自行添加。

2. 示例

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;public class DragZoomImageView extends AppCompatImageView {private static final int NONE = 0;private static final int DRAG = 1;private static final int ZOOM = 2;private int mMode = NONE;private Matrix mFinalMatrix = new Matrix();private Matrix mSavedMatrix = new Matrix();// 图像以FitXY显示时使用的Scale大小private float mOriginScale = 1.0f;// 图像的最小、最大缩放比例private float mMinScale = 0.5f;private float mMaxScale = 5.0f;private float mCurrentScale = 1.0f;private Bitmap mBitmap;private GestureDetector mGestureDetector;private ScaleGestureDetector mScaleGestureDetector;public DragZoomImageView(@NonNull Context context) {super(context);init();}public DragZoomImageView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public DragZoomImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {setScaleType(ScaleType.MATRIX);mGestureDetector = new GestureDetector(getContext(), new GestureListener());mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleListener());}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);Drawable drawable = getDrawable();Bitmap bitmap = drawableToBitmap(drawable);if (bitmap != null) {Matrix matrix = getFitCenterMatrix(bitmap.getWidth(), bitmap.getHeight(), w, h);setImageMatrix(matrix);mOriginScale = getMatrixScaleX(matrix);mBitmap = bitmap;}}@Overridepublic void setImageMatrix(Matrix matrix) {super.setImageMatrix(matrix);mFinalMatrix.set(matrix);}@Overridepublic void setImageBitmap(Bitmap bm) {if (bm == null) {return;}super.setImageBitmap(bm);Matrix matrix = getFitCenterMatrix(bm.getWidth(), bm.getHeight(), getWidth(), getHeight());setImageMatrix(matrix);mOriginScale = getMatrixScaleX(matrix);mBitmap = bm;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mBitmap == null) {return false;}switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:  // 单指mMode = DRAG;break;case MotionEvent.ACTION_POINTER_DOWN:  // 多指mMode = ZOOM;break;}if (mMode == DRAG) {mGestureDetector.onTouchEvent(event);}if (mMode == ZOOM) {mScaleGestureDetector.onTouchEvent(event);}return true;}public void handleScale(float scale) {handleScale(scale, getWidth() / 2, getHeight() / 2);}public void handleScale(float scale, float px, float py) {if (scale < mMinScale) {scale = mMinScale;}if (scale > mMaxScale) {scale = mMaxScale;}if (mCurrentScale == scale) {return;}mCurrentScale = scale;  // record scalefloat newScale = scale * mOriginScale;float oldScale = getMatrixScaleX(mFinalMatrix);float postScale = newScale / oldScale;mFinalMatrix.postScale(postScale, postScale, px, py);super.setImageMatrix(mFinalMatrix);}private float getMatrixScaleX(Matrix matrix) {float[] values = new float[9];matrix.getValues(values);return values[Matrix.MSCALE_X];}private Matrix getFitCenterMatrix(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) {Matrix matrix = new Matrix();matrix.reset();float scale;float dx;float dy;scale = Math.min((float) viewWidth / (float) bitmapWidth, (float) viewHeight / (float) bitmapHeight);dx = Math.round((viewWidth - bitmapWidth * scale) * 0.5f);dy = Math.round((viewHeight - bitmapHeight * scale) * 0.5f);matrix.setScale(scale, scale);matrix.postTranslate(dx, dy);return matrix;}private Bitmap drawableToBitmap(Drawable drawable) {if (drawable == null) {return null;}if (drawable instanceof BitmapDrawable) {return ((BitmapDrawable) drawable).getBitmap();} else if (drawable instanceof ColorDrawable) {// 如果 Drawable 是 ColorDrawable,则创建一个相同大小的 BitmapColorDrawable colorDrawable = (ColorDrawable) drawable;int width = colorDrawable.getIntrinsicWidth();int height = colorDrawable.getIntrinsicHeight();Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);colorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());colorDrawable.draw(canvas);return bitmap;} else {// 如果 Drawable 不是 BitmapDrawable 或 ColorDrawable,则返回空return null;}}private class GestureListener extends GestureDetector.SimpleOnGestureListener {@Overridepublic boolean onDown(MotionEvent e) {mSavedMatrix.set(mFinalMatrix);return super.onDown(e);}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {Matrix matrix = new Matrix(mSavedMatrix);float dx = e2.getX() - e1.getX();float dy = e2.getY() - e1.getY();matrix.postTranslate(dx, dy);setImageMatrix(matrix);return true;}}private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {float px;float py;@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {px = detector.getFocusX();py = detector.getFocusY();return true;}@Overridepublic boolean onScale(ScaleGestureDetector detector) {float scale = detector.getScaleFactor() * mCurrentScale;handleScale(scale, px, py);return true;}}
}

这篇关于Android手势识别GestureDetector和ScaleGestureDetector介绍与使用,以自定义一个可拖拽拉伸的ImageView为例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/884338

相关文章

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

使用Python的requests库调用API接口的详细步骤

《使用Python的requests库调用API接口的详细步骤》使用Python的requests库调用API接口是开发中最常用的方式之一,它简化了HTTP请求的处理流程,以下是详细步骤和实战示例,涵... 目录一、准备工作:安装 requests 库二、基本调用流程(以 RESTful API 为例)1.

使用Python开发一个Ditto剪贴板数据导出工具

《使用Python开发一个Ditto剪贴板数据导出工具》在日常工作中,我们经常需要处理大量的剪贴板数据,下面将介绍如何使用Python的wxPython库开发一个图形化工具,实现从Ditto数据库中读... 目录前言运行结果项目需求分析技术选型核心功能实现1. Ditto数据库结构分析2. 数据库自动定位3

Python yield与yield from的简单使用方式

《Pythonyield与yieldfrom的简单使用方式》生成器通过yield定义,可在处理I/O时暂停执行并返回部分结果,待其他任务完成后继续,yieldfrom用于将一个生成器的值传递给另一... 目录python yield与yield from的使用代码结构总结Python yield与yield

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1