最近公司项目用到手机拍照的问题,好不容易在网上copy了一些代码,但是运行起来一大堆bug,先是三星手机上运行程序直接崩掉,debug了一下原来是onActivityResult中data返回为空,找了资料,说是部分手机,特别是三星手机会出现这个问题,那不行啊,得解决问题,总不能提示用户换手机吧,既然不能接收到data,那就直接拿uri操作呗,好不容易解决掉这个问题,又出现了OOM的问题,再接着还出现了图片角度不对的问题,所幸是都解决啦,现在把代码提取出来,方便下次使用,先看运行的效果。
把处理OOM,图片角度的代码都封装在PictureHelper类中啦,直接调用就行
1 package com.example.keranbin.cameraldemo;
2
3 import .Activity;
4 import android.content.ContentResolver;
5 import android.content.Context;
6 import android.database.Cursor;
7 import android.graphics.Bitmap;
8 import android.graphics.BitmapFactory;
9 import android.graphics.Matrix;
10 import .ExifInterface;
11 import android.net.Uri;
12
13 import java.io.ByteArrayInputStream;
14 import java.io.ByteArrayOutputStream;
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.io.IOException;
18 import java.io.InputStream;
19
20 /**
21 * Created by keranbin on 2016/7/20.
22 */
23 public class PictureHelper {
24
25 /**
26 * 通过Uri获取文件
27 *
28 * @param context
29 * @param uri
30 * @return
31 */
32 public static File getFileFromMediaUri(Context context, Uri uri) {
33 if (uri.getScheme().toString().compareTo("content") == 0) {
34 ContentResolver cr = context.getContentResolver();
35 Cursor cursor = cr.query(uri, null, null, null, null);// 根据Uri从数据库中找
36 if (cursor != null) {
37 cursor.moveToFirst();
38 String filePath = cursor.getString(cursor.getColumnIndex("_data"));// 获取图片路径
39 cursor.close();
40 if (filePath != null) {
41 return new File(filePath);
42 }
43 }
44 } else if (uri.getScheme().toString().compareTo("file") == 0) {
45 return new File(uri.toString().replace("file://", ""));
46 }
47 return null;
48 }
49
50
51 /**
52 * 通过uri获取图片并进行压缩
53 *
54 * @param uri
55 */
56 public static Bitmap getBitmapFormUri(Activity context, Uri uri) throws FileNotFoundException, IOException {
57 InputStream input = context.getContentResolver().openInputStream(uri);
58 BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
59 onlyBoundsOptions.inJustDecodeBounds = true;
60 onlyBoundsOptions.inDither = true;//optional
61 onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
62 BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
63 input.close();
64 int originalWidth = onlyBoundsOptions.outWidth;
65 int originalHeight = onlyBoundsOptions.outHeight;
66 if ((originalWidth == -1) || (originalHeight == -1))
67 return null;
68 //图片分辨率以480x800为标准
69 float hh = 800f;//这里设置高度为800f
70 float ww = 480f;//这里设置宽度为480f
71 //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
72 int be = 1;//be=1表示不缩放
73 if (originalWidth > originalHeight && originalWidth > ww) {//如果宽度大的话根据宽度固定大小缩放
74 be = (int) (originalWidth / ww);
75 } else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的话根据宽度固定大小缩放
76 be = (int) (originalHeight / hh);
77 }
78 if (be <= 0)
79 be = 1;
80 //比例压缩
81 BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
82 bitmapOptions.inSampleSize = be;//设置缩放比例
83 bitmapOptions.inDither = true;//optional
84 bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
85 input = context.getContentResolver().openInputStream(uri);
86 Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
87 input.close();
88
89 return compressImage(bitmap);//再进行质量压缩
90 }
91
92 /**
93 * 质量压缩方法
94 *
95 * @param image
96 * @return
97 */
98 public static Bitmap compressImage(Bitmap image) {
99
100 ByteArrayOutputStream baos = new ByteArrayOutputStream();
101 image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
102 int options = 100;
103 while (baos.toByteArray().length / 1024 > 100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
104 baos.reset();//重置baos即清空baos
105 //第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差 ,第三个参数:保存压缩后的数据的流
106 image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
107 options -= 10;//每次都减少10
108 }
109 ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
110 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
111 return bitmap;
112 }
113
114 /**
115 * 读取图片的旋转的角度
116 *
117 * @param path 图片绝对路径
118 * @return 图片的旋转角度
119 */
120 public static int getBitmapDegree(String path) {
121 int degree = 0;
122 try {
123 // 从指定路径下读取图片,并获取其EXIF信息
124 ExifInterface exifInterface = new ExifInterface(path);
125 // 获取图片的旋转信息
126 int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
127 ExifInterface.ORIENTATION_NORMAL);
128 switch (orientation) {
129 case ExifInterface.ORIENTATION_ROTATE_90:
130 degree = 90;
131 break;
132 case ExifInterface.ORIENTATION_ROTATE_180:
133 degree = 180;
134 break;
135 case ExifInterface.ORIENTATION_ROTATE_270:
136 degree = 270;
137 break;
138 }
139 } catch (IOException e) {
140 e.printStackTrace();
141 }
142 return degree;
143 }
144
145 /**
146 * 将图片按照某个角度进行旋转
147 *
148 * @param bm 需要旋转的图片
149 * @param degree 旋转角度
150 * @return 旋转后的图片
151 */
152 public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
153 Bitmap returnBm = null;
154
155 // 根据旋转角度,生成旋转矩阵
156 Matrix matrix = new Matrix();
157 matrix.postRotate(degree);
158 try {
159 // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
160 returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
161 } catch (OutOfMemoryError e) {
162 }
163 if (returnBm == null) {
164 returnBm = bm;
165 }
166 if (bm != returnBm) {
167 bm.recycle();
168 }
169 return returnBm;
170 }
171
172 }
创建需要的popWindow的PopwindowHelper类
1 package com.example.keranbin.cameraldemo;
2
3 import .Activity;
4 import android.content.Context;
5 import android.content.Intent;
6 import android.graphics.drawable.BitmapDrawable;
7 import android.net.Uri;
8 import android.os.Environment;
9 import android.provider.MediaStore;
10 import android.view.Gravity;
11 import android.view.LayoutInflater;
12 import android.view.View;
13 import android.view.ViewGroup;
14 import android.view.Window;
15 import android.view.WindowManager;
16 import android.widget.Button;
17 import android.widget.LinearLayout;
18 import android.widget.PopupWindow;
19
20 import java.io.File;
21 import java.util.Date;
22
23 /**
24 * Created by keranbin on 2016/7/19.
25 */
26 public class PopWindowHelper {
27 private PopupWindow popupWindow;
28 private Context context;
29 public void createCameralPop(final Activity context, View parent, final Window window, int layout, int btnCameralId, int btnPhotoId, int btnCancelId) {
30 this.context=context;
31 final View view =LayoutInflater.from(context).inflate(layout,null);
32 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
33 view.setLayoutParams(params);
34 popupWindow = new PopupWindow(view, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
35 //设置MyPopupWindow的View
36 popupWindow.setContentView(view);
37 //设置MyPopupWindow的View弹出窗体的宽
38 popupWindow.setWidth(ViewGroup.LayoutParams.FILL_PARENT);
39 //设置MyPopupWindow的View弹出窗体的高
40 popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
41 //设置MyPopupWindow的View弹出窗体可点击,如果不添加这个属性,那么点击EditText无法弹出输入法
42 popupWindow.setFocusable(true);
43 //设置MyPopupWindow去除边际黑线
44 popupWindow.setBackgroundDrawable(new BitmapDrawable());
45 //避免输入法覆盖掉popWindow
46 popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
47 popupWindow.showAtLocation(parent, Gravity.BOTTOM, 0, 0);
48
49 setWindowGray(window);
50
51 //popWindow消失后,还原页面背景
52 popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
53 @Override
54 public void onDismiss() {
55 setWindowGray(window);
56 }
57 });
58
59 //点击拍照按钮,跳转到拍照页面
60 view.findViewById(btnCameralId).setOnClickListener(new View.OnClickListener() {
61 @Override
62 public void onClick(View view) {
63 inentToCameral(context);
64 }
65 });
66
67 //点击我的相册按钮,跳转到我的相册
68 view.findViewById(btnPhotoId).setOnClickListener(new View.OnClickListener() {
69 @Override
70 public void onClick(View view) {
71 intentToPhoto(context);
72 }
73 });
74
75 //点击取消按钮,设置PopWindow隐藏
76 view.findViewById(btnCancelId).setOnClickListener(new View.OnClickListener() {
77 @Override
78 public void onClick(View view) {
79 popupWindow.dismiss();
80 }
81 });
82
83
84 }
85 public void setWindowGray(Window window) {
86 WindowManager.LayoutParams lp = window.getAttributes();
87 if (popupWindow.isShowing()) {
88 lp.alpha = 0.5f;
89 window.setAttributes(lp);
90 } else {
91 lp.alpha = 1.0f;
92 window.setAttributes(lp);
93 }
94 }
95
96 //调用系统相机
97 public void inentToCameral(Activity context){
98 //Standard Intent action that can be sent to have the camera application capture an image and return it.
99 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
100 Date date = new Date();
101 String strPath= Environment.getExternalStorageDirectory().getPath() + "/" + date.getTime();
102 File path=new File(strPath);
103 if(!path.exists()){
104 path.mkdirs();
105 }
106 Flags.PICTURE_SAVE_PATH = path + Flags.PICTURE_SAVETYPE;
107 Uri uri = Uri.fromFile(new File(Flags.PICTURE_SAVE_PATH));
108 //把拍照的图片存储到我们传入的Uri对应的File里面。
109 intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
110 context.startActivityForResult(intent, Flags.TAKEPICTURE_RESULTCODE);
111 popupWindow.dismiss();
112 }
113
114
115 //调用系统相册
116 private void intentToPhoto(Activity context) {
117 Intent intent = new Intent(Intent.ACTION_PICK, .EXTERNAL_CONTENT_URI);
118 context.startActivityForResult(intent, Flags.GETSYSTEMPICTURE);
119 popupWindow.dismiss();
120 }
121
122 }
再看看MainActivity中的调用方法和获取图片Uri
1 package com.example.keranbin.cameraldemo;
2
3 import .Activity;
4 import android.content.Intent;
5 import android.graphics.Bitmap;
6 import android.net.Uri;
7 import android.os.Bundle;
8 import android.view.View;
9 import android.widget.ImageView;
10
11 import java.io.File;
12 import java.io.IOException;
13
14 public class MainActivity extends Activity {
15 private PopWindowHelper popWindowHelper;
16
17 @Override
18 protected void onCreate(Bundle savedInstanceState) {
19 super.onCreate(savedInstanceState);
20 setContentView(R.layout.activity_main);
21 popWindowHelper = new PopWindowHelper();
22 findViewById(.tvClick).setOnClickListener(new View.OnClickListener() {
23 @Override
24 public void onClick(View view) {
25 popWindowHelper.createCameralPop(
26 MainActivity.this,
27 MainActivity.this.findViewById(.rl),
28 MainActivity.this.getWindow(),
29 R.layout.cameral_pop_bottom,
30 .btnCamera,
31 .btnPhoto,
32 .btnCancel);
33 }
34 });
35
36 }
37
38
39 @Override
40 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
41 switch (requestCode) {
42 case Flags.TAKEPICTURE_RESULTCODE:
43 getImageFromCameral(resultCode, data);
44 break;
45 case Flags.GETSYSTEMPICTURE:
46 getImageFromAlbum(resultCode, data);
47 break;
48
49 }
50
51 }
52 private void getImageFromCameral(int resultCode, Intent data) {
53 if (resultCode == RESULT_OK) {
54 Bitmap bitmap = null;
55 if (data != null) { //可能尚未指定intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
56 //返回有缩略图
57 if (data.hasExtra("data")) {
58 bitmap = data.getParcelableExtra("data");
59 }
60 } else {
61 try {
62 //由于指定了目标uri,存储在目标uri,intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
63 // 通过目标uri,找到图片
64 File photofile = PictureHelper.getFileFromMediaUri(MainActivity.this, Uri.fromFile(new File(Flags.PICTURE_SAVE_PATH)));
65 Bitmap photoBitmap = PictureHelper.getBitmapFormUri(MainActivity.this, Uri.fromFile(photofile));
66 int degree = PictureHelper.getBitmapDegree(photofile.getAbsolutePath());
67 bitmap = PictureHelper.rotateBitmapByDegree(photoBitmap, degree);
68 } catch (IOException e) {
69 e.printStackTrace();
70 }
71
72 }
73 ((ImageView) findViewById(.imageView)).setImageBitmap(bitmap);
74 }
75 }
76
77 private void getImageFromAlbum(int resultCode, Intent data) {
78 if (resultCode == RESULT_OK && null != data) {
79 try {
80 //由于指定了目标uri,存储在目标uri,intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
81 // 通过目标uri,找到图片
82 File photofile = PictureHelper.getFileFromMediaUri(MainActivity.this, data.getData());
83 //防止OOM问题,对图片进行压缩
84 Bitmap photoBitmap = PictureHelper.getBitmapFormUri(MainActivity.this, Uri.fromFile(photofile));
85 //读取图片的旋转的角度
86 int degree = PictureHelper.getBitmapDegree(photofile.getAbsolutePath());
87 //将图片按照某个角度进行旋转
88 Bitmap bitmap = PictureHelper.rotateBitmapByDegree(photoBitmap, degree);
89 ((ImageView) findViewById(.imageView)).setImageBitmap(bitmap);
90 } catch (IOException e) {
91 e.printStackTrace();
92 }
93 }
94
95 }
96 }
从底下弹出来的popWindow布局代码如下
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http:///apk/res/android"
3 android:id="@+id/pop_layout"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 android:orientation="vertical">
7
8 <LinearLayout
9 android:layout_width="match_parent"
10 android:layout_height="wrap_content"
11 android:layout_margin="15dp"
12 android:orientation="vertical">
13
14 <Button
15 android:id="@+id/btnCamera"
16 android:layout_width="match_parent"
17 android:layout_height="50dp"
18 android:layout_gravity="center"
19 android:background="@drawable/take_photo"
20 android:text="拍照"
21 android:textColor="#1C86EE"
22 android:textSize="20dp" />
23
24 <View
25 android:id="@+id/straight_line"
26 android:layout_width="fill_parent"
27 android:layout_height="3px"
28 android:background="#404040"></View>
29
30 <Button
31 android:id="@+id/btnPhoto"
32 android:layout_width="match_parent"
33 android:layout_height="50dp"
34 android:layout_gravity="center"
35 android:background="@drawable/my_photo"
36 android:text="我的相册"
37 android:textColor="#1C86EE"
38 android:textSize="20dp" />
39 </LinearLayout>
40
41
42 <Button
43 android:id="@+id/btnCancel"
44 android:layout_width="match_parent"
45 android:layout_height="50dp"
46 android:layout_gravity="center"
47 android:layout_margin="15dp"
48 android:background="@drawable/btn_pop_cancel"
49 android:text="取消"
50 android:textColor="#104E8B"
51 android:textSize="22dp"
52 android:textStyle="bold" />
53
54 </LinearLayout>
MainActivity的布局就更加简单啦
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http:///apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:id="@+id/rl">
6
7 <LinearLayout xmlns:android="http:///apk/res/android"
8 android:layout_width="match_parent"
9 android:layout_height="match_parent"
10 android:gravity="center"
11 android:orientation="vertical">
12
13
14 <TextView
15 android:id="@+id/tvClick"
16 android:layout_width="wrap_content"
17 android:layout_height="wrap_content"
18 android:text="点击"
19 android:textSize="25dp" />
20
21 <ImageView
22 android:id="@+id/imageView"
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content"
25 android:layout_marginTop="15dp" />
26
27 </LinearLayout>
28
29 </RelativeLayout>