实现Android图片手势缩放功能的完整自定义View方案,结合了多种手势交互功能

主要功能特点:

  1. 支持双指手势缩放图片,通过ScaleGestureDetector实现平滑的缩放效果25
  2. 双击图片可切换初始大小和中等放大比例16
  3. 使用Matrix进行图像变换,保持缩放中心点为手势焦点位置57
  4. 自动缩放动画通过Runnable实现渐进式变化1
  5. 限制最小和最大缩放比例,防止过度缩放25

使用方式:

  1. 在布局文件中直接使用ZoomImageView替代普通ImageView
  2. 通过setImageResource()或setImageBitmap()设置图片
  3. 可通过setInitScale()等方法自定义缩放参数

注意事项:

  1. 需要处理onGlobalLayout回调确保视图完成初始化1
  2. 建议添加边界检查防止图片缩放后超出视图范围68
  3. 如需添加拖动功能,需扩展onTouchEvent实现移动逻辑
/*** 支持手势缩放的ImageView实现类* 功能:双指缩放、双击放大/还原、自动居中、缩放比例限制*/
public class ZoomImageView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener,  // 用于监听视图布局完成ScaleGestureDetector.OnScaleGestureListener,  // 缩放手势监听View.OnTouchListener {  // 触摸事件监听// 标志位:是否已初始化private boolean mOnce = false;// 初始缩放比例private float mInitScale;// 中等缩放比例(双击第一次)private float mMidScale;// 最大缩放比例private float mMaxScale;// 用于图片变换的矩阵private Matrix mScaleMatrix;// 缩放手势检测器private ScaleGestureDetector mScaleDetector;// 手势检测器(用于双击检测)private GestureDetector mGestureDetector;// 构造方法链public ZoomImageView(Context context) {this(context, null);}public ZoomImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ZoomImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// 初始化变换矩阵mScaleMatrix = new Matrix();// 必须设置为MATRIX才能通过矩阵控制缩放setScaleType(ScaleType.MATRIX);// 初始化缩放手势检测器mScaleDetector = new ScaleGestureDetector(context, this);// 初始化手势检测器(主要用于双击检测)mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onDoubleTap(MotionEvent e) {// 双击事件处理:当前缩放小于中等比例则放大到中等比例,否则还原float scale = getScale();float targetScale = scale < mMidScale ? mMidScale : mInitScale;// 启动自动缩放动画post(new AutoScaleRunnable(targetScale, e.getX(), e.getY()));return true;}});// 设置触摸监听setOnTouchListener(this);}/*** 缩放手势回调 - 缩放过程中持续触发*/@Overridepublic boolean onScale(ScaleGestureDetector detector) {float scale = getScale();float scaleFactor = detector.getScaleFactor();// 缩放条件判断:放大时不超过最大比例,缩小时不小于初始比例if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)) {// 限制缩放因子范围scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 5.0f));// 以手势焦点为中心进行缩放mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());// 应用变换矩阵setImageMatrix(mScaleMatrix);}return true;}/*** 触摸事件处理*/@Overridepublic boolean onTouch(View v, MotionEvent event) {// 将触摸事件传递给手势检测器mScaleDetector.onTouchEvent(event);mGestureDetector.onTouchEvent(event);return true;}/*** 自动缩放动画Runnable*/private class AutoScaleRunnable implements Runnable {private float mTargetScale;  // 目标缩放比例private float x, y;         // 缩放中心点坐标private final float BIGGER = 1.07f;   // 放大系数private final float SMALLER = 0.93f;  // 缩小系数public AutoScaleRunnable(float targetScale, float x, float y) {this.mTargetScale = targetScale;this.x = x;this.y = y;}@Overridepublic void run() {float currentScale = getScale();// 判断应该放大还是缩小float tmpScale = currentScale < mTargetScale ? BIGGER : SMALLER;// 执行缩放变换mScaleMatrix.postScale(tmpScale, tmpScale, x, y);setImageMatrix(mScaleMatrix);// 判断是否需要继续动画if ((tmpScale > 1f && currentScale < mTargetScale) || (tmpScale < 1f && currentScale > mTargetScale)) {// 16ms后继续执行(约60FPS)postDelayed(this, 16);}}}
}

代码功能说明:

  1. 核心功能:通过Matrix实现图片的缩放变换,支持双指手势缩放和双击切换
  2. 手势检测:使用ScaleGestureDetector处理捏合手势,GestureDetector处理双击事件
  3. 比例控制:限制最小/最大缩放比例,防止图片过度缩放
  4. 动画效果:通过Runnable实现平滑的自动缩放动画
  5. 交互优化:缩放中心始终为手势焦点位置,提升用户体验

在Android开发中,我做过最复杂的自定义View是一个支持多手势操作的图片浏览器,主要实现了以下高级功能:

  1. 核心难点实现:
  • 多层级手势冲突处理(双指缩放+单指拖动+双击+长按)
  • 基于Matrix的精准图像变换控制
  • 边缘回弹效果和惯性滑动
  • 动态加载大图的分块显示
  1. 自定义View开发要点:
    (1)绘制流程
  • 测量(onMeasure)→布局(onLayout)→绘制(onDraw)
  • 处理wrap_content和padding
  • 优化invalidate()的调用范围

(2)事件处理

  • 使用GestureDetector处理单击/双击/长按
  • 通过ScaleGestureDetector实现双指缩放
  • VelocityTracker计算滑动速度
  • 自定义事件分发逻辑解决手势冲突

(3)性能优化

  • 使用ValueAnimator实现平滑动画
  • 通过Canvas.clipRect()限制绘制区域
  • 大图采用BitmapRegionDecoder分块加载
  • 使用View.post()保证线程安全
  1. 典型问题解决方案:
  • 缩放中心点计算:通过Matrix映射坐标
  • 边界检测:计算图像变换后的位置矩阵
  • 内存优化:及时recycle()不再使用的Bitmap
  • 过渡绘制:关闭硬件加速时单独处理

这个自定义View最终实现了类似微信图片浏览器的完整交互:

  • 支持双指自由缩放(带惯性效果)
  • 双击智能缩放(自动适配屏幕/原始尺寸)
  • 拖动时边缘回弹
  • 长按弹出操作菜单
  • 支持超高清图片的流畅浏览

开发过程中最大的挑战是处理各种手势的优先级冲突,最终通过状态机模式管理不同交互状态,并引入手势阈值判定机制来完美解决。

在Android开发中,我做过最复杂的自定义View是一个支持多手势操作的图片浏览器,主要实现了以下高级功能:

  1. 核心难点实现:
  • 多层级手势冲突处理(双指缩放+单指拖动+双击+长按)
  • 基于Matrix的精准图像变换控制
  • 边缘回弹效果和惯性滑动
  • 动态加载大图的分块显示
  1. 自定义View开发要点:
    (1)绘制流程
  • 测量(onMeasure)→布局(onLayout)→绘制(onDraw)
  • 处理wrap_content和padding
  • 优化invalidate()的调用范围

(2)事件处理

  • 使用GestureDetector处理单击/双击/长按
  • 通过ScaleGestureDetector实现双指缩放
  • VelocityTracker计算滑动速度
  • 自定义事件分发逻辑解决手势冲突

(3)性能优化

  • 使用ValueAnimator实现平滑动画
  • 通过Canvas.clipRect()限制绘制区域
  • 大图采用BitmapRegionDecoder分块加载
  • 使用View.post()保证线程安全
  1. 典型问题解决方案:
  • 缩放中心点计算:通过Matrix映射坐标
  • 边界检测:计算图像变换后的位置矩阵
  • 内存优化:及时recycle()不再使用的Bitmap
  • 过渡绘制:关闭硬件加速时单独处理

这个自定义View最终实现了类似微信图片浏览器的完整交互:

  • 支持双指自由缩放(带惯性效果)
  • 双击智能缩放(自动适配屏幕/原始尺寸)
  • 拖动时边缘回弹
  • 长按弹出操作菜单
  • 支持超高清图片的流畅浏览

开发过程中最大的挑战是处理各种手势的优先级冲突,最终通过状态机模式管理不同交互状态,并引入手势阈值判定机制来完美解决。

以下是一个完整的Android自定义View示例,实现带进度动画的圆形进度条:

CircleProgressView.java

该自定义View主要实现以下功能特点:

  1. 继承View基类并实现三种构造方法5
  2. 在onMeasure()中处理View的尺寸测量逻辑3
  3. 通过Paint和Canvas在onDraw()中完成圆形进度条的绘制6
  4. 使用ValueAnimator实现进度变化的平滑动画4
  5. 支持通过setProgress()方法动态更新进度值1

使用方式:

  1. 在XML布局中添加:

xmlCopy Code

<com.example.CircleProgressView android:layout_width="200dp" android:layout_height="200dp"/>

  1. 在代码中控制进度:

javaCopy Code

progressView.setProgress(75); // 设置75%进度

如需添加自定义属性(如进度条颜色/宽度等),可参考以下扩展:

  1. 在res/values/attrs.xml定义属性7
  2. 在构造方法中解析属性值4
  3. 添加属性设置方法实现动态修改

/*** 自定义圆形进度条View* 功能特点:* 1. 支持动态设置进度值* 2. 内置平滑过渡动画* 3. 可自定义进度条样式*/
public class CircleProgressView extends View {// 背景圆画笔private Paint mBackgroundPaint;// 进度条画笔private Paint mProgressPaint;// 绘制弧形的矩形区域private RectF mArcRect = new RectF();// 当前进度值(0-100)private float mCurrentProgress = 0;// 进度动画控制器private ValueAnimator mProgressAnimator;// 构造方法1:代码创建View时调用public CircleProgressView(Context context) {this(context, null);}// 构造方法2:XML布局中声明时调用public CircleProgressView(Context context, AttributeSet attrs) {super(context, attrs);initPaints(); // 初始化画笔}/*** 初始化画笔配置*/private void initPaints() {// 背景圆画笔配置mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mBackgroundPaint.setColor(Color.LTGRAY); // 默认灰色背景mBackgroundPaint.setStyle(Paint.Style.STROKE); // 空心样式mBackgroundPaint.setStrokeWidth(20); // 线条宽度// 进度条画笔配置mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mProgressPaint.setColor(Color.BLUE); // 默认蓝色进度mProgressPaint.setStyle(Paint.Style.STROKE); // 空心样式mProgressPaint.setStrokeWidth(20); // 线条宽度mProgressPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角线帽}/*** 测量View尺寸* 确保View始终是正方形*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 取宽高最小值作为正方形边长int size = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));setMeasuredDimension(size, size); // 设置最终测量尺寸}/*** 绘制View内容*/@Overrideprotected void onDraw(Canvas canvas) {// 计算圆心坐标和半径float center = getWidth() / 2f;float radius = center - mProgressPaint.getStrokeWidth();// 设置绘制弧形的矩形区域mArcRect.set(center - radius, center - radius, center + radius, center + radius);// 绘制背景圆canvas.drawCircle(center, center, radius, mBackgroundPaint);// 绘制进度弧线(从-90度开始,顺时针绘制)canvas.drawArc(mArcRect, -90, mCurrentProgress * 3.6f, false, mProgressPaint);}/*** 设置进度值(带动画效果)* @param progress 目标进度值(0-100)*/public void setProgress(float progress) {// 取消之前的动画(如果存在)if (mProgressAnimator != null) {mProgressAnimator.cancel();}// 创建属性动画(从当前进度到目标进度)mProgressAnimator = ValueAnimator.ofFloat(mCurrentProgress, progress);mProgressAnimator.setDuration(800); // 动画时长800ms// 动画更新监听器mProgressAnimator.addUpdateListener(animation -> {mCurrentProgress = (float) animation.getAnimatedValue();invalidate(); // 触发重绘});mProgressAnimator.start(); // 启动动画}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/919058.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/919058.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app实战教程 从0到1开发 画图软件 (橡皮擦)

一、本期内容简述1. 开发内容上一期&#xff0c;我们一起学习了如何进行绘画&#xff0c;本期我们将学习如何擦除我们所绘画的内容&#xff0c;也就是“橡皮擦”功能。首先&#xff0c;我们应该明确需求&#xff0c;橡皮擦可以擦除掉我们绘画的内容。2. 开发需求所以开发需求&a…

《A Practical Guide to Building Agents》文档学习

《A Practical Guide to Building Agents》文档总结 该文档是一份面向产品和工程团队的实用指南&#xff0c;旨在帮助团队探索并构建首个基于大语言模型&#xff08;LLM&#xff09;的智能体&#xff08;Agent&#xff09;&#xff0c;提炼了大量客户部署经验&#xff0c;提供了…

OpenCV图像注册模块

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 注册模块实现了参数化图像配准。所实现的方法是直接对齐&#xff08;direct alignment&#xff09;&#xff0c;即&#xff0c;它直接使用像素值来…

模型驱动与分布式建模:技术深度与实战落地指南

摘要 在AI、云原生与全球化协作的大潮中&#xff0c;模型驱动架构&#xff08;MDA&#xff09;与分布式建模不再是概念&#xff0c;而是支撑复杂系统设计与持续演化的核心引擎。本文从元模型、模型转换引擎&#xff0c;到协同协议、冲突解决算法&#xff0c;再到AI辅助建模与自…

计算机基础速通--数据结构·图的基础应用二(基础图算法)

如有问题大概率是我的理解比较片面&#xff0c;欢迎评论区或者私信指正。 最近了解到了一个新的改变和提高自己的方法时刻记录不论多小的事情都记下&#xff0c;我目前用了4天&#xff0c;之前感觉一天天忙死但没啥收获&#xff0c;但是记录了之后知道自己的时间花在了哪里&…

设计模式-策略模式 Java

模式概述 策略模式是一种行为型设计模式&#xff0c;它通过定义一系列可互换的算法&#xff0c;并将每个算法封装成独立类&#xff0c;使客户端能够根据需要动态切换算法 简单代码示例 // 1. 抽象策略接口 interface PaymentStrategy {void pay(int amount); }// 2. 具体策略实…

【机器学习深度学习】客观评估训练程度

目录 前言 一、什么是客观评估&#xff1f; 二、客观评估的两大核心方法 1. 判别式评测&#xff08;Discriminative Evaluation&#xff09; 2. 生成式评测&#xff08;Generative Evaluation&#xff09; 三、为什么客观评估成本更高&#xff1f; 1.训练目标收紧 2.训…

Linux软件编程:线程间通信

目录 一、线程间通信基础 1. 概念 2. 通信基础&#xff1a;共享空间 二、互斥锁&#xff08;Mutex&#xff09; 1. 概念 2. 使用流程 3. 函数接口 三、死锁 1. 概念 2. 死锁产生的 4 个必要条件 3. 避免死锁的方法 四、信号量&#xff08;Semaphore&#xff09; 1…

【学习笔记】JVM GC回收机制

1.三种基本的垃圾回收算法 1>标记-清除法 ①先将从树根开始&#xff0c;可以到达的对象标记为可达&#xff08;JVM中的对象们存储为一颗树&#xff09; ②将没有标记的对象清除掉 缺点&#xff1a;会产生大量内存碎片 2>复制算法&#xff08;新生代&#xff09; ①先将a区…

软件的终极:为70亿人编写70亿个不同的软件

这是个脑洞大开的想法。昨天晚上&#xff0c;我在用Claude code帮我写一个小工具&#xff0c;用来管理我本地那些乱七八糟的文档。写着写着&#xff0c;突然意识到一个问题&#xff1a;这个工具完全是按照我的工作习惯定制的——我喜欢用Markdown&#xff0c;习惯把TODO放在文件…

LakeHouse--湖仓一体架构

大家可能发现了,近些年湖仓一体数据架构被提及的频率越来越高。各家大厂也有湖仓一体架构的实践,也有很多公开分享。 那什么是湖仓一体?为什么出现了湖仓一体架构,换言之,它解决了以前数据仓库、数据湖+数仓两层架构所不能解决的什么问题? 本文会从数仓、数据湖依次介绍…

基于FPGA的实时图像处理系统(1)——SDRAM回环测试

SDRAM回环设计 文章目录SDRAM回环设计一、SDRAM简介1、引脚2、内部结构框图3、操作指令二、系统设计三、实现流程1、SDRAM接口2、FIFO设置3、内部SDRAM的控制模块4、其他四、实现效果五、总结六、代码1、top2、sdram_top3、sdram_ctrl一、SDRAM简介 SDRAM英文全称“Synchronou…

一键检测接口是否存活:用 Python/Shell 写个轻量级监控脚本

网罗开发&#xff08;小红书、快手、视频号同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…

优秀工具包-Hutool工具详解

优秀工具包-Hutool工具详解 课程概述 Hutool简介 定位&#xff1a; 小而全的Java工具库&#xff0c;简化开发流程。对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装。 核心优势&#xff1a;零依赖、高性能、中文网页完善。 应用场景&#xff1a;Web开发、数…

《深度解构:构建浏览器端Redis控制台的WebSocket协议核心技术》

Redis作为高性能的内存数据库,其原生客户端多依赖命令行或桌面应用,而浏览器端控制台的缺失,成为制约Web化管理的关键瓶颈,WebSocket协议的出现,打破了HTTP协议单向通信的局限,为浏览器与Redis服务之间建立持久、双向的实时连接提供了可能。本文将从协议本质、交互逻辑、…

Pushgateway安装和部署,以及对应Prometheus调整

目录Pushgateway简介安装验证Prometheus的配置&#xff1a;其它命令Pushgateway简介 Pushgateway 是 Prometheus 生态系统中的一个组件。主要特点是推送而非拉取&#xff1a;Prometheus 默认采用拉取&#xff08;pull&#xff09;模式收集指标&#xff0c;但 Pushgateway 允许…

JAVA面试汇总(四)JVM(一)

久违的重新写了一篇面试汇总的&#xff0c;关于JVM的一篇&#xff0c;一共三篇&#xff0c;今天写了第一篇&#xff0c;继续重新学习&#xff0c;重新卷起来&#xff0c;come on baby 1.什么情况下会触发类的初始化&#xff1f; &#xff08;1&#xff09;首先是类未被初始化时…

Agent中的memory

rag系列文章目录 文章目录rag系列文章目录前言一、Memory机制作用二、memory分类三、langgraph实践总结前言 众所周知&#xff0c;大模型是无状态的。但是基于大模型的agent一般是有状态的&#xff0c;也就是它有记忆功能。在AI Agent框架中&#xff0c;Memory机制是核心组件之…

AI与IT从业者的未来:替代焦虑还是协作革命?

​​引言&#xff1a;技术渗透与核心命题​​2025年&#xff0c;人工智能技术已从实验室走向产业核心。国务院《关于深入实施“人工智能”行动的意见》推动AI在医疗、制造、金融等领域的规模化落地&#xff0c;全球AI应用用户规模突破2.3亿&#xff0c;生成式AI工具渗透率达16.…

手机版碰一碰发视频系统批量剪辑功能开发,支持OEM贴牌

引言在当今短视频盛行的时代&#xff0c;视频内容的快速生产与分享变得愈发重要。手机版碰一碰发视频系统&#xff0c;借助 NFC 等近场通信技术&#xff0c;实现了便捷的数据交互与视频分享&#xff0c;而在此基础上集成的批量剪辑功能&#xff0c;更是为内容创作者和商家带来了…