Android 调用系统相机,拍照,并上传图片所注意的一些问题
2017-01-15 22:03
651 查看
其实android拍照这个地方还是有很多注意事项的,我在上个项目中就遇到一些坑,因此我想把它记录下来,希望能帮助一些跟我遇到的同样的问题的人
如果你在项目中遇到以下问题:
通过系统路径,拍出来的图片不清楚在某些情况下,onActivityResult(int requestCode, int resultCode, Intent data) 回调方法中,data为null
有些时候,在某些手机拍照之后会闪退,报空指针错误,等等
有些时候在拍完照片之后,点击确认按钮之后,无法返回到前一个Activity
可以继续往下看,否则的话,就跳过吧,不能耽误你的时间
先说第一种情况,如果直接调用系统相机,一般系统会把图片放在默认的路径,然后通过回调函数onActivityResult(int requestCode, int resultCode, Intent data)中的data拿值,Bitmap bitmap =(Bitmap) data.getExtras().get("data"),这样就拿到bitmap了。值得注意的是,返回的这个图片是缩略图并不是原图。如果是需要拍身份证照片,录入信息的,不能用考虑这种。当然这种调用相机也会有人用到,先贴一下代码:
public class MainActivity extends AppCompatActivity { public static final int TAKE_PHOTO = 111; private Button mButton; private ImageView mImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button) findViewById(R.id.takePhoto); mImageView = (ImageView) findViewById(R.id.img); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { takePhoto(); } }); } public void takePhoto() { Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(captureIntent, TAKE_PHOTO); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == TAKE_PHOTO) { Bitmap bitmap = (Bitmap) data.getExtras().get("data"); //拿到bitmap,做喜欢做的事情把 ---> 显示 or上传? mImageView.setImageBitmap(bitmap); //upload } } } }
第二种情况,onActivityResult(int requestCode, int resultCode, Intent data) 回调方法中,data为null。这种情况,如果代码没错误,那么产生这种原因是什么呢? 其实当你自定义拍照路径之后,回调函数中,返回的data就为null了,那么你拿图片资源就只能通过file转化成bitmap的了。下面我会把Bitmap工具类提供出来
public class MainActivity extends AppCompatActivity { public static final int TAKE_PHOTO = 111; private Button mButton; private ImageView mImageView; //保存 照片的目录 private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mms"; private File photo_file = new File(path); private String photoPath ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button) findViewById(R.id.takePhoto); mImageView = (ImageView) findViewById(R.id.img); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { takePhoto(); } }); } public void takePhoto() { Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (!photo_file.exists()) { photo_file.mkdirs(); } photo_file = new File(path, "/temp.jpg"); photoPath = path + "/temp.jpg"; if (photo_file != null) { captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo_file)); startActivityForResult(captureIntent, TAKE_PHOTO); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == TAKE_PHOTO) { //这种情况下 data是为null的,因为自定义了路径 } } } }
这种情况下,因为data为null,所以正确做法应该是从 photoPath中来获取bitmap。在后面会写出全部代码的,这里只是说明为啥data会为null
在某些手机拍照之后会闪退,报空指针错误。这个错误主要是因为 当点击拍照时,某些情况下,系统会认为这个是耗费资源的操作,所以会未经你允许的情况下把之前的Activity给销毁了,所以当再次回到上个界面中,生命周期会重新来一遍,所以当执行到onActivityResult(int requestCode, int resultCode, Intent data)中,photoPath还是为空,所以报了空指针异常。解决办法如下:@Override protected void onSaveInstanceState< 11bc8 /span>(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("photoPath", photoPath); Log.d(TAG, "onSaveInstanceState"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (TextUtils.isEmpty(photoPath)) { photoPath = savedInstanceState.getString("photoPath"); } Log.d(TAG, "onRestoreInstanceState"); }
通过onSaveInstanceState(Bundle outState)把数据保存起来,然后界面确实被系统销毁了,那么就会执行onRestoreInstanceState(Bundle savedInstanceState)方法,在这个方法中就可以取到数据了,从而恢复界面。
有些时候在拍完照片之后,点击确认按钮之后,无法返回到前一个Activity,这个问题主要是文件建的不对,导入图片资源无法写进去,点击确定后也无法回退到上个界面。这里就按照我这种创建的方式把。完整拍照上传图片的代码如下:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final int TAKE_PHOTO = 111; private Button mButton; private ImageView mImageView; //保存 照片的目录 private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mms"; private File photo_file = new File(path); private String photoPath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button) findViewById(R.id.takePhoto); mImageView = (ImageView) findViewById(R.id.img); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { takePhoto(); } }); } public void takePhoto() { Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (!photo_file.exists()) { photo_file.mkdirs(); } photo_file = new File(path, "/temp.jpg"); photoPath = path + "/temp.jpg"; if (photo_file != null) { captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo_file)); startActivityForResult(captureIntent, TAKE_PHOTO); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == TAKE_PHOTO) { //这中情况下 data是为null的,因为自定义了路径 所以通过这个路径来获取 Bitmap smallBitmap = BitmapUtil.getSmallBitmap(photoPath); // ok 拿到图片的base64 上传 String base64 = BitmapToBase64Util.bitmapToBase64(smallBitmap); } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("photoPath", photoPath); Log.d(TAG, "onSaveInstanceState"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (TextUtils.isEmpty(photoPath)) { photoPath = savedInstanceState.getString("photoPath"); } Log.d(TAG, "onRestoreInstanceState"); } }
BitmapUtil
@SuppressLint("NewApi") public class BitmapUtil { // 计算图片的缩放值 public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, 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 = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } // 根据路径获得图片并压缩,返回bitmap用于显示 public static Bitmap getSmallBitmap(String filePath) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, 200, 380); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(filePath, options); } // 根据路径获得图片并压缩,返回bitmap用于显示 public static Bitmap getSmallBitmap(InputStream is) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, 480, 800); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeStream(is, null, options); } public static Bitmap comp(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); if (baos.toByteArray().length / 1024 > 1024) {// 判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出 baos.reset();// 重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, 40, baos);// 这里压缩50%,把压缩后的数据存放到baos中 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); BitmapFactory.Options newOpts = new BitmapFactory.Options(); // 开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 float hh = 800f;// 这里设置高度为800f float ww = 480f;// 这里设置宽度为480f // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;// be=1表示不缩放 if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;// 设置缩放比例 // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩 } // 把bitmap转换成String public static String bitmapToString(String filePath) { Bitmap bm = getSmallBitmap(filePath); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.JPEG, 40, baos); byte[] b = baos.toByteArray(); return Base64.encodeToString(b, Base64.DEFAULT); } /** * 质量压缩 * @param image * @return */ public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 50, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 50; while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩 baos.reset();// 重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中 options -= 10;// 每次都减少10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片 return bitmap; } /** * 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换 * @param context * @param imageUri * @author yaoxing * @date 2014-10-12 */ public static String getImageAbsolutePath(Activity context, Uri imageUri) { if (context == null || imageUri == null) return null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) { if (isExternalStorageDocument(imageUri)) { String docId = DocumentsContract.getDocumentId(imageUri); String[] split = docId.split(":"); String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } else if (isDownloadsDocument(imageUri)) { String id = DocumentsContract.getDocumentId(imageUri); Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } else if (isMediaDocument(imageUri)) { String docId = DocumentsContract.getDocumentId(imageUri); String[] split = docId.split(":"); String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } String selection = MediaStore.Images.Media._ID + "=?"; String[] selectionArgs = new String[] { split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(imageUri.getScheme())) { // Return the remote address if (isGooglePhotosUri(imageUri)) return imageUri.getLastPathSegment(); return getDataColumn(context, imageUri, null, null); } // File else if ("file".equalsIgnoreCase(imageUri.getScheme())) { return imageUri.getPath(); } return null; } public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; String column = MediaStore.Images.Media.DATA; String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } /*** * 图片的缩放方法 * * @param bgimage * :源图片资源 * @param newWidth * :缩放后宽度 * @param newHeight * :缩放后高度 * @return */ public static Bitmap zoomImage(Bitmap bgimage, double newWidth, double newHeight) { // 获取这个图片的宽和高 float width = bgimage.getWidth(); float height = bgimage.getHeight(); // 创建操作图片用的matrix对象 Matrix matrix = new Matrix(); // 计算宽高缩放率 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 缩放图片动作 matrix.postScale(scaleWidth, scaleHeight); Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width, (int) height, matrix, true); return bitmap; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } }
BitmapUtil工具类主要包含了获取bitmap、质量压缩等一些方法。当然拍照之后一般会获取图片的base64,然后上传给服务端,或者前端js。 BitmapToBase64Util
代码如下;
BitmapToBase64Util
public class BitmapToBase64Util { /** * bitmapתΪbase64 * * @param bitmap * @return */ public static String bitmapToBase64(Bitmap bitmap) { String result = null; ByteArrayOutputStream baos = null; try { if (bitmap != null) { baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos); //这里50表示压缩50% baos.flush(); baos.close(); byte[] bitmapBytes = baos.toByteArray(); result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (baos != null) { baos.flush(); baos.close(); } } catch (Exception e) { e.printStackTrace(); } } return result; } /** * base64תΪbitmap * * @param base64Data * @return */ public static Bitmap base64ToBitmap(String base64Data) { byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT); return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } }
关于调用系统相机、拍照上传就到处为止,希望能帮助到一些需要帮助的人
相关文章推荐
- Android 调用系统相机拍照并且显示在相册中,以及中间可能会遇到的一些问题的解决
- Android 拍照以及一些常用的处理,例如将图片显示到相册(包含了安卓系统6.0以上调用相机的处理)
- Android开发 调用系统相机相册图片功能,解决小米手机拍照或者图片横竖相反问题,及小米手机相册图片路径问题
- Android 调用系统拍照的一些应该注意的问题
- Android基础--调用系统相机,打开相册,展示到界面,出现的一些问题Uri为空,data为null,图片错乱问题
- Android手机调用系统相机拍照、裁剪以及获取Url上传图片
- HTML5拍照上传图片&Phonegap封装HTML5调用Android相机拍照上传到PHP端
- Android调用系统相机拍照像素太低以及内存溢出问题
- Android调用系统相机拍照及图片保存的Uri方式------菜鸟学习历程
- 解决android调用系统相机拍照保存时onActivityResult中data为null的问题
- Android调用系统拍照获取图片问题
- Android demo--调用系统相机拍照并显示图片为黑白
- 调用系统相机拍照,从相册选取图片上传
- 转载 Android解决通过Intent调用系统拍照程序,返回图片太小的问题
- 解决三星调用系统相机拍照显示图片旋转90度横着的问题
- 解决Android调用系统相机拍照后相片无法在相册中显示问题
- Android调用系统相机获取图片尺寸很小问题
- 解决通过Intent调用系统拍照程序,返回图片太小的问题[android]
- Android 调用本地相机拍照并上传图片
- android调用系统相机和相册进行拍照裁剪处理,解决不同安卓版本存在无法加载相册的问题,处理了是否有sd卡的存在的情况