昨天同事跟我说了一个bug,让我整了一天,从昨天下午这个时间到今天下午这个时间,哎... 大家都懂的
首先我来说一个bug,我用surfaceView来实现自定义拍照的功能,先来张在genymotion运行的截图
大体就是这个样子,相机上面附上一张图片加上几个按钮,来实现拍照的基本需求,但是拍完照片的时候在小米4手机上拍完之后展示的照片非常模糊,就像是被压缩的很厉害,于是就开始来解决这个bug
下面我做一个小的Demo,来说一下解决这个bug走的弯路,记录一下这里的一些功能的实现,以便以后遇到类似的需求直接拿来用,直奔主题
这个功能的实现我是基于EasyCamera--更简单更灵活的相机应用编写 这篇博客来实现我的功能的,谢谢这个博主
EasyCamera.PictureCallback mPictureCallback = new EasyCamera.PictureCallback(){
@Override
public void onPictureTaken(byte[] data, EasyCamera.CameraActions actions) {
}
};
点击拍照按钮执行这个方法,得到onPictureTaken()这个方法的回调,我们想要得到的数据就存在第一个参数这个数组中,由于三星Note 3手机拍的照片正常,所以我就把这个得到的大小和小米4手机拍的照片得到的大小进行比较,结果我发现这个两个大小差不多,说明小米4系统没有进行特别大的压缩,这里我走的弯路,我在这里卡住了,我google了网上遇到类似的问题,其中有一个的做法是他先是把拍的照片直接存到本地,然后在需要展示照片的时候,获取本地保存的照片,进行展示,但是我也尝试这种做法结果可想而知,然并卵!为什么呢?想必一猜就知道,人家遇到的情况跟我不一样,先说说他的情况,他是调用系统的照相机,拍完照之后把数据用Intent返回到上一个页面,由于图片太大,所以系统进行了压缩Android相机和本地相册获取图片显示并保存到SD卡 这是那篇博文,当然这种情况的这种做法还是很合理的,拍完之后接着保存到本地,需要的时候从本地读取,这样的话保证图片的清晰度和分辨率,自行再进行压缩. 但是我需求不是这样的,我得到了data保存起来,展示的时候接着读取,都是在一个页面中进行的,这不是典型的脱掉裤子放屁,多此一举嘛,当时也是没有多想,死马当活马医!这是我的第一次个弯路然后我就想,既然三星手机得到data和小米4手机得到的data大小差不多,说明不能onPictureTaken()这个方法上面找,得往下面找这个问题的原因
/**
* 图片旋转
*
* @param b
* @return
*/
private Bitmap imageRotate(byte[] b, int angle) {
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
Matrix matrix = new Matrix();
if (cameraPosition == 1) {
matrix.postScale(-1, 1); // 镜像水平翻转
}
matrix.postRotate(angle);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
return bitmap;
}
如果对得到的照片不进行处理直接展示的话,会展示出一个镜像旋转的图片,这个原因不太了解,有了解的童鞋可以说一下,所以我就进行镜像水平翻转,最后得到一个bitmap进行展示,我就使劲看这个方法有没有牵扯到压缩,是不是因为我的这块代码导致我图片变得不清楚,分辨率特别低,就在这时候我发现了原因,在一次调试的时候我发现这个bitmap(上面代码块红色)的height和width特别小,比用系统相机小很多
可以看一下我debug的结果,这就是我上面代码块红色的bitmap的width和height的值,这么小,我靠!原因终于找到了,分辨率这么小,但是我让图片强制拉大展示,当然看到很模糊了!
现在就是就是怎么解决这个bug了,既然原因找到了,那么解决就相对简单了.但是,这个设置宽高我也是费了很大功夫,首先我该对谁进行宽高的处理bitmap or camera ? 我先强制设置了得到的bitmap的宽高,让他的宽高跟手机分辨率一样,但是然并卵,既然bitmap的宽高设置没有用,那么我就继续往上找原因,我看到了这个EasyCamera,可不可以在这个地方做文章?我就google了一下,可不可以在拍照的时候进行尺寸的设置,结果果然有这样的方法
// Parameters parameters = mEasyCamera.getParameters();// 获取相机参数集
// List SupportedPreviewSizes =
// parameters.getSupportedPreviewSizes();// 获取支持预览照片的尺寸
// Size previewSize = SupportedPreviewSizes.get(0);// 从List取出Size
// parameters.setPreviewSize(previewSize.width, previewSize.height);//
// 设置预览照片的大小
// List supportedPictureSizes =
// parameters.getSupportedPictureSizes();// 获取支持保存图片的尺寸
// Size pictureSize = supportedPictureSizes.get(0);// 从List取出Size
// parameters.setPictureSize(previewSize.width, previewSize.height);//
// 设置照片的大小
// mEasyCamera.setParameters(parameters);
这一堆代码,果然,可以设置,parameters.getSupportedPreviewSizes()和parameters.getSupportedPictureSizes()返回的都是一个list 存放的Size类型的数据,就是它支持的宽高的尺寸,结果这个list的大小真不小,而且不同的手机这个list的大小也是不一样的,这样的话我就有思路了,我把拍照时的尺寸设置成屏幕的分辨率就可以了啊,但是问题又出来了,有个问题我不确定,这个list里面一定有本手机屏幕的分辨率吗?我估计应该有,但是不确定,所以这个地方我要做点特殊处理.首先,我遍历这个list找到跟屏幕分辨率相等的那个size然后返回这个下标,如果没有这个分辨率的话,我就取出这个list中间的一个size的下标进行返回(因为我发现这个list里面的size它是由从大到小的顺序进行排序的,所以我选择了一个大胆的想法,选取中间一个size)
/**
* 获取拍照之后的尺寸
*/
private int getPictureSize(List sizes) {
// 屏幕的宽度
int screenWidth = MyApplication.getMYIntance().widthPixels;
int index = -1;
for (int i = 0; i < sizes.size(); i++) {
if (Math.abs(screenWidth - sizes.get(i).width) == 0) {
index = i;
break;
}
}
// 当未找到与手机分辨率相等的数值,取列表中间的分辨率
if (index == -1) {
index = sizes.size() / 2;
}
return index;
}
这是代码,设置了EasyCamera的parameter,但是问题又来了,这段代码写在何地?刚开始的时候,我把这段代码写在了SurfaceHolder的surfaceCreated和surfaceChanged方法中,这样,进入这个页面第一次拍照,得到的照片很清晰,但是当我点击取消,再次拍照的时候图片就变模糊了,后来我把这段代码写在了点击拍照的那个按钮的点击事件上就OK了.
到此为止,这个bug就算完成了,后面对图片的压缩等一系列的操作就根据自己的需求进行了由于时间很紧,写得挺仓促,博客的格式基本没有,大家凑合着看吧,我主要是想把我这近一天的bug解决经历分享一下,而并不是给出这个bug的解决方案,主要是说一下思路和我当时遇到的坑和走的弯路,思路最重要!
当然还有几处不明白的地方,比如图片为什么自动翻转了,这个还需要继续努力寻找原因
源码下载 代码只是简单的功能实现