實現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
英文地址,請注明出處:http://en.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;更是為內容創作者和商家帶來了…