#android-studio #bitmap #onactivityresult
#android-studio #растровое изображение #onactivityresult
Вопрос:
В моем проекте 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, 0, 0, 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
amp;amp; (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize = 1;
}
}
return inSampleSize;
}
public static Bitmap decodeSampleBitmapFromFile(File file, int reqWidth, 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, 100);
он всегда выдает исключение. Я думаю, что проблема заключается в той части, где я создаю файловый объект из Uri, возвращаемого в результате. Может кто-нибудь, пожалуйста, описать мне, откуда возникает проблема, и помочь мне написать правильный код?
Комментарии:
1. Какое исключение выдается?
2. @Benjamin Извините, но я уже нашел правильное решение, и теперь я не могу сказать ничего, кроме того, что это было исключение IOException. Но я настоятельно рекомендую прочитать мой собственный ответ, потому что есть много вещей, которые хорошо освещены.
Ответ №1:
Я нашел правильный способ решить проблему. Вот прокомментированный код, описывающий истинный способ решения проблемы.
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, 0, 0, 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
amp;amp; (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 reqWidth, 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, null, options);
return rotateImageIfRequired(bitmap, descriptor);
}
}
Очень важно загружать изображение в память с уменьшенным размером, поскольку для растрового изображения реального изображения может потребоваться более 500 МБ оперативной памяти.