拍照、选择相片

最近做项目需要用到拍照和选择相册照片,并显示出来imageview 上,然后压缩上传到服务器中,这本是一个非常常见的功能,但对于图片的处理确实一个技术活,稍微不注意会出现oom,图片压缩也要处理的刚刚好,不能浪费用户的流量,也不能过分的压缩使图片失真,这真的不简单,好在开源中国的安卓端app以开源,本人特意从开源中国整理了这个demo,分享给大家。

进入相册选择照片:注意6.0之后要申请运行时权限,即api23。

1
2
3
4
5
6
7
8
9
10
11
Intent intent;
if (Build.VERSION.SDK_INT < 19) {
intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "选择图片"), ImageUtils.REQUEST_CODE_GETIMAGE_BYSDCARD);
} else {
intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "选择图片"), ImageUtils.REQUEST_CODE_GETIMAGE_BYSDCARD);
}

或者拍照:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void toCamera() {
// 判断是否挂载了SD卡
String savePath = "";
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
savePath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/oschina/Camera/";
File savedir = new File(savePath);
if (!savedir.exists()) {
savedir.mkdirs();
}
}

// 没有挂载SD卡,无法保存文件
if (TextUtils.isEmpty(savePath)) {
// AppContext.showToastShort("无法保存照片,请检查SD卡是否挂载");
return;
}

String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String fileName = timeStamp + ".jpg";// 照片命名
File out = new File(savePath, fileName);
Uri uri = Uri.fromFile(out);
//tweet.setImageFilePath(savePath + fileName); // 该照片的绝对路径
mPhotoPath=savePath + fileName;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, ImageUtils.REQUEST_CODE_GETIMAGE_BYCAMERA);
}

在onActivity获取图片信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK)
return;
if (requestCode == ImageUtils.REQUEST_CODE_GETIMAGE_BYSDCARD) {
if (data == null)
return;
Uri selectedImageUri = data.getData();
if (selectedImageUri != null) {
String path = ImageUtils.getImagePath(selectedImageUri, this);
setImageFromPath(path);
}
} else if (requestCode == ImageUtils.REQUEST_CODE_GETIMAGE_BYCAMERA) {
//setImageFromPath(tweet.getImageFilePath());
setImageFromPath(mPhotoPath);


}
}

通过返回的uri获取图片路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 通过Uri获取文件路径
* 支持图片媒体,文件等
* <p/>
* Author qiujuer@live.cn
*
* @param uri Uri
* @param context Context
* @return 文件路径
*/
@SuppressLint({"NewApi", "Recycle"})
public static String getImagePath(Uri uri, Context context) {
String selection = null;
String[] selectionArgs = null;
// Uri is different in versions after KITKAT (Android 4.4), we need to
if (Build.VERSION.SDK_INT >= 19 && DocumentsContract.isDocumentUri(context.getApplicationContext(), uri)) {
String authority = uri.getAuthority();
if ("com.android.externalstorage.documents".equals(authority)) {
// isExternalStorageDocument
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
return Environment.getExternalStorageDirectory() + "/" + split[1];
} else if ("com.android.providers.downloads.documents".equals(authority)) {
// isDownloadsDocument
final String id = DocumentsContract.getDocumentId(uri);
uri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
} else if ("com.android.providers.media.documents".equals(authority)) {
// isMediaDocument
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("image".equals(type)) {
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
selection = "_id=?";
selectionArgs = new String[]{
split[1]
};
}
}
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null);
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.moveToFirst()) {
return cursor.getString(column_index);
}
}
} catch (Exception e) {
e.fillInStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}

根据图片地址去获取bitmap这时需要自己传入图片的大小即高度和宽度,根据自己的需求去传。

1
Bitmap bitmap = BitmapCreate.bitmapFromStream(new FileInputStream(path), 512, 512);

图片压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

/**
* 获取一个指定大小的bitmap<br>
* 实际调用的方法是bitmapFromByteArray(data, 0, data.length, w, h);
*
* @param is 从输入流中读取Bitmap
* @param reqWidth 目标宽度
* @param reqHeight 目标高度
*/
public static Bitmap bitmapFromStream(InputStream is, int reqWidth,
int reqHeight) {
if (reqHeight == 0 || reqWidth == 0) {
try {
return BitmapFactory.decodeStream(is);
} catch (OutOfMemoryError e) {
}
}
byte[] data = FileUtils.input2byte(is);
return bitmapFromByteArray(data, 0, data.length, reqWidth, reqHeight);
}

/**
* 获取一个指定大小的bitmap
*
* @param data Bitmap的byte数组
* @param offset image从byte数组创建的起始位置
* @param length the number of bytes, 从offset处开始的长度
* @param reqWidth 目标宽度
* @param reqHeight 目标高度
*/
public static Bitmap bitmapFromByteArray(byte[] data, int offset,
int length, int reqWidth, int reqHeight) {
if (reqHeight == 0 || reqWidth == 0) {
try {
return BitmapFactory.decodeByteArray(data, offset, length);
} catch (OutOfMemoryError e) {
}
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPurgeable = true;
BitmapFactory.decodeByteArray(data, offset, length, options);
options = calculateInSampleSize(options, reqWidth, reqHeight);
return BitmapFactory.decodeByteArray(data, offset, length, options);
}

/**
* 图片压缩处理(使用Options的方法)
* <br>
* <b>说明</b> 使用方法:
* 首先你要将Options的inJustDecodeBounds属性设置为true,BitmapFactory.decode一次图片 。
* 然后将Options连同期望的宽度和高度一起传递到到本方法中。
* 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。
* <br>
* <b>说明</b> BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存
* ,这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数
* ,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存
* ,返回值也不再是一个Bitmap对象, 而是null。虽然Bitmap是null了,但是Options的outWidth、
* outHeight和outMimeType属性都会被赋值。
*
* @param reqWidth 目标宽度,这里的宽高只是阀值,实际显示的图片将小于等于这个值
* @param reqHeight 目标高度,这里的宽高只是阀值,实际显示的图片将小于等于这个值
*/
public static BitmapFactory.Options calculateInSampleSize(
final BitmapFactory.Options options, final int reqWidth,
final int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
// 设置压缩比例
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
return options;
}

将压缩后的bitmap存在sdcard中,待会儿要上传到服务器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 图片写入文件
*
* @param bitmap
* 图片
* @param filePath
* 文件路径
* @return 是否写入成功
*/
public static boolean bitmapToFile(Bitmap bitmap, String filePath) {
boolean isSuccess = false;
if (bitmap == null) {
return isSuccess;
}
File file = new File(filePath.substring(0,
filePath.lastIndexOf(File.separator)));
if (!file.exists()) {
file.mkdirs();
}

OutputStream out = null;
try {
out = new BufferedOutputStream(new FileOutputStream(filePath),
8 * 1024);
isSuccess = bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
closeIO(out);
}
return isSuccess;
}

将压缩后的bitmap 根据需求进一步缩放,显示在imageview上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 放大缩小图片
*
* @param bitmap
* @param w
* @param h
* @return
*/
public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h) {
Bitmap newbmp = null;
if (bitmap != null) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidht = ((float) w / width);
float scaleHeight = ((float) h / height);
matrix.postScale(scaleWidht, scaleHeight);
newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix,
true);
}
return newbmp;
}

在拍照的过程中,有的机型照片会倒转,这是需要处理一下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* 读取图片属性:旋转的角度
* @param path 图片绝对路径
* @return degree旋转的角度
*/
public static int readPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}

/**
* 旋转图片
* @param angle
* @param bitmap
* @return
*/
public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
//旋转图片 动作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
System.out.println("angle2=" + angle);
// 创建新的图片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}

这些代码百分之七八十来自开源中国。感谢原作者。

源码下载

请我吃🍗