问题描述
在我的android studio项目中,我试图通过startActivityForResult
从外部存储中选择一个图像,然后将所选图像加载到内存中。我也有一个BitmapLoader
助手类。这是我调用活动以获取结果的代码
private void pickFromgallery() {
//Create an Intent with action as ACTION_PICK
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// Sets the type as image/*. This ensures only components of type image are selected
intent.setType("image/*");
//We pass an extra array with the accepted mime types. This will ensure only components with these MIME types as targeted.
String[] mimeTypes = {"image/jpeg","image/png"};
intent.putExtra(Intent.EXTRA_MIME_TYPES,mimeTypes);
// Launching the Intent
startActivityForResult(intent,galLERY_REQUEST_CODE); //galLERY_REQUEST_CODE is a constant integer
}
这是活动结果回调
public void onActivityResult(int requestCode,int resultCode,@Nullable Intent data) {
super.onActivityResult(requestCode,resultCode,data);
// Result code is RESULT_OK only if the user selects an Image
if(resultCode == Activity.RESULT_OK) {
Bitmap imageBitmap = null;
switch(requestCode) {
case galLERY_REQUEST_CODE:
//data.getData returns the content URI for the selected Image
File file = new File(data.getData().getPath());
try {
//BitmapLoader is my helper class
imageBitmap = BitmapLoader.decodeSampleBitmapFromFile(file,100,100);
} catch (IOException e) {
e.printstacktrace();
Toast.makeText(this,"Error while reading a file!",Toast.LENGTH_SHORT).show();
return;
}
userImage.setimageBitmap(imageBitmap);
break;
}
}
}
最后,这是BitmapLoader
助手类。
public class BitmapLoader {
private BitmapLoader() {}
private static Bitmap rotateImageIfrequired(Bitmap img,File file) throws IOException {
ExifInterface ei = new ExifInterface(file);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_norMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img,90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img,180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img,270);
default:
return img;
}
}
private static Bitmap rotateImage(Bitmap img,int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img,img.getWidth(),img.getHeight(),matrix,true);
img.recycle();
return rotatedImg;
}
private static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfheight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value and keeps both
// height and width larger than the requested height and width.
while ((halfheight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize += 1;
}
}
return inSampleSize;
}
public static Bitmap decodeSampleBitmapFromFile(File file,int reqHeight) throws IOException {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getPath(),options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath(),options);
return rotateImageIfrequired(bitmap,file);
}
}
问题是,当我调用imageBitmap = BitmapLoader.decodeSampleBitmapFromFile(file,100);
时,它总是引发异常。我认为问题是我从返回的Uri中创建File对象的部分。有人可以向我描述问题的出处,并帮助我编写正确的代码吗?
解决方法
我找到了解决问题的正确方法。这是注释后的代码,描述了解决问题的真正方法。
private void pickFromGallery() {
// intent with ACTION_OPEN_DOCUMENT to make
// content providers (gallery application,downloads application and so on)
// to show their files
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
//and that files must be openable
intent.addCategory(Intent.CATEGORY_OPENABLE);
//setting mime type to get only image files
intent.setType("image/*");
//just invoking intent
startActivityForResult(intent,GALLERY_REQUEST_CODE);
}
//getting picked image
public void onActivityResult(int requestCode,int resultCode,@Nullable Intent data) {
super.onActivityResult(requestCode,resultCode,data);
// Result code is RESULT_OK only if the user selects an Image
if(resultCode == Activity.RESULT_OK) {
Bitmap imageBitmap;
switch(requestCode) {
case GALLERY_REQUEST_CODE:
if(data == null) return;
//ParcelFileDescriptor allowing you to close it when done with it.
ParcelFileDescriptor parcelFileDescriptor;
try {
//a method to get file descriptor via Uri(data.getData() returns a Uri,"r" means for reading)
parcelFileDescriptor = getContentResolver().openFileDescriptor(data.getData(),"r");
//getting a descriptor from ParcelFileDescriptor
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
// and sending that descriptor to BitmapLoader,which now takes only descriptor and required width and height to load bitmap
imageBitmap = BitmapLoader.decodeSampleBitmapFromDescriptor(fileDescriptor,100,100);
parcelFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this,"Error while reading a file!",Toast.LENGTH_SHORT).show();
return;
}
//here you can use the loaded bitmap as you like
userImage.setImageBitmap(imageBitmap);
break;
}
}
}
最后是Bitmaploader类
public class BitmapLoader {
private BitmapLoader() {}
//this method uses ExifInterface to figure out how to rotate the image to bring it back to normal orientation,but that's another story.
private static Bitmap rotateImageIfRequired(Bitmap img,FileDescriptor descriptor) throws IOException {
ExifInterface ei = new ExifInterface(descriptor);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img,90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img,180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img,270);
default:
return img;
}
}
//just a helper for the previous method
private static Bitmap rotateImage(Bitmap img,int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img,img.getWidth(),img.getHeight(),matrix,true);
img.recycle();
return rotatedImg;
}
// calculates how many times a bitmap should be reduced
private static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize += 1;
}
}
return inSampleSize;
}
//main working method that throws an IOException because there might be problems reading the file
public static Bitmap decodeSampleBitmapFromDescriptor(@NonNull FileDescriptor descriptor,int reqHeight) throws IOException {
// BitmapFactory.Options helps to load only the image information first.
final BitmapFactory.Options options = new BitmapFactory.Options();
// must set to true to load only the image information
options.inJustDecodeBounds = true;
/**null is just a padding*/
//loading into options the information about image
BitmapFactory.decodeFileDescriptor(descriptor,null,options);
// Calculation of the dimensions of the image for loading in accordance with the required width and height
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
// now setting to false to load real bitmap with required dimensions
options.inJustDecodeBounds = false;
//decoding an image and returning a bitmap
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(descriptor,options);
return rotateImageIfRequired(bitmap,descriptor);
}
}
以减小的大小将图像加载到内存中非常重要,因为真实图像的位图可能需要超过500MB的RAM。