Android Studio通过活动结果加载位图

问题描述

在我的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。