Android中实现Bitmap在自定义View中的放大与拖动
一基本实现思路:
基于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布局如下:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <com.example.matrixdemo.MyImageView
- android:id="@+id/myView"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:text="@string/hello_world" />
- </RelativeLayout>
自定义View实现代码如下:
- package com.example.matrixdemo;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.graphics.Paint.Style;
- import android.graphics.Point;
- import android.graphics.Rect;
- import android.util.AttributeSet;
- import android.view.View;
- public class MyImageView extends View {
- private Paint mPaint;
- private Bitmap bitmap;
- private Matrix matrix;
- // 平移开始点与移动点
- private Point startPoint;
- private Point movePoint;
- private float initDistance;
- // 记录当前平移距离
- private int sx;
- private int sy;
- // 保存平移状态
- private int oldsx;
- private int oldsy;
- // scale rate
- private float widthRate;
- private float heightRate;
- public MyImageView(Context context) {
- super(context);
- }
- public MyImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- public void setBitmap(Bitmap bitmap) {
- this.bitmap = bitmap;
- }
- private void initParameters() {
- // 初始化画笔
- mPaint = new Paint();
- mPaint.setColor(Color.BLACK);
- matrix = new Matrix();
- if(bitmap != null)
- {
- float iw = bitmap.getWidth();
- float ih = bitmap.getHeight();
- float width = this.getWidth();
- float height = this.getHeight();
- // 初始放缩比率
- widthRate = width / iw;
- heightRate = height / ih;
- }
- sx = 0;
- sy = 0;
- oldsx = 0;
- oldsy = 0;
- }
- public void setStartPoint(Point startPoint) {
- this.startPoint = startPoint;
- }
- public void setInitDistance(float initDistance) {
- this.initDistance = initDistance;
- }
- public void zoomIn(float distance)
- {
- float rate = distance / this.initDistance;
- float iw = bitmap.getWidth();
- float ih = bitmap.getHeight();
- float width = this.getWidth();
- float height = this.getHeight();
- // get scale rate
- widthRate = (width / iw ) * rate;
- heightRate = (height / ih) * rate;
- // make it same as view size
- float iwr = (width / iw );
- float ihr = (height / ih);
- if(iwr >= widthRate)
- {
- widthRate = (width / iw );
- }
- if(ihr >= heightRate)
- {
- heightRate = (height / ih);
- }
- // go to center
- oldsx = (int)((width - widthRate * iw) / 2);
- oldsy = (int)((height - heightRate * ih) / 2);
- }
- public void setMovePoint(Point movePoint) {
- this.movePoint = movePoint;
- sx = this.movePoint.x - this.startPoint.x;
- sy = this.movePoint.y - this.startPoint.y;
- float iw = bitmap.getWidth();
- float ih = bitmap.getHeight();
- // 检测边缘
- int deltax = (int)((widthRate * iw) - this.getWidth());
- int deltay = (int)((heightRate * ih) - this.getHeight());
- if((sx + this.oldsx) >= 0)
- {
- this.oldsx = 0;
- sx = 0;
- }
- else if((sx + this.oldsx) <= -deltax)
- {
- this.oldsx = -deltax;
- sx = 0;
- }
- if((sy + this.oldsy) >= 0)
- {
- this.oldsy = 0;
- this.sy = 0;
- }
- else if((sy + this.oldsy) <= -deltay)
- {
- this.oldsy = -deltay;
- this.sy = 0;
- }
- float width = this.getWidth();
- // 初始放缩比率
- float iwr = width / iw;
- if(iwr == widthRate)
- {
- sx = 0;
- sy = 0;
- oldsx = 0;
- oldsy = 0;
- }
- }
- public void savePreviousResult()
- {
- this.oldsx = this.sx + this.oldsx;
- this.oldsy = this.sy + this.oldsy;
- // zero
- sx = 0;
- sy = 0;
- }
- @Override
- protected void onDraw(Canvas canvas) {
- if(matrix == null)
- {
- initParameters();
- }
- if(bitmap != null)
- {
- matrix.reset();
- matrix.postScale(widthRate, heightRate);
- matrix.postTranslate(oldsx+sx, oldsy + sy);
- canvas.drawBitmap(bitmap, matrix, mPaint);
- }
- else
- {
- // fill rect
- Rect rect = new Rect(0, 0, getWidth(), getHeight());
- mPaint.setAntiAlias(true);
- mPaint.setColor(Color.BLACK);
- mPaint.setStyle(Style.FILL_AND_STROKE);
- canvas.drawRect(rect, mPaint);
- }
- }
- }
Activity类中实现对View的OnTouchListener监听与MotionEvent事件处理的代码如下:
- package com.example.matrixdemo;
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Point;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.Menu;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.View.OnTouchListener;
- public class MainActivity extends Activity implements OnTouchListener {
- public static final int SCALE_MODE = 4;
- public static final int TRANSLATION_MODE = 2;
- public static final int NULL_MODE = 1;
- private MyImageView myView;
- private int mode;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- startMyImageView();
- }
- private void startMyImageView() {
- myView = (MyImageView) this.findViewById(R.id.myView);
- Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(),
- R.drawable.flower_001);
- myView.setBitmap(bitmap);
- myView.setOnTouchListener(this);
- myView.invalidate();
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- Log.i("touch event","touch x = " + event.getX());
- switch (MotionEvent.ACTION_MASK & event.getAction())
- {
- case MotionEvent.ACTION_DOWN:
- mode = TRANSLATION_MODE;
- myView.setStartPoint(new Point((int)event.getX(), (int)event.getY()));
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_OUTSIDE:
- case MotionEvent.ACTION_UP:
- mode = NULL_MODE;
- myView.savePreviousResult();
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- mode = SCALE_MODE;
- myView.setInitDistance(calculateDistance(event));
- break;
- case MotionEvent.ACTION_MOVE:
- if(mode == SCALE_MODE)
- {
- float dis = calculateDistance(event);
- myView.zoomIn(dis);
- }
- else if(mode == TRANSLATION_MODE)
- {
- myView.setMovePoint(new Point((int)event.getX(), (int)event.getY()));
- }
- else
- {
- Log.i("unknow mode tag","do nothing......");
- }
- break;
- }
- myView.invalidate();
- return true;
- }
- private float calculateDistance(MotionEvent event) {
- float dx = event.getX(0) - event.getX(1);
- float dy = event.getY(0) - event.getY(1);
- float distance = (float)Math.sqrt(dx*dx + dy*dy);
- return distance;
- }
- }
三:运行效果如下
Android中实现Bitmap在自定义View中的放大与拖动相关推荐
- 《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进
在<Android开发艺术探索>一书中自定义View一节中提到了关于一个类似横向滑动List的自定义ViewGroup:HorizontalScrollViewEx.如果你使用过的话就会发 ...
- Android 自定义View中坐标点的理解学习(一)
本文主要是记录学习自定义view中看到的资料,为了方便记忆做了保存整理便于自己学习也方便其他Android开发爱好者学习,参考资料看底部链接. 一.getLocationInWindow和getLoc ...
- 安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例
安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例,图片压缩处理逻辑 本文旨在介绍自定义View的实现 ...
- Android软件开发之盘点自定义View界面大合集(二)
Android软件开发之盘点自定义View界面大合集(二) - 雨松MOMO的程序世界 - 51CTO技术博客 雨松MOMO带大家盘点Android 中的自定义View界面的绘制 今天我用自己写的一个 ...
- android绘制心形_Android自定义View系列(一)——打造一个爱心进度条
写作原因:Android进阶过程中有一个绕不开的话题--自定义View.这一块是安卓程序员更好地实现功能自主化必须迈出的一步.下面这个系列博主将通过实现几个例子来认识安卓自定义View的方法.从自定义 ...
- Android 雪花飘落动画效果 自定义View
在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天.每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不 ...
- Carson带你学Android:源码解析自定义View Draw过程
前言 自定义View是Android开发者必须了解的基础 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化 等 今天,我将全面总结自定义View ...
- android 仿360浮动,Android仿360悬浮小球自定义view实现示例
Android仿360悬浮小球自定义view实现示例 效果图如下: 实现当前这种类似的效果 和360小球 悬浮桌面差不错类似.这种效果是如何实现的呢.废话不多说 ,直接上代码. 1.新建工程,添加悬浮 ...
- Android仿IOS滑动关机-自定义view系列(6)
Android仿IOS滑动关机-自定义view系列 功能简介 GIf演示 主要实现步骤-具体内容看github项目里的代码 Android技术生活交流 更多其他页面-自定义View-实用功能合集:点击 ...
最新文章
- java dbcp_Java dbcp连接池基本使用方法详解
- pulsar 容量_Pulsar 负载均衡设计
- mysql5.6+master+date_MySQL5.6的4个自带库详解
- python变量类型声明_python变量声明及简单数据类型
- 增广最小二乘法 matlab 东南大学,各种最小二乘法总结(算法+matlab源码)
- 正则匹配字符串有则替换无则添加;用正则实现添加和替换字符串,原字符串中包含某字段就替换(覆盖),不包含某字段就添加!
- BZOJ3668:[NOI2014]起床困难综合症(贪心)
- macOS安装配置rzsz(附博主安装过程中的错误解决)
- LM2596降压DCDC芯片详解
- SolidWorks机箱机柜钣金3D模型图档
- java打开dex文件_dex文件反编译工具(Dedexer)
- keepalived 单播模式
- 【华人学者风采】杨义 悉尼科技大学
- 从少年变成老男孩-----韩寒与郭敬明的十年
- SQL中OVER(PARTITION BY)详解
- 新年贺卡php,PS设计2016猴年大吉新年贺卡
- 简单理解SpringMVC的三层结构顺序MCV以及ModelAndView的使用
- 华硕主板更改UEFI安全启动项
- RISC-V扩展指令示例
- neu坐标系和xyz坐标系转换_航测必知的坐标系详解和转换关系
热门文章
- 【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )
- 【Groovy】Groovy 方法调用 ( Groovy 构造函数中为成员赋值 | Groovy 函数的参数传递与键值对参数 | 完整代码示例 )
- 【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )
- 【Windows 逆向】OD 调试器工具 ( 推荐汉化版的 OD 调试工具 | 吾爱破解专用版Ollydbg | 备选工具 )
- 【Windows 逆向】CheatEngine 工具 ( CheatEngine 简介 | 使用 Lazarus 编译 CE 源码 | CheatEngine 相关文档资料 )
- 【错误记录】执行 Python 程序报错 ( NameError: name ‘reload‘ is not defined )
- 【Android NDK 开发】JNI 方法解析 ( JNIEXPORT 与 JNICALL 宏定义作用 )
- 深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数
- CodeForces - 833B The Bakery
- java web学习笔记-jsp篇