最近公司项目用到手机拍照的问题,好不容易在网上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>