分享功能相关
微信分享
总结
在本次开发微信分享模块的过程中,也遇到了一些问题,下面的几点是总结:
- 功能需求:分享的时候可以选择原图还是缩略图。而微信API可以支持两种方式分享本地图片:本地url和bitmap。使用本地url进行分享的问题是,微信API会在拿到某个路径下的图片后对其自动进行压缩,所以就无法达到功能的要求:可以发原图或者缩略图。在这里,最终选择的是发bitmap,区别就是,原图发送的是大尺寸的bitmap,而缩略图则是发送小尺寸的bitmap。
- 使用微信API发送图片,
WXMediaMessage
类中的thumbData
要传入缩略图,而这个缩略图大小又不能超过32Kb,所以要把这个缩略图大小压缩到小于32Kb才可以。 - 微信官方文档说分享图片或者文件的限制是10Mb,但是在实测中发现,图片大于2Mb,就会出现调不起来微信分享的情况。可能也是因为图片太大,系统生成bitmap并且进行压缩这样的耗时操作,会导致性能吃紧的情况。所以在生成bitmap时都会将从路径中拿到的图片进行压缩,操作的方式是改变
BitmapFactory.Options
这个类中的inSampleSize
参数。 - 上面一点说到
inSampleSize
这个参数,在实际开发过程中,发现只有当参数为2的方幂时(1,2,4,8,依次往下),才会在图片尺寸上有变化。- 当为
1
时,不压缩 - 当为
2
时,压缩1/4左右(例如:500多Kb压缩到100多Kb) - 当为
3
时,压缩程度与2
时相同 - 当为
4
时,再压缩1/4左右,之后5,6,7的压缩比例都与4相同 - 当为
8
时,再压缩1/4左右,之后再以此类推
- 当为
- 微信分享的回调机制是在
WXEntryActivity
重写onResp
函数来处理微信分享的返回信息,而WXEntryActivity
有必须放在包名下面的wxapi
这个文件夹里。 - 另外微信分享对于应用签名也有者严格的限制,用不同的keystore打包会生成不同的应用签名,而微信分享只认准一个应用签名,所以如果
debug
和release
的keystore是不同的,则需要在应用正式上线前,将release版的签名再次提交审核,通过之后才能进行微信分享。 - 关于微信分享的其他一些细节,有一篇网上的博客也写得很详细,可以参考: Android 微信SDK分享功能中的最全过程步骤分析
图片压缩部分代码
|
|
上面一段代码是将一个bitmap进行压缩后,再用IO流返回一个byte数组传入WXMediaMessage
中的thumbData
参数。
QQ分享
总结
QQ分享的限制没有微信那么多,对于图片的大小也没有特殊限制,分享本地图片的方法也只有一种:本地url。只要给QQ的API一个本地url路径,它就会去那个路径下找图片,然后发送。但是MME相机出于对安全的考虑,相机里面的照片存放路径是应用私有的,如果传这个url路径给QQ分享,它的API没有权限去拿到这个路径下的图片,所以在开发过程中采用的方案是:
- 进行QQ分享时,先获取到当前图片解密后的应用下路径,并生成bitmap
- 将bitmap写入到系统公有的一个路径里,为了安全考虑,此时要将保存图片的文件夹设置为不可见,采用在文件夹名签名加“.”的方式
- 传入该公有的路径给QQ分享API,完成QQ分享操作
- 在QQ分享的回调函数
IUiListener
类里面将刚刚保存的图片删除(不管有没有分享成功),也是为了安全考虑
保存到本地
总结
保存到本地功能有一个地方需要注意的是,在保存完成后,需要发个广播,来通知系统更新刚保存到本地相册的图片,代码是:
Android里Touch事件机制
问题描述
在本版开发中,有两个针对相机的功能:点击聚焦和双指缩放变焦。这两个功能都是和Touch事件相关的操作,开始的思路是对CameraPreview
对象设置onTouchListener
监听,然后在里面的onTouch
方法实现双指缩放逻辑,但是因为原来的代码在CameraPreview
类中重写了onTouchEvent
方法,这样就导致了只能实现双指缩放而不能实现点击聚焦。针对这个问题,查了网上对于两者区别的比对,现在记录在下面,以后想起来可以翻一下。
onTouchListener中的onTouch方法与onTouchEvent的区别
这里只写结论,分析过程可以参考android onTouchEvent和setOnTouchListener中onTouch的区别或者阅读View
类里面的dispatchTouchEvent
方法。
onTouchListener
优先级高于onTouchEvent
- 系统会先执行
onTouchListener
中的onTouch
方法,如果onTouch
方法返回true
,则系统会判断此次Touch事件已经执行,也就不会再去执行onTouchEvent
方法;只有当onTouch
返回false
时,才会继续执行onTouchEvent
方法
解决方案
由于原来代码里面的点击聚焦功能写在CameraPreview
类里,为了代码能统一起来,所以准备把双指缩放的功能也加入到onTouchEvent
里面进行处理,下面是一些注意点:
onTouchEvent
方法需要返回true
才行,否则系统无法处理此次Touch事件,直观的感受就是只会执行MotionEvent.ACTION_DOWN,而不会去执行其它的一些手势操作MotionEvent.ACTION_MASK
作用是识别多指操作onTouchEvent
中的MotionEvent.ACTION_DOWN
+MotionEvent.ACTION_UP
动作就等于onClick
方法执行了一次
其它
另外一个遗留问题是,在进行了点按聚焦后,相机的连续自动对焦功能失效,最终的解决方案是:
- 在
CameraPreview
里的touchFocus
方法里重新设置连续自动对焦
两个在UI线程(主线程)更新UI的例子
导入照片显示进度的Dialog
- 需要记录当前已经解密完成的照片数量,也要拿到总共需要导入的照片数量
- 在开始记录的时候,用
handler
发送空消息,在UI线程里面显示dialog,并且时时更新导入进度 - 在导入完成时,用
handler
发送ON_IMPORT_COUNT_END
空消息,在UI线程里dismiss
掉dialog
主要代码
在TaskCallback<Image>
对象的onStateChange
方法里记录导入加密的情况:
显示Toast
问题描述
这里出现的问题是:在完成马赛克操作,点击保存进行加密保存的时候,这个过程实在子线程里进行的,会有一个onEncrypt
的回调函数,刚开始的时候,把照片保存成功的Toast直接写在这个回调函数里,后面发现这样写会报错,然后代码也就不会往下执行,也无法进行页面的跳转。
解决方案
在onEncrypt
回调函数里面用handler发送空消息,然后重写handler里面的handleMessage
方法,进行Toast显示以及页面跳转等操作。
图片保存路径
问题描述
在进完马赛克操作后,用户点击进行保存,刚开始的保存方式是直接调用GPUImageView
类中的saveImage
方法,而安全相册里面的照片都是保存在应用的私有路径里,所以刚开始的时候,在安全相册里一直看不到保存的马赛克后的图片。
解决方案
参照拍照完的照片加密保存的方式,对马赛克图片也同样进行加密保存到应用的私有路径里。
几种图片路径的区别
- 内部存储(Internal Storage)
context.getFilesDir()
是将文件保存在以应用报名命名的私有目录里context.getCacheDir()
是将文件保存在应用的私有缓存目录里
- 外部存储(External Storage)
Environment.getExternalStoragePublicDirectory(String type)
是将文件保存在公有目录下,并且应用卸载后数据不会被删除context.getExternalFilesDir(String type)
是将文件保存在外部存储的根目录下,数据会随着程序被卸载而删除
通过路径获取图片的Exif信息
问题描述
Android系统由于是开源的,不同厂商会针对原生系统做自己的优化,对于拍照应用来说,有些厂商会对图片的旋转进行处理,而有些则不会,这就造成在有些机型上,拍摄的照片在显示时会翻转过来。
解决方案
通过路径获取图片的Exif信息,得到图片的旋转角度,然后按照这个角度对图片进行翻转操作,让图片在显示的时候重新正回来。
获取图片Exif信息代码
|
|
获取degree之后,再进行翻转操作,代码如下:
UI相关
Button点击效果
- 如果想要Button里面的字有点击效果,则需要设置
textColor
属性,用selector
来设置点击时候的颜色 - 如果想要Button的背景有点击效果,需要设置的是
background
属性
ImageButton和ImageView
ImageButton
的图片默认会放在中心位置,虽然可以用scaleType
属性设置放在左边、中间还是右边,但是没法设置padding
属性,让ImageButton
在wrap_content
情况下点击面积更大- 而
ImageView
可以设置padding
属性让点击面积更大
设置强制横屏的代码
|
|
如何阻止系统进行截屏操作
|
|
马赛克功能
马赛克功能实现机制
bmBaseLayer
层,可以理解为直接拿原始图片作为最底下那一层bmMosaicLayer
层,可以理解为在bmBaseLayer
上面一层,最终的马赛克图像是由两层叠加生成一个新的bitmap进行保存后形成的- 对于
bmMosaicLayer
这一层来说,实际不是对它进行直接操作,而是在它上面进行画图操作,其中有一层是bmCoverLayer
,在代码里有三种可选:GridMosaic
、ColorMosaic
和BlurMosaic
,在我们这里选择的是GridMosaic
。具体实现方式是:将图片分成一格一格,取左上角那个点的像素颜色值,然后设置成那个格子的颜色,最后进行画图 - 还有一层就是
bmTouchLayer
,是我们手进行操作的那一层,最终两层合在一起,是bmMosaicLayer
- 对于
Bug1:橡皮擦操作后的位置无法再次进行马赛克操作
问题描述
由于updatePathMosaic
的机制是实时绘制马赛克和橡皮擦操作的路径集合,并且橡皮擦的绘制在后面,所以当两者路径重合时,后绘制的橡皮擦会覆盖马赛克,所以在观感上就会造成橡皮擦操作后的位置无法再次进行马赛克操作的感觉
解决方案
bmTouchLayer
从局部变量变成全局变量,由它来保存马赛克和橡皮擦操作后的图层,而之前是由一个Path
集合来分别保存马赛克和橡皮擦的路径- 每次进行操作之前,清空Path集合里面的路径
Bug2:某些小图片上划过的Path和Grid宽度很大
问题描述
尺寸较小的图片进入马赛克页面后会进行拉伸处理,之前对于Path和Grid的宽度都是采用一个固定值,这样就会造成小图上进行马赛克处理时划过的Path和Grid宽度很大
解决方案
- 对于Grid的宽度问题,根据图片宽度设置几个Level,每个Level固定每行Grid的个数,这样也就不会造成小图片每行只有一两个Grid的情况,但是对于Grid的宽度也有一个极限值是50,因为如果不做处理,非常大的图片一行也只有20个Grid,就会造成每个Grid的宽度特别大
- 对于Path宽度的问题,小图设置宽度较小,大图设置宽度较大
主要代码
|
|
Bug3:马赛克页面图片显示比例失真
问题描述
原来的方案:没有按照图片原来宽高比的尺寸进行压缩或者拉伸,这样子对于有些图片显示出来就会有比例失真的问题
解决方案
- 核心思路:图片无论如何都要按照原有图片的宽高比来进行缩放显示
- 具体操作1:当宽>高时,让宽充满View,再按照原始宽高比计算出新的高的值
- 具体操作2:当高>宽时,让高充满View,再按照原始宽高比计算出新的宽的值,
例外情况
:当计算出的新宽值大于View的宽值时,改变方式,让宽充满View,再按照原始宽高比计算出新的高的值
主要代码
|
|