启动拍照
如果只是拍照,调起 android 自身的拍照界面就行,两行代码。
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, TAKE_PIC_CODE);
拍完照之后的回调会在 onActivityResult 方法中。但是如果要指定拍照后照片的存储,可附加上如下代码
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
指定了照片的存储是位置在 uri。通过源码可以知道,要是我们指定了照片文件,那么在拍照回调里 intent 就不再返回照片数据了,而是帮我们写在了指定的路径下面了。所以我们只好记住路径,拍照成功回调的时候,自己再去取一下文件。网上查到的资料是这样解释源码的
if (mSaveUri != null) {
OutputStream outputStream = null;
try {
//这里就是将拍好的照片写到了我们指定的路径下面,返回的时候就没有再把图片数据返回了
outputStream = mContentResolver.openOutputStream(mSaveUri);
outputStream.write(data);
outputStream.close();
setResult(RESULT_OK);
finish();
} catch (IOException ex) {
// ignore exception
} finally {
Util.closeSilently(outputStream);
}
} else {
Bitmap bitmap = createCaptureBitmap(data);
setResult(RESULT_OK,new Intent("inline-data").putExtra("data", bitmap));
finish();
}
照片旋转
图片旋转是根据图片信息中的角度信息得出来是否要进行旋转吧,这里还没太深入探究,如果要进行旋转处理可以进行如下操作:
ExifInterface inface = null;
inface = new ExifInterface(path);//path 是图片文件的存储路径
int rotation = inface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);//这里就是读取照片中的信息,第一个入参说明了要读取的数据是水平方向角度吧,第二个入参不是很清楚,我看网上这么用,这里还可以填别的值,比如 ORIENTATION_NORMAL 得到的值好像没什么区别,具体不清楚。
int degree = 0;
//这里就是根据读取出来的水平方向角来赋值我们想要调整的角度,这里没有 360,因为 360 就是 0 嘛也就没有了。
switch(ratation){
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
Matrix matrix = new Matrix();
matrix.setRotate(degree);
Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeigth(), matrix, true);//这里就是对图片 bitmap 通过矩阵进行了角度旋转,得到的 result 就是旋转后的图片了
图片压缩
图片的压缩可以有两种方式,一种是解码时,通过一些解码参数在解码的时候就将图片进行了压缩或者其他处理;另外一种是通过 bitmap 自身的 compress 方法进行 quality 的压缩。那么通过解码方式的压缩处理如下:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//这个属性标识了解码时是否只是解码文件尺寸而不载入内存
options.inPreferredConfig = Bitmap.Config.RGB_565;//这个属性标识了解码使用的色值,默认是 ARGB_8888,这个属性保留了 alpha 值,且每个占位 8 位,所以一个像素要 4 个字节的空间。如果是 RGB_565,去掉了 alpha 透明值,总共需要 2 个字节就可以了,还原度上看去和默认差别不大,所以如果对透明度没要求的可以用 RGB_888,其他属性就暂不讨论。
options.inSampleSize = sample;//这个属性就是修改尺寸的,压缩的关键。通常我们会指定一个压缩尺寸,比如 512 * 512,那么原图和要求的尺寸就会有一个比例,sample 值就是这个压缩比例。如果 sample < 1,压缩比例就默认为 1,如果 sample 是个奇数,那么压缩比例会是小于 sample 的最大的偶数,因为 inSampleSize 的值是以 2 倍数出现的。
options.inJustDecodeBounds = false;//之前设置会 true 的解码是为了先获取原图尺寸来计算压缩比例,等到真正解码时,要还原为 false,这样才能将图片载入内存
Bitmap bitmap = BitmapFactory.decodeFile(path, options);//path 就是文件来自哪里的路径。
通过 compress 方法压缩的处理如下:
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
//首先先解码图片文件,这里 options 的属性都是默认的
ByteArrayOutputStream outStream = new ByteArrayOutputStream();//准备用来存放压缩图片的位数组
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream);//这里就是压缩关键,第一个入参是选择以哪种图片格式进行压缩,jpeg 是不带透明度的,所以如果以 jpeg 格式进行压缩,那 options.inPreferredConfig 就可以设置为 RGB_565,如果是 png,那质量不会损失,第二个入参是无效的。第二个入参就是质量数 0~100 之间,值越大质量越好。
ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
Bitmap result = BitmapFactory.decodeStream(inStream, null, null);//解码数据流的方式,将图片解码
两种方法比较来看,压缩比较显著的还是通过压缩尺寸方式,因为这是最直接的改变了图片的宽高,也就改变了像素点的个数,自然就小了。质量压缩的压缩效果没有压缩尺寸的方式来的好,具体是压缩什么不太清楚,但质量一但调到比较小的时候,失真就比较严重了,色彩偏差会很明显。所以既然这样,我们在保证质量压缩不失真的条件下,尽可能的压缩尺寸,同时考虑一下每个像素采用的色值,图片输出格式,这样基本可以保证压缩效果。