最近公司项目用到手机拍照的问题,好不容易在网上copy了一些代码,但是运行起来一大堆bug,先是三星手机上运行程序直接崩掉,debug了一下原来是onActivityResult中data返回为空,找了资料,说是部分手机,特别是三星手机会出现这个问题,那不行啊,得解决问题,总不能提示用户换手机吧,既然不能接收到data,那就直接拿uri操作呗,好不容易解决掉这个问题,又出现了OOM的问题,再接着还出现了图片角度不对的问题,所幸是都解决啦,现在把代码提取出来,方便下次使用,先看运行的效果。

    

把处理OOM,图片角度的代码都封装在PictureHelper类中啦,直接调用就行

  1 package com.example.keranbin.cameraldemo;
  2 
  3 import android.app.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 android.media.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 android.app.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, android.provider.MediaStore.Images.Media.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 android.app.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(R.id.tvClick).setOnClickListener(new View.OnClickListener() {
23             @Override
24             public void onClick(View view) {
25                 popWindowHelper.createCameralPop(
26                         MainActivity.this,
27                         MainActivity.this.findViewById(R.id.rl),
28                         MainActivity.this.getWindow(),
29                         R.layout.cameral_pop_bottom,
30                         R.id.btnCamera,
31                         R.id.btnPhoto,
32                         R.id.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(R.id.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(R.id.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://schemas.android.com/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://schemas.android.com/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://schemas.android.com/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>