Android选择图片、拍照、裁剪 注意事项
2017-03-20 18:21
423 查看
一、基础代码
调用系统相机或相册,获取图片,然后使用系统裁剪功能处理图片
二、注意事项
1.拍照图片存储问题
调用系统相机实现拍照功能的核心代码如下:
其中 MediaStore.EXTRA_OUTPUT 数据表示,拍照所得图片保存到指定目录下的文件(一般会在 SD 卡中创建当前应用的目录,并创建临时文件保存图片)。然后,在 onActivityResult 方法中根据文件路径获取图片。
如果不为 intent 添加该数据的话,将在 onActivityResult 的 intent 对象中返回一个 Bitmap 对象,通过如下代码获取:
这里的 Bitmap 对象是拍照所得图片的一个缩略图,尺寸很小。
所以,调用系统相机时,一般都会添加MediaStore.EXTRA_OUTPUT 参数,避免返回 Bitmap 对象。当然,这么做也能保证应用产生的数据,包括文件,都能存储在应用目录下,方便清理缓存时统一清除。
2.拍照图片旋转问题
部分手机,比如三星手机,调用系统相机拍照所得的照片可能会发生自动旋转问题,常见为旋转 90°。所以,要求我们在拍照之后,使用图片之前,判断图片是否发生过旋转,如果是,要将照片旋转回来。
这是获取图片旋转角度的代码:
这是根据指定角度旋转图片的代码:
3.横竖屏切换问题
在部分手机,调用系统拍照功能时,可能会发生横竖屏切换过程,导致返回应用时当前 Activity 发生销毁重建,各个生命周期又重新走了一遍。此时,一些应用内的变量数据可能丢失,使用时容易发生空值异常,进而导致 app 崩溃退出。
为了避免这种现象,我们需要在 AndroidManifest.xml 文件的对应 activity标签中添加属性:
这样,当发生屏幕旋转时,不会导致 Activity 销毁重建,而是执行onConfigurationChanged() 方法:
4.调用系统裁剪问题
调用系统裁剪的代码如下:
可以看出,调用系统裁剪功能,需要设置一些 Extra 参数,很多人容易在这里产生疑惑,不知如何取舍,如何设值。这里列举一下常用的 Extra 名字、值类型和作用:
crop:String 类型数据,发送裁剪信号
aspectX 和 aspectY:int 类型数据,设置裁剪框的 X 与 Y 值比例
outputX 和 outputY:int 类型数据,设置裁剪输出的图片大小
scale:boolean 类型数据,设置是否支持裁剪缩放
return-data:boolean 类型数据,设置是否在 onActivityResult 方法的 intent 值中返回Bitmap 对象
MediaStore.EXTRA_OUTPUT:Uri 类型数据,设置是否将裁剪结果保存到指定文件中
需要注意的是:
第一,设置 return-data 参数为 true 时,返回的 Bitmap 对象也为缩略图,获取方式与前面所述相机拍照获取 Bitmap 的方式一致;
第二,调用系统相册并裁剪时,如果使用MediaStore.EXTRA_OUTPUT参数,Uri 尽量不要设置为源文件对应的 Uri 值,另做保存,不损坏系统相册中的源图文件;
第三,根据经验,outputX 与 outputY 值设置太大时,容易出现卡屏现象;
第四,可以不设置 outputX 与 outputY 参数,使用户根据自身按比例自由裁剪,就像示例代码这样。
有时候,我们需要根据 Uri 获取文件路径。比如如果你不需要使用裁剪功能的话,调用系统相册选择图片后返回的就是一个 Uri 对象,我们需要从这个 Uri 对象中解析出对应的图片文件路径,便于上传至服务器等后续处理。
正确的获取方式是:
其对应的文件路径应该是这个样子的:
三、Base64 文件编码处理
使用 Base64 编码并根据字节数组获取字符串的处理过程:
四、zip 压缩文件处理
五、注意事项
在 AndroidManifest.xml 文件中添加系统权限(前面示例代码中没有考虑到 Android 6.0 运行时权限的问题,实际使用时注意添加处理):
调用系统相机或相册,获取图片,然后使用系统裁剪功能处理图片
public class MainActivity extends FragmentActivity { public static final int REQUEST_CAMERA = 1; public static final int REQUEST_ALBUM = 2; public static final int REQUEST_CROP = 3; public static final String IMAGE_UNSPECIFIED = "image/*"; private ImageButton mPictureIb; private File mImageFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPictureIb = (ImageButton) findViewById(R.id.ib_picture); } public void onClickPicker(View v) { new AlertDialog.Builder(this) .setTitle("选择照片") .setItems(new String[]{"拍照", "相册"}, new OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { if (i == 0) { selectCamera(); } else { selectAlbum(); } } }) .create() .show(); } private void selectCamera() { createImageFile(); if (!mImageFile.exists()) { return; } Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile)); startActivityForResult(cameraIntent, REQUEST_CAMERA); } private void selectAlbum() { Intent albumIntent = new Intent(Intent.ACTION_PICK); albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED); startActivityForResult(albumIntent, REQUEST_ALBUM); } private void cropImage(Uri uri){ Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile)); startActivityForResult(intent, REQUEST_CROP); } private void createImageFile() { mImageFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg"); try { mImageFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "出错啦", Toast.LENGTH_SHORT).show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (RESULT_OK != resultCode) { return; } switch (requestCode) { case REQUEST_CAMERA: cropImage(Uri.fromFile(mImageFile)); break; case REQUEST_ALBUM: createImageFile(); if (!mImageFile.exists()) { return; } Uri uri = data.getData(); if (uri != null) { cropImage(uri); } break; case REQUEST_CROP: mPictureIb.setImageURI(Uri.fromFile(mImageFile)); break; } } }
二、注意事项
1.拍照图片存储问题
调用系统相机实现拍照功能的核心代码如下:
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile)); startActivityForResult(cameraIntent, REQUEST_CAMERA);
其中 MediaStore.EXTRA_OUTPUT 数据表示,拍照所得图片保存到指定目录下的文件(一般会在 SD 卡中创建当前应用的目录,并创建临时文件保存图片)。然后,在 onActivityResult 方法中根据文件路径获取图片。
如果不为 intent 添加该数据的话,将在 onActivityResult 的 intent 对象中返回一个 Bitmap 对象,通过如下代码获取:
Bitmap bmp = data.getParcelableExtra("data");
这里的 Bitmap 对象是拍照所得图片的一个缩略图,尺寸很小。
所以,调用系统相机时,一般都会添加MediaStore.EXTRA_OUTPUT 参数,避免返回 Bitmap 对象。当然,这么做也能保证应用产生的数据,包括文件,都能存储在应用目录下,方便清理缓存时统一清除。
2.拍照图片旋转问题
部分手机,比如三星手机,调用系统相机拍照所得的照片可能会发生自动旋转问题,常见为旋转 90°。所以,要求我们在拍照之后,使用图片之前,判断图片是否发生过旋转,如果是,要将照片旋转回来。
这是获取图片旋转角度的代码:
/** * 获取图片旋转角度 * @param path 图片路径 * @return */ private int parseImageDegree(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 bm 需要旋转的图片 * @param degree 旋转角度 * @return 旋转后的图片 */ private Bitmap rotateBitmap(Bitmap bm, int degree) { Bitmap returnBm = null; Matrix matrix = new Matrix(); matrix.postRotate(degree); try { returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); } catch (OutOfMemoryError e) { } if (returnBm == null) { returnBm = bm; } if (bm != returnBm) { bm.recycle(); } return returnBm; }
3.横竖屏切换问题
在部分手机,调用系统拍照功能时,可能会发生横竖屏切换过程,导致返回应用时当前 Activity 发生销毁重建,各个生命周期又重新走了一遍。此时,一些应用内的变量数据可能丢失,使用时容易发生空值异常,进而导致 app 崩溃退出。
为了避免这种现象,我们需要在 AndroidManifest.xml 文件的对应 activity标签中添加属性:
android:configChanges="orientation|screenSize"
这样,当发生屏幕旋转时,不会导致 Activity 销毁重建,而是执行onConfigurationChanged() 方法:
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }
4.调用系统裁剪问题
调用系统裁剪的代码如下:
Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile)); startActivityForResult(intent, REQUEST_CROP);
可以看出,调用系统裁剪功能,需要设置一些 Extra 参数,很多人容易在这里产生疑惑,不知如何取舍,如何设值。这里列举一下常用的 Extra 名字、值类型和作用:
crop:String 类型数据,发送裁剪信号
aspectX 和 aspectY:int 类型数据,设置裁剪框的 X 与 Y 值比例
outputX 和 outputY:int 类型数据,设置裁剪输出的图片大小
scale:boolean 类型数据,设置是否支持裁剪缩放
return-data:boolean 类型数据,设置是否在 onActivityResult 方法的 intent 值中返回Bitmap 对象
MediaStore.EXTRA_OUTPUT:Uri 类型数据,设置是否将裁剪结果保存到指定文件中
需要注意的是:
第一,设置 return-data 参数为 true 时,返回的 Bitmap 对象也为缩略图,获取方式与前面所述相机拍照获取 Bitmap 的方式一致;
第二,调用系统相册并裁剪时,如果使用MediaStore.EXTRA_OUTPUT参数,Uri 尽量不要设置为源文件对应的 Uri 值,另做保存,不损坏系统相册中的源图文件;
第三,根据经验,outputX 与 outputY 值设置太大时,容易出现卡屏现象;
第四,可以不设置 outputX 与 outputY 参数,使用户根据自身按比例自由裁剪,就像示例代码这样。
有时候,我们需要根据 Uri 获取文件路径。比如如果你不需要使用裁剪功能的话,调用系统相册选择图片后返回的就是一个 Uri 对象,我们需要从这个 Uri 对象中解析出对应的图片文件路径,便于上传至服务器等后续处理。
正确的获取方式是:
private String parseFilePath(Uri uri) { String[] filePathColumn = { MediaStore.Images.Media.DATA }; Cursor cursor = getContentResolver().query(uri, filePathColumn, null, null, null); cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String picturePath = cursor.getString(columnIndex); cursor.close(); return picturePath; }
其对应的文件路径应该是这个样子的:
/storage/emulated/0/Pictures/Screenshots/S70302-131606.jpg
三、Base64 文件编码处理
使用 Base64 编码并根据字节数组获取字符串的处理过程:
public static String fileToBase64String(String filePath) { File photoFile = new File(filePath); try { FileInputStream fis = new FileInputStream(photoFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(10000); byte[] buffer = new byte[1000]; while (fis.read(buffer)!=-1) { baos.write(buffer); } baos.close(); fis.close(); return Arrays.toString(Base64.encode(baos.toByteArray(), Base64.DEFAULT)); }catch (IOException e) { e.printStackTrace(); } return null; }
四、zip 压缩文件处理
public String zipCompass(String filePath){ File zipFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".zip"); try{ //指定了两个待压缩的文件,都在assets目录中 String[] filenames = new String[]{ "activity_main.xml", "strings.xml" }; FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(fos); int i = 1; //枚举filenames中的所有待压缩文件 while (i <= filenames.length){ //从filenames数组中取出当前待压缩的文件名,作为压缩后的名称,以保证压缩前后文件名一致 ZipEntry zipEntry = new ZipEntry(filenames[i - 1]); //打开当前的zipEntry对象 zos.putNextEntry(zipEntry); FileInputStream is = new FileInputStream(filePath); byte[] buffer = new byte[8192]; int count = 0; //写入数据 while ((count = is.read(buffer)) >= 0){ zos.write(buffer, 0, count); } zos.flush(); zos.closeEntry(); is.close(); i++; } zos.finish(); zos.close(); return zipFile.getAbsolutePath(); } catch (Exception e){ Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); return null; } }
五、注意事项
在 AndroidManifest.xml 文件中添加系统权限(前面示例代码中没有考虑到 Android 6.0 运行时权限的问题,实际使用时注意添加处理):
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
相关文章推荐
- Android大图片裁剪之手机拍照和从相册中选择注意点
- Android实现拍照,以及从相册选择图片裁剪功能同时保存在本地
- Android 通过拍照或相册选择图片并裁剪(精简版)
- 一款用于在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库
- Android实现拍照、选择相册图片并裁剪功能
- Android实现拍照、选择图片并裁剪图片功能
- Android拍照,选择图片,裁剪和上传服务器
- Android如何拍照或选择图片并裁剪
- Android 拍照、选择图片并裁剪
- Android拍照或从图库选择图片并裁剪
- android客户端学习-拍照或从相册选择图片并裁剪
- Android 拍照、选择图片并裁剪
- Android 拍照、选择图片并裁剪
- Android拍照或从图库选择图片并裁剪
- Android 拍照、选择图片并裁剪
- Android图片裁剪(拍照和从相册选择)
- Android 通过拍照或相册选择图片并裁剪(精简版)
- Android开发之头像上传(包含拍照,从相册选择图片,裁剪等)
- Android实现拍照、选择图片并裁剪图片功能
- android选择系统相机拍照和系统相册,裁剪图片并保存和设置头像,适配至7.0