android:照片涂画功能实现过程及原理
这个功能可以帮你实现,在图片上进行随意的涂抹,可以用于SNS产品。
绘图本身很简单,但是要实现在图片上指定的部分精确(位置,缩放)的绘图,就有点麻烦了。
下面讲讲实现过程及原理:
UI构图
这个UI,看似简单,还是有点复杂的,下面需要一个底图,上面再来一个透明的图片控件,画图的时候要同步两个控件的变形。
UI层次简图
为什么,需要上面一个Canvas Image与Back保持一致?因为,Canvas Image会被画到Canvas上,它是Canvas的宿主,即ImageView被变成了一块画布,原来ImageView上的内容会被擦除。如果只有Back Image那么一旦开启画布,你什么也看不到。
神奇的Matrix
ImageView控件是常用的Android控件之一,主要用于图片展示。基本上所有的高级语言中,都有类似控件。但是,有一样东西让他化腐朽为神奇,那就是Matrix。有了Matrix我们就可以实现图片平移、放大、旋转、扭曲等常用的特效。Matrix本身是一个9*9的矩阵,里面存放的是平移坐标、放大系数、sin/cos角度值。我们可以通过getMatrix()来获取一个IV的矩阵,或者通过setMatrix()来设置它的值。
上面的东西拿来有什么用?试想一下,当我们打开相册,查看一张照片的时候,可以通过触摸,平移或者放大图片。我们,要在上面绘图,先把Canvas Image 变为Canvas,但是,Canvas Image没有经过变化。必须,至少确保两个Img控件拥有相同的变形,否则无法对齐画的坐标点。这个时候,要么当Back Image变的时候,Canvas Image立即同步操作,要么,最画到canvas上的时候,同步变形。前一种方案是没有必要的,果断使用后一种。这个时候你就要问,怎么得到IV的变形参数?IV提供了一些方法来单独的获取和设置某种变形,当时折腾了很久,不但繁琐,而且达不到效果。这时候,上面的Matrix就派上用场了。当时,可没人这么愉快的告诉我这个。
坐标映射
上面完成了图形的变换,现在终于可以再上面作画了。但是一画,你就会发现一个问题,画不到指定位置上。这是什么问题呢?坐标系偏移。(0,0)点默认为屏幕的左上角。但是,想一下当我们的图片不满一个屏幕,很小的时候,Canvas的坐标系在什么位置?我在屏幕(0,0)坐标画一个点,canvas上就会出现一个点,即使两者的位置相差很远。
这个时候,我们需要将两个坐标系进行映射,通过偏移对齐坐标系。偏移多少?这时候该使用矩阵的translate值了。这样我们就可以知道图片在坐标系上的偏移了,随边你怎么移动坐标都能准确对齐。
private HashMap getImageViewIneerSize(ImageView iv){
HashMap size=new HashMap();
//获得ImageView中Image的变换矩阵
Matrix m = iv.getImageMatrix();
float[] values = new float[10];
m.getValues(values);
//Image在绘制过程中的变换矩阵,从中获得x和y方向的缩放系数
float sx = values[0];
float sy = values[4];
//计算Image在屏幕上实际绘制的宽高
size.put("scaleX", 1/sx);
size.put("scaleY", 1/sy);
size.put("offsetX", values[2]); //X轴的translate的值
size.put("offsetY",values[5]);
return size;
}
其中width=backImage.getDrawable().getBounds().width();
你会发现有个scaleX,这是干什么的?我们假设现在图片经过缩放后的宽度恰好等于屏幕宽度,图片的实际宽度是960px。但是我们在X=480px的地方画一个点,这个点应该显示在图片的什么地方呢?我们的意图是要在图片的最后面,即X=960px的地方画一个点,但是现在却跑到了480处,明显不满足要求。这时,就需要乘上上面的scaleX了。
画线的最终代码:
/*根据两点坐标,绘制连线
*startX、stopX 为触摸事件开始、结束的地方
*offsetX,为图片在X轴的位移值
*scaleX,为图片在X轴的缩放值的倒数
*/
if((startY-offsetY)>=0&&(stopY-offsetY)>=0)
canvas.drawLine((startX-offsetX)*scaleX, (startY-offsetY)*scaleY, (stopX-offsetX)*scaleX, (stopY-offsetY)*scaleY, pen);
【注】
ImageView的实际大小等于屏幕的大小,Canvas的实际大小由图片实际大小决定。
ImageView的宽高很容易取得,但是它里面的图片是变过形的,怎么获取它的当前大小呢?用(原始大小*缩放系数)。
合并
最后一步就是将两个图层合并为一张图片。参考代码如下:
/**
* 合并两张bitmap为一张
* @param background
* @param foreground
* @return Bitmap
*/
public static Bitmap combineBitmap(Bitmap background, Bitmap foreground) {
if (background == null) {
return null;
}
int bgWidth = background.getWidth();
int bgHeight = background.getHeight();
int fgWidth = foreground.getWidth();
int fgHeight = foreground.getHeight();
Bitmap newmap = Bitmap.createBitmap(bgWidth, bgHeight, Config.ARGB_8888);
Canvas canvas = new Canvas(newmap);
canvas.drawBitmap(background, 0, 0, null);
canvas.drawBitmap(foreground, (bgWidth - fgWidth) / 2,
(bgHeight - fgHeight) / 2, null);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
return newmap;
} //end of combineBitmap
通过Canvas来合并和改变Bitmap的大小,由于两个图层的大小、位置完全一致,故坐标对齐(0,0)点就可以了。
如果,没有前面的工作,你是很难精确的进行图片合并的。
原创作品,转载请注明出处。如有不当之处,望各位读者指正。
android:照片涂画功能实现过程及原理的更多相关文章
- Android内核三大核心功能之一AMS内部原理
上面类是AmS的全称,另外两大核心功能是WindowManagerService.java和View.java AmS提供的主要功能: 统一调度各应用程序 内存管理 进程管理 AmS中定义了几个重要的 ...
- android开发之画图版
android开发之画图版 一.新的开始,也是新的挑战: 开始学习java,除了刚开始的时候有些难,觉得难有些晕,慢慢接触之后也就挺好的了, 学习了4天的安卓开发,完成了一个小程序,收获还是不小的:有 ...
- Android 实现图片画画板
本文主要讲述了Android 实现图片画画板 设计项目布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk ...
- Android应用程序的Activity启动过程简要介绍和学习计划
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6685853 在Android系统中,Activ ...
- Android使得手机拍照功能的发展(源共享)
Android系统调用手机拍照功能有两种方法来直接调用手机自带摄像头还有一个就是要当心自己的节拍. 例Camera360 强大的一个在每个操作系统都有一个手机摄影软件:您可以捕捉不同风格,不同特效的照 ...
- Android Camera2 预览功能实现
1. 概述 最近在做一些关于人脸识别的项目,需要用到 Android 相机的预览功能.网上查阅相关资料后,发现 Android 5.0 及以后的版本中,原有的 Camera API 已经被 Camer ...
- Android集成微信分享功能应用签名生成方法及分享不生效的问题
通过友盟sdk集成微博.微信.qq等分享功能时,微博和qq很顺利,但在做微信集成时一直不成功.主要问题还是之前在微信开放平台申请创建移动应用时,对应用签名没有填写对,走了很多弯路现总结出来,加深记忆避 ...
- Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...
- Android Webview实现文件下载功能
在做美图欣赏Android应用的时候,其中有涉及到Android应用下载的功能,这个应用本身其实也比较简单,就是通过WebView控制调用相应的WEB页面进行展示.刚开始以为和普通的文件下载实 ...
随机推荐
- *[topcoder]LCMSetEasy
http://community.topcoder.com/stat?c=problem_statement&pm=13040 DFS集合全排列+LCM和GCD.但事实上,有更简单的算法,列在 ...
- Visual Studio 2010快捷键大全
http://www.cnblogs.com/zhuiyi/archive/2011/10/26/2224941.html [窗口快捷键]Ctrl+W,W: 浏览器窗口 Ctrl+W,S: 解决方案管 ...
- Django 大文件下载
django提供文件下载时,若果文件较小,解决办法是先将要传送的内容全生成在内存中,然后再一次性传入Response对象中: def simple_file_download(request): # ...
- eclipse(STS,myeclipse)老是报ThreadPoolExecutor$Worker.run()
资料地址:http://stackoverflow.com/questions/6290470/eclipse-debugger-always-blocks-on-threadpoolexecutor ...
- Windows 7下配置JDK环境变量
安装jdk1.8版本(下载链接:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) ...
- jQuery--隐藏事件
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 购买使用Linode VPS必须知晓的十个问题
Linode是国外非常著名的VPS商之一,目前在国内站长圈中备受推崇.有许多站长已经购买了Linode VPS,但是部分站长由于中英语言不通,对Linode的政策不了解,从而造成了许多不必要的损失.本 ...
- git cheat sheet,git四张手册图
- maximum-gap(经过了提示)
下面的分桶个数做的不太好,原来的解法是用的 int gap = (big - small) / vlen; if (gap == 0) { gap = 1; } 下面是现在的Java解法: packa ...
- C#.NET U盘插拔监控
[1]涉及的知识点 1) windows消息处理函数 ? 1 protected override void WndProc(ref Message m) 捕获Message的系统硬件改变发出的系统消 ...