Android studio загружает растровое изображение через результат действия

#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 МБ оперативной памяти.