实现可缩放的马赛克控件---Android
2015-12-14 17:06
477 查看
需求:实现可以缩放、移动和打马赛克的控件。
由于之前对图片处理的经验很缺乏,所以拿到需求的第一步我就从github上面找相关的项目。
然后,就找到了这个项目:ProMosaic
这个项目有两个痛点:
1.加载图片未处理尺寸,当尺寸过大时,会内存溢出(小问题)
2.未实现缩放功能
正文:
一、ProMosaic实现马赛克原理分析
首先,在内存中有三层Bitmap:
bmBaseLayer ---- 原图 ,
bmCoverLayer ---- 将整张原图转成马赛克效果
bmTouchLayer ---- 记录手指滑过的路径Path
每次手指滑动时,将手指的Path保存下来,并且将所有Path绘制在bmTouchLayer中,然后将bmCoverLayer和bmTouchLayer合并,合并的算法采用的是Xfermode的DST_IN效果(具体Xfermode请自己查询相关内容)。反正最终的结果就是生成一张马赛克图层bmMosaicLayer,这个图层就是要打马赛克的部分。
然后,将bmMosaicLayer绘制在bmBaseLayer图层上,就实现了最终的马赛克效果。
这个方法对内存的消耗比较大,如果图片压缩不够,在绘制马赛克过程很超级卡。
二、加载图片时压缩图片(优化内存)
项目中的控件名是MosaicView,在设置图片时调用的是setSrcPath(),在这个函数中对要加载的图片进行压缩。可以采用BitmapFactory.Options的inSampleSize来压缩。
相关的资料网上很多,我就不赘述了。
三、添加缩放和移动功能
接下来是最重要的实现缩放的功能了。
实现思路:
1.如何检测缩放手势? -------- 采用ScaleGestureDetector来检测手势多点缩放
2.如何缩放图片? -------- 通过设置Canvas上的绘制区域大小来实现缩放(通过canvas.scale()函数来实现,会出现手势坐标无法转换的问题。)
3.手势坐标如何转换? ------- 这里的尺寸有两类:图片的真实大小 和 图片显示的大小。需要将显示图片的坐标换算到真实图片上的坐标。通过两个尺寸的比例进行换算即可。
图片的真实大小 ---- 是Bitmap建立时的大小,这个尺寸并不直接显示在屏幕上,仅仅存在内存中。
图片的显示大小 ---- 是在Canvas上的绘制大小。调用canvas.drawBitmap(Bitmap bitmap,rect src,rect dst,Paint paint)时,dst这个参数就是设置bitmap在画布Canvas上的绘制区域。dst越大,图片显示就越大。
说到这里,思路就算完成了。
具体实现:
首先,实现手势检测,让控件实现OnScaleGestureListener接口
ScaleGestureDetector.onTouchEvent必须在onTouchEvent()函数中调用才可调用OnScaleGestureListener接口中的函数。
然后,需要初始化Canvas的绘制区域Rect变量。mImageRect和mInitImageRect,第一个是现在的大小,第二个是初始化的大小用于计算缩放比例。
初始化在onLayout中进行。
最后,实现onScale()函数,检测缩放。
当然,还需要对dispatchTouchEvent()进行修改,在单指滑动是进行打马赛克,在多指操作时进行缩放和移动。这里就不多说了。
项目源码下载
由于之前对图片处理的经验很缺乏,所以拿到需求的第一步我就从github上面找相关的项目。
然后,就找到了这个项目:ProMosaic
这个项目有两个痛点:
1.加载图片未处理尺寸,当尺寸过大时,会内存溢出(小问题)
2.未实现缩放功能
正文:
一、ProMosaic实现马赛克原理分析
首先,在内存中有三层Bitmap:
bmBaseLayer ---- 原图 ,
bmCoverLayer ---- 将整张原图转成马赛克效果
bmTouchLayer ---- 记录手指滑过的路径Path
每次手指滑动时,将手指的Path保存下来,并且将所有Path绘制在bmTouchLayer中,然后将bmCoverLayer和bmTouchLayer合并,合并的算法采用的是Xfermode的DST_IN效果(具体Xfermode请自己查询相关内容)。反正最终的结果就是生成一张马赛克图层bmMosaicLayer,这个图层就是要打马赛克的部分。
然后,将bmMosaicLayer绘制在bmBaseLayer图层上,就实现了最终的马赛克效果。
这个方法对内存的消耗比较大,如果图片压缩不够,在绘制马赛克过程很超级卡。
二、加载图片时压缩图片(优化内存)
项目中的控件名是MosaicView,在设置图片时调用的是setSrcPath(),在这个函数中对要加载的图片进行压缩。可以采用BitmapFactory.Options的inSampleSize来压缩。
相关的资料网上很多,我就不赘述了。
三、添加缩放和移动功能
接下来是最重要的实现缩放的功能了。
实现思路:
1.如何检测缩放手势? -------- 采用ScaleGestureDetector来检测手势多点缩放
2.如何缩放图片? -------- 通过设置Canvas上的绘制区域大小来实现缩放(通过canvas.scale()函数来实现,会出现手势坐标无法转换的问题。)
3.手势坐标如何转换? ------- 这里的尺寸有两类:图片的真实大小 和 图片显示的大小。需要将显示图片的坐标换算到真实图片上的坐标。通过两个尺寸的比例进行换算即可。
图片的真实大小 ---- 是Bitmap建立时的大小,这个尺寸并不直接显示在屏幕上,仅仅存在内存中。
图片的显示大小 ---- 是在Canvas上的绘制大小。调用canvas.drawBitmap(Bitmap bitmap,rect src,rect dst,Paint paint)时,dst这个参数就是设置bitmap在画布Canvas上的绘制区域。dst越大,图片显示就越大。
说到这里,思路就算完成了。
具体实现:
首先,实现手势检测,让控件实现OnScaleGestureListener接口
public class MosaicView extends ViewGroup implements ScaleGestureDetector.OnScaleGestureListener{
ScaleGestureDetector.onTouchEvent必须在onTouchEvent()函数中调用才可调用OnScaleGestureListener接口中的函数。
@Override public boolean onTouchEvent(MotionEvent event) { mScaleGestureDetector.onTouchEvent(event); return super.onTouchEvent(event); }
然后,需要初始化Canvas的绘制区域Rect变量。mImageRect和mInitImageRect,第一个是现在的大小,第二个是初始化的大小用于计算缩放比例。
初始化在onLayout中进行。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mImageWidth <= 0 || mImageHeight <= 0) { return; } int contentWidth = right - left; int contentHeight = bottom - top; int viewWidth = contentWidth - mPadding * 2; int viewHeight = contentHeight - mPadding * 2; float widthRatio = viewWidth / ((float) mImageWidth); float heightRatio = viewHeight / ((float) mImageHeight); float ratio = widthRatio < heightRatio ? widthRatio : heightRatio; int realWidth = (int) (mImageWidth * ratio); int realHeight = (int) (mImageHeight * ratio); int imageLeft = (contentWidth - realWidth) / 2; int imageTop = (contentHeight - realHeight) / 2; int imageRight = imageLeft + realWidth; int imageBottom = imageTop + realHeight; mImageRect.set(imageLeft, imageTop, imageRight, imageBottom); mInitImageRect.set(imageLeft,imageTop,imageRight,imageBottom); }
最后,实现onScale()函数,检测缩放。
@Override public boolean onScale(ScaleGestureDetector detector) { float scale = detector.getScaleFactor(); scaleFactor *= scale; if (scaleFactor < 1.0f){ scaleFactor = 1.0f; } if (scaleFactor > 2.0f) scaleFactor = 2.0f; if (mImageRect != null){ int addWidth =(int) (mInitImageRect.width() * scaleFactor) - mImageRect.width (); int addHeight=(int) (mInitImageRect.height()*scaleFactor) - mImageRect.height(); float centerWidthRatio = (detector.getFocusX()-mImageRect.left)/mImageRect.width(); float centerHeightRatio = (detector.getFocusY() - mImageRect.left)/mImageRect.height(); int leftAdd = (int) (addWidth * centerWidthRatio); int topAdd = (int) (addHeight * centerHeightRatio); mImageRect.left = mImageRect.left - leftAdd; mImageRect.right = mImageRect.right + (addWidth - leftAdd); mImageRect.top = mImageRect.top - topAdd; mImageRect.bottom = mImageRect.bottom + (addHeight - topAdd); checkCenterWhenScale(); } //Log.d("Javine","detector's scaleFactor is "+scale); invalidate(); return true; } private void checkCenterWhenScale() { int deltaX = 0; int deltaY = 0; if (mImageRect.left > mInitImageRect.left){ //mImageRect.offsetTo(mInitImageRect.left,mImageRect.top); deltaX = mInitImageRect.left - mImageRect.left; } if (mImageRect.right < mInitImageRect.right){ deltaX = mInitImageRect.right - mImageRect.right; } if (mImageRect.top > mInitImageRect.top){ deltaY = mInitImageRect.top - mImageRect.top; } if (mImageRect.bottom < mInitImageRect.bottom){ deltaY = mInitImageRect.bottom - mImageRect.bottom; } mImageRect.offset(deltaX,deltaY); }
当然,还需要对dispatchTouchEvent()进行修改,在单指滑动是进行打马赛克,在多指操作时进行缩放和移动。这里就不多说了。
项目源码下载
相关文章推荐
- Android 后台线程弹对话框导致程序崩溃(is not valid; is your activity running)
- 网页链接触发原生Intent
- 安卓的各个版本以及对应的API的 Level
- Android学习路线图
- android 同一个ImageView显示不同的图片--->level-list
- Android的onCreateOptionsMenu()创建菜单Menu
- Android技巧:学习使用GridLayout
- 实现快速升级Android SDK
- android应用中去掉标题栏的方法
- Activity瓦解坠落退出效果
- Android消息机制Message消息池
- android摄像头开发
- android AndFix--热补丁框架
- 安卓开发学习之017 自定义控件之属性获取
- Android studio使用中遇到的坑——错误提示没有了(不出现了)
- android actionbar设置返回不指定ParentName
- Xml解析之——Java/Android/Python
- ADB(Android Debug Bridge) 的使用
- Android setImageResource、setImageBitmap、setImageDrawable区别
- TextView实现歌词同步