一基本实现思路:

基于View类实现自定义View –MyImageView类。在使用View的Activity类中完成OnTouchListener接口,实现对MotionEvent事件的监听与处理,常见的MotionEvent事件如下:

ACTION_DOWN事件,记录平移开始点
ACTION_UP事件,结束平移事件处理
ACTION_MOVE事件,记录平移点,计算与开始点距离,实现Bitmap平移,在多点触控时候,计算两点之间的距离,实现图像放大
ACTION_POINTER_DOWN事件,计算两点之间的距离,作为初始距离,实现图像手势放大时候使用。
ACTION_POINTER_UP事件,结束两点触控放大图像处理

放大与拖动
基于单点触控实现Bitmap对象在View上的拖动、并且检测View的边缘,防止拖动过界。基于两个点触控实现Bitmap对象在View上的放大、并且检测放大倍数。基于Matrix对象实现对Bitmap在View上放大与平移变换,Matrix对象是android中实现图像几何变换的矩阵,支持平移、放大、缩小、错切、旋转等常见操作。

Bitmap对象在View中的更新与显示
通过重载onDraw方法,使用canvas实现绘制Bitmap对象、通过view.invalidate()方法实现View的刷新。

MyImageView类的重要方法说明:
initParameters()初始化所有需要用到的参数
setStartPoint()设置图像平移的开始点坐标

setMovePoint()设置图像平移的移动点坐标,然后集合开始点位置,计算它们之间的距离,从而得到Bitmap对象需要平移的两个参数值sx、sy。其中还包括保证图像不会越过View边界的检查代码。

savePreviousResult()保存当前的平移数据,下次可以继续在次基础上平移Bitmap对象。

zoomIn()根据两个点之间的欧几里德距离,通过初始距离比较,得到放大比例,实现Bitmap在View对象上的放大

Matrix中关于放大与平移的API

Matrix.postScale方法与Matrix.postTranslate方法可以不改变Bitmap对象本身实现平移与放大。

二:代码实现

自定义View类使用xml布局如下:

[html] view plaincopy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:paddingBottom="@dimen/activity_vertical_margin"
  6. android:paddingLeft="@dimen/activity_horizontal_margin"
  7. android:paddingRight="@dimen/activity_horizontal_margin"
  8. android:paddingTop="@dimen/activity_vertical_margin"
  9. tools:context=".MainActivity" >
  10. <com.example.matrixdemo.MyImageView
  11. android:id="@+id/myView"
  12. android:layout_width="fill_parent"
  13. android:layout_height="fill_parent"
  14. android:text="@string/hello_world" />
  15. </RelativeLayout>

自定义View实现代码如下:

[java] view plaincopy
  1. package com.example.matrixdemo;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Matrix;
  7. import android.graphics.Paint;
  8. import android.graphics.Paint.Style;
  9. import android.graphics.Point;
  10. import android.graphics.Rect;
  11. import android.util.AttributeSet;
  12. import android.view.View;
  13. public class MyImageView extends View {
  14. private Paint mPaint;
  15. private Bitmap bitmap;
  16. private Matrix matrix;
  17. // 平移开始点与移动点
  18. private Point startPoint;
  19. private Point movePoint;
  20. private float initDistance;
  21. // 记录当前平移距离
  22. private int sx;
  23. private int sy;
  24. // 保存平移状态
  25. private int oldsx;
  26. private int oldsy;
  27. // scale rate
  28. private float widthRate;
  29. private float heightRate;
  30. public MyImageView(Context context) {
  31. super(context);
  32. }
  33. public MyImageView(Context context, AttributeSet attrs) {
  34. super(context, attrs);
  35. }
  36. public void setBitmap(Bitmap bitmap) {
  37. this.bitmap = bitmap;
  38. }
  39. private void initParameters() {
  40. // 初始化画笔
  41. mPaint = new Paint();
  42. mPaint.setColor(Color.BLACK);
  43. matrix = new Matrix();
  44. if(bitmap != null)
  45. {
  46. float iw = bitmap.getWidth();
  47. float ih = bitmap.getHeight();
  48. float width = this.getWidth();
  49. float height = this.getHeight();
  50. // 初始放缩比率
  51. widthRate = width / iw;
  52. heightRate = height / ih;
  53. }
  54. sx = 0;
  55. sy = 0;
  56. oldsx = 0;
  57. oldsy = 0;
  58. }
  59. public void setStartPoint(Point startPoint) {
  60. this.startPoint = startPoint;
  61. }
  62. public void setInitDistance(float initDistance) {
  63. this.initDistance = initDistance;
  64. }
  65. public void zoomIn(float distance)
  66. {
  67. float rate = distance / this.initDistance;
  68. float iw = bitmap.getWidth();
  69. float ih = bitmap.getHeight();
  70. float width = this.getWidth();
  71. float height = this.getHeight();
  72. // get scale rate
  73. widthRate = (width / iw ) * rate;
  74. heightRate = (height / ih) * rate;
  75. // make it same as view size
  76. float iwr = (width / iw );
  77. float ihr = (height / ih);
  78. if(iwr >= widthRate)
  79. {
  80. widthRate = (width / iw );
  81. }
  82. if(ihr >= heightRate)
  83. {
  84. heightRate = (height / ih);
  85. }
  86. // go to center
  87. oldsx = (int)((width - widthRate * iw) / 2);
  88. oldsy = (int)((height - heightRate * ih) / 2);
  89. }
  90. public void setMovePoint(Point movePoint) {
  91. this.movePoint = movePoint;
  92. sx = this.movePoint.x - this.startPoint.x;
  93. sy = this.movePoint.y - this.startPoint.y;
  94. float iw = bitmap.getWidth();
  95. float ih = bitmap.getHeight();
  96. // 检测边缘
  97. int deltax = (int)((widthRate * iw) - this.getWidth());
  98. int deltay = (int)((heightRate * ih) - this.getHeight());
  99. if((sx + this.oldsx) >= 0)
  100. {
  101. this.oldsx = 0;
  102. sx = 0;
  103. }
  104. else if((sx + this.oldsx) <= -deltax)
  105. {
  106. this.oldsx = -deltax;
  107. sx = 0;
  108. }
  109. if((sy + this.oldsy) >= 0)
  110. {
  111. this.oldsy = 0;
  112. this.sy = 0;
  113. }
  114. else if((sy + this.oldsy) <= -deltay)
  115. {
  116. this.oldsy = -deltay;
  117. this.sy = 0;
  118. }
  119. float width = this.getWidth();
  120. // 初始放缩比率
  121. float iwr = width / iw;
  122. if(iwr == widthRate)
  123. {
  124. sx = 0;
  125. sy = 0;
  126. oldsx = 0;
  127. oldsy = 0;
  128. }
  129. }
  130. public void savePreviousResult()
  131. {
  132. this.oldsx = this.sx + this.oldsx;
  133. this.oldsy = this.sy + this.oldsy;
  134. // zero
  135. sx = 0;
  136. sy = 0;
  137. }
  138. @Override
  139. protected void onDraw(Canvas canvas) {
  140. if(matrix == null)
  141. {
  142. initParameters();
  143. }
  144. if(bitmap != null)
  145. {
  146. matrix.reset();
  147. matrix.postScale(widthRate, heightRate);
  148. matrix.postTranslate(oldsx+sx, oldsy + sy);
  149. canvas.drawBitmap(bitmap, matrix, mPaint);
  150. }
  151. else
  152. {
  153. // fill rect
  154. Rect rect = new Rect(0, 0, getWidth(), getHeight());
  155. mPaint.setAntiAlias(true);
  156. mPaint.setColor(Color.BLACK);
  157. mPaint.setStyle(Style.FILL_AND_STROKE);
  158. canvas.drawRect(rect, mPaint);
  159. }
  160. }
  161. }

Activity类中实现对View的OnTouchListener监听与MotionEvent事件处理的代码如下:

[java] view plaincopy
  1. package com.example.matrixdemo;
  2. import android.app.Activity;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.graphics.Point;
  6. import android.os.Bundle;
  7. import android.util.Log;
  8. import android.view.Menu;
  9. import android.view.MotionEvent;
  10. import android.view.View;
  11. import android.view.View.OnTouchListener;
  12. public class MainActivity extends Activity implements OnTouchListener {
  13. public static final int SCALE_MODE = 4;
  14. public static final int TRANSLATION_MODE = 2;
  15. public static final int NULL_MODE = 1;
  16. private MyImageView myView;
  17. private int mode;
  18. @Override
  19. protected void onCreate(Bundle savedInstanceState) {
  20. super.onCreate(savedInstanceState);
  21. setContentView(R.layout.activity_main);
  22. startMyImageView();
  23. }
  24. private void startMyImageView() {
  25. myView = (MyImageView) this.findViewById(R.id.myView);
  26. Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(),
  27. R.drawable.flower_001);
  28. myView.setBitmap(bitmap);
  29. myView.setOnTouchListener(this);
  30. myView.invalidate();
  31. }
  32. @Override
  33. public boolean onCreateOptionsMenu(Menu menu) {
  34. getMenuInflater().inflate(R.menu.main, menu);
  35. return true;
  36. }
  37. @Override
  38. public boolean onTouch(View view, MotionEvent event) {
  39. Log.i("touch event","touch x = " + event.getX());
  40. switch (MotionEvent.ACTION_MASK & event.getAction())
  41. {
  42. case MotionEvent.ACTION_DOWN:
  43. mode = TRANSLATION_MODE;
  44. myView.setStartPoint(new Point((int)event.getX(), (int)event.getY()));
  45. break;
  46. case MotionEvent.ACTION_POINTER_UP:
  47. case MotionEvent.ACTION_OUTSIDE:
  48. case MotionEvent.ACTION_UP:
  49. mode = NULL_MODE;
  50. myView.savePreviousResult();
  51. break;
  52. case MotionEvent.ACTION_POINTER_DOWN:
  53. mode = SCALE_MODE;
  54. myView.setInitDistance(calculateDistance(event));
  55. break;
  56. case MotionEvent.ACTION_MOVE:
  57. if(mode == SCALE_MODE)
  58. {
  59. float dis = calculateDistance(event);
  60. myView.zoomIn(dis);
  61. }
  62. else if(mode == TRANSLATION_MODE)
  63. {
  64. myView.setMovePoint(new Point((int)event.getX(), (int)event.getY()));
  65. }
  66. else
  67. {
  68. Log.i("unknow mode tag","do nothing......");
  69. }
  70. break;
  71. }
  72. myView.invalidate();
  73. return true;
  74. }
  75. private float calculateDistance(MotionEvent event) {
  76. float dx = event.getX(0) - event.getX(1);
  77. float dy = event.getY(0)  - event.getY(1);
  78. float distance = (float)Math.sqrt(dx*dx + dy*dy);
  79. return distance;
  80. }
  81. }

三:运行效果如下

Android中实现Bitmap在自定义View中的放大与拖动相关推荐

  1. 《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进

    在<Android开发艺术探索>一书中自定义View一节中提到了关于一个类似横向滑动List的自定义ViewGroup:HorizontalScrollViewEx.如果你使用过的话就会发 ...

  2. Android 自定义View中坐标点的理解学习(一)

    本文主要是记录学习自定义view中看到的资料,为了方便记忆做了保存整理便于自己学习也方便其他Android开发爱好者学习,参考资料看底部链接. 一.getLocationInWindow和getLoc ...

  3. 安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例

    安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例,图片压缩处理逻辑 本文旨在介绍自定义View的实现 ...

  4. Android软件开发之盘点自定义View界面大合集(二)

    Android软件开发之盘点自定义View界面大合集(二) - 雨松MOMO的程序世界 - 51CTO技术博客 雨松MOMO带大家盘点Android 中的自定义View界面的绘制 今天我用自己写的一个 ...

  5. android绘制心形_Android自定义View系列(一)——打造一个爱心进度条

    写作原因:Android进阶过程中有一个绕不开的话题--自定义View.这一块是安卓程序员更好地实现功能自主化必须迈出的一步.下面这个系列博主将通过实现几个例子来认识安卓自定义View的方法.从自定义 ...

  6. Android 雪花飘落动画效果 自定义View

    在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天.每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不 ...

  7. Carson带你学Android:源码解析自定义View Draw过程

    前言 自定义View是Android开发者必须了解的基础 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化 等 今天,我将全面总结自定义View ...

  8. android 仿360浮动,Android仿360悬浮小球自定义view实现示例

    Android仿360悬浮小球自定义view实现示例 效果图如下: 实现当前这种类似的效果 和360小球 悬浮桌面差不错类似.这种效果是如何实现的呢.废话不多说 ,直接上代码. 1.新建工程,添加悬浮 ...

  9. Android仿IOS滑动关机-自定义view系列(6)

    Android仿IOS滑动关机-自定义view系列 功能简介 GIf演示 主要实现步骤-具体内容看github项目里的代码 Android技术生活交流 更多其他页面-自定义View-实用功能合集:点击 ...

最新文章

  1. java dbcp_Java dbcp连接池基本使用方法详解
  2. pulsar 容量_Pulsar 负载均衡设计
  3. mysql5.6+master+date_MySQL5.6的4个自带库详解
  4. python变量类型声明_python变量声明及简单数据类型
  5. 增广最小二乘法 matlab 东南大学,各种最小二乘法总结(算法+matlab源码)
  6. 正则匹配字符串有则替换无则添加;用正则实现添加和替换字符串,原字符串中包含某字段就替换(覆盖),不包含某字段就添加!
  7. BZOJ3668:[NOI2014]起床困难综合症(贪心)
  8. macOS安装配置rzsz(附博主安装过程中的错误解决)
  9. LM2596降压DCDC芯片详解
  10. SolidWorks机箱机柜钣金3D模型图档
  11. java打开dex文件_dex文件反编译工具(Dedexer)
  12. keepalived 单播模式
  13. 【华人学者风采】杨义 悉尼科技大学
  14. 从少年变成老男孩-----韩寒与郭敬明的十年
  15. SQL中OVER(PARTITION BY)详解
  16. 新年贺卡php,PS设计2016猴年大吉新年贺卡
  17. 简单理解SpringMVC的三层结构顺序MCV以及ModelAndView的使用
  18. 华硕主板更改UEFI安全启动项
  19. RISC-V扩展指令示例
  20. neu坐标系和xyz坐标系转换_航测必知的坐标系详解和转换关系

热门文章

  1. 【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )
  2. 【Groovy】Groovy 方法调用 ( Groovy 构造函数中为成员赋值 | Groovy 函数的参数传递与键值对参数 | 完整代码示例 )
  3. 【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )
  4. 【Windows 逆向】OD 调试器工具 ( 推荐汉化版的 OD 调试工具 | 吾爱破解专用版Ollydbg | 备选工具 )
  5. 【Windows 逆向】CheatEngine 工具 ( CheatEngine 简介 | 使用 Lazarus 编译 CE 源码 | CheatEngine 相关文档资料 )
  6. 【错误记录】执行 Python 程序报错 ( NameError: name ‘reload‘ is not defined )
  7. 【Android NDK 开发】JNI 方法解析 ( JNIEXPORT 与 JNICALL 宏定义作用 )
  8. 深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数
  9. CodeForces - 833B The Bakery
  10. java web学习笔记-jsp篇