Android从图库获取图片时会旋转。

64

我正在尝试让用户从相册中选择个人资料图片。我的问题是,有些图片会显示为向右旋转。

我这样开始图片选择器:

Intent photoPickerIntent = new Intent();
photoPickerIntent.setType("image/*");
photoPickerIntent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(photoPickerIntent, "Select profile picture"), Global.CODE_SELECT_PICTURE);

我这样从onActivityResult中获取图像:

Uri selectedPicture = data.getData();
profilePic = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), selectedPicture);

如何使图像不旋转?

更新:

在收到一些有用的回答后,我成功地提出了以下可行的解决方案(这只是一个可行的代码,没有很好地编写)。我希望能够听取您关于改进它的反馈!

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (resultCode == Activity.RESULT_OK && requestCode == Global.CODE_SELECT_PICTURE) {

        // Get selected gallery image
        Uri selectedPicture = data.getData();
        // Get and resize profile image
        String[] filePathColumn = {MediaStore.Images.Media.DATA};
        Cursor cursor = activity.getContentResolver().query(selectedPicture, filePathColumn, null, null, null);
        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String picturePath = cursor.getString(columnIndex);
        cursor.close();

        Bitmap loadedBitmap = BitmapFactory.decodeFile(picturePath);

        ExifInterface exif = null;
        try {
            File pictureFile = new File(picturePath);
            exif = new ExifInterface(pictureFile.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }

        int orientation = ExifInterface.ORIENTATION_NORMAL;

        if (exif != null)
            orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                loadedBitmap = rotateBitmap(loadedBitmap, 90);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                loadedBitmap = rotateBitmap(loadedBitmap, 180);
                break;

            case ExifInterface.ORIENTATION_ROTATE_270:
                loadedBitmap = rotateBitmap(loadedBitmap, 270);
                break;
        }           
    }
}

public static Bitmap rotateBitmap(Bitmap bitmap, int degrees) {
    Matrix matrix = new Matrix();
    matrix.postRotate(degrees);
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

你的解决方案适用于每个软件/设备吗?我遇到了同样的问题... - tsiro
谢谢。它像魔法一样奏效了。下面的答案在我的情况下不起作用。 - Ritesh Bhavsar
使用正确的方向从URI获取位图。 - Atul
这个不起作用,我会把pictureFile清空。 - rossinelo
7个回答

73

你可以使用ExifInterface来修改方向:

public static Bitmap modifyOrientation(Bitmap bitmap, String image_absolute_path) throws IOException {
    ExifInterface ei = new ExifInterface(image_absolute_path);
    int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

    switch (orientation) {
    case ExifInterface.ORIENTATION_ROTATE_90:
        return rotate(bitmap, 90);

    case ExifInterface.ORIENTATION_ROTATE_180:
        return rotate(bitmap, 180);

    case ExifInterface.ORIENTATION_ROTATE_270:
        return rotate(bitmap, 270);

    case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
        return flip(bitmap, true, false);

    case ExifInterface.ORIENTATION_FLIP_VERTICAL:
        return flip(bitmap, false, true);

    default:
        return bitmap;
    }
}

public static Bitmap rotate(Bitmap bitmap, float degrees) {
    Matrix matrix = new Matrix();
    matrix.postRotate(degrees);
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

public static Bitmap flip(Bitmap bitmap, boolean horizontal, boolean vertical) {
    Matrix matrix = new Matrix();
    matrix.preScale(horizontal ? -1 : 1, vertical ? -1 : 1);
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

想要从图像的URI获取其绝对路径,请查看此答案


1
我使用这个解决方案时出现了OutOfMemoryError。 - Mohammad Tazehkar
2
你所提到的获取图像绝对路径的答案是错误的做法。不应该这样做。请参考CommonsWare在答案中的评论。 - Atul

15

使用Picasso和Glide库的一行代码解决方案

在花费大量时间寻找解决图像旋转问题的解决方案后,我最终找到了两种简单的解决方案。我们不需要做任何额外的工作。

使用Picasso library https://github.com/square/picasso

Picasso.with(context).load("http url or sdcard url").into(imageView);

使用 Glide 库 https://github.com/bumptech/glide

Glide.with(this).load("http url or sdcard url").into(imgageView);

Picasso和Glide是处理应用程序中图像的非常强大的库。它可以读取图像EXIF数据并自动旋转图像。


太好了,不需要存储权限(而 ExifInterface 需要)。 - Pavel
使用Glide获取位图:Glide.with(context).asBitmap().load(uri).into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) { /* 现在你有了位图资源 */ } }); - Pavel
2
从我的角度来看,如果将图像转换为位图,则无法正常工作。如果直接将URI传递给Glide,则可以解决问题。感谢分享解决方案。 - thanhbinh84

9

我使用这些静态方法。第一个确定方向,第二个旋转图像并根据需要缩小它。

public static int getOrientation(Context context, Uri photoUri) {

    Cursor cursor = context.getContentResolver().query(photoUri,
            new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);

    if (cursor == null || cursor.getCount() != 1) {
        return 90;  //Assuming it was taken portrait
    }

    cursor.moveToFirst();
    return cursor.getInt(0);
}

/**
* Rotates and shrinks as needed
*/
public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri, int maxWidth)
                throws IOException {

            InputStream is = context.getContentResolver().openInputStream(photoUri);
            BitmapFactory.Options dbo = new BitmapFactory.Options();
            dbo.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, dbo);
            is.close();


            int rotatedWidth, rotatedHeight;
            int orientation = getOrientation(context, photoUri);

            if (orientation == 90 || orientation == 270) {
                Log.d("ImageUtil", "Will be rotated");
                rotatedWidth = dbo.outHeight;
                rotatedHeight = dbo.outWidth;
            } else {
                rotatedWidth = dbo.outWidth;
                rotatedHeight = dbo.outHeight;
            }

            Bitmap srcBitmap;
            is = context.getContentResolver().openInputStream(photoUri);
            Log.d("ImageUtil", String.format("rotatedWidth=%s, rotatedHeight=%s, maxWidth=%s",
                    rotatedWidth, rotatedHeight, maxWidth));
            if (rotatedWidth > maxWidth || rotatedHeight > maxWidth) {
                float widthRatio = ((float) rotatedWidth) / ((float) maxWidth);
                float heightRatio = ((float) rotatedHeight) / ((float) maxWidth);
                float maxRatio = Math.max(widthRatio, heightRatio);
                Log.d("ImageUtil", String.format("Shrinking. maxRatio=%s",
                        maxRatio));

                // Create the bitmap from file
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = (int) maxRatio;
                srcBitmap = BitmapFactory.decodeStream(is, null, options);
            } else {
                Log.d("ImageUtil", String.format("No need for Shrinking. maxRatio=%s",
                        1));

                srcBitmap = BitmapFactory.decodeStream(is);
                Log.d("ImageUtil", String.format("Decoded bitmap successful"));
            }
            is.close();

        /*
         * if the orientation is not 0 (or -1, which means we don't know), we
         * have to do a rotation.
         */
            if (orientation > 0) {
                Matrix matrix = new Matrix();
                matrix.postRotate(orientation);

                srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
                        srcBitmap.getHeight(), matrix, true);
            }

            return srcBitmap;
        }

您可能希望在生产版本中删除Log.d调用。 - Alberto Gaona
你忘记关闭游标了,这会导致内存泄漏。 - Andrea De Gaetano

4

以下是用 Kotlin 编写的 ExifInterface 方法:

fun modifyOrientation(bitmap: Bitmap, image_absolute_path: String): Bitmap {
    val ei = ExifInterface(image_absolute_path)
    val orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
    return when (orientation) {
        ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage(bitmap, 90f)
        ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage(bitmap, 180f)
        ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage(bitmap, 270f)
        ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> flipImage(bitmap, true, false)
        ExifInterface.ORIENTATION_FLIP_VERTICAL -> flipImage(bitmap, false, true)
        else -> bitmap
    }
}

private fun rotateImage(bitmap: Bitmap, degrees: Float): Bitmap {
    val matrix = Matrix()
    matrix.postRotate(degrees)
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}

private fun flipImage(bitmap: Bitmap, horizontal: Boolean, vertical: Boolean): Bitmap {
    val matrix = Matrix()
    matrix.preScale((if (horizontal) -1 else 1).toFloat(), (if (vertical) -1 else 1).toFloat())
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}

3
我是这样做的:
public void browseClick(View view) {
    view.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(), R.anim.button_animation));
    Intent i = new Intent(
            Intent.ACTION_PICK,
            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

    startActivityForResult(i, RESULT_LOAD_IMAGE);
}

当检查方向时,最让您感兴趣的是结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
        Uri selectedImage = data.getData();
        String[] filePathColumn = { MediaStore.Images.Media.DATA };

        Cursor cursor = getContentResolver().query(selectedImage,
                filePathColumn, null, null, null);
        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String picturePath = cursor.getString(columnIndex);
        cursor.close();

        Bitmap loadedBitmap = BitmapFactory.decodeFile(picturePath);

        Matrix matrix = new Matrix();
        Bitmap scaledBitmap;
        if (loadedBitmap.getWidth() >= loadedBitmap.getHeight()){
            matrix.setRectToRect(new RectF(0, 0, loadedBitmap.getWidth(), loadedBitmap.getHeight()), new RectF(0, 0, 400, 300), Matrix.ScaleToFit.CENTER);
            scaledBitmap = Bitmap.createBitmap(loadedBitmap, 0, 0, loadedBitmap.getWidth(), loadedBitmap.getHeight(), matrix, true);
        } else{
            matrix.setRectToRect(new RectF(0, 0, loadedBitmap.getWidth(), loadedBitmap.getHeight()), new RectF(0, 0, 300, 400), Matrix.ScaleToFit.CENTER);
            scaledBitmap = Bitmap.createBitmap(loadedBitmap, 0, 0, loadedBitmap.getWidth(), loadedBitmap.getHeight(), matrix, true);
        }

        File file = new File(getExternalCacheDir(), "image.jpg");
        try {
            FileOutputStream out = new FileOutputStream(file);
            scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
            out.flush();
            out.close();
        } catch (Exception e) {
            Log.e("Image", "Convert");
        }

        imageView.setImageBitmap(scaledBitmap);
    }
}

1
使用 Picasso:
Picasso
.get()
.load("Your image path")
.into(imageView);

Picasso.with(this) 现在被替换为 Picasso.get()

Picasso库: https://github.com/square/picasso

在build.gradle文件中添加:implementation 'com.squareup.picasso:picasso:2.71828'

Picasso将负责图像自动旋转。


0
我找到的更简单的解决方案是使用 Kotlin,在我的情况下完全有效:
internal object ImageHandler {
    fun retrieveImageFromUri(
        uri: Uri,
        contentResolver: ContentResolver,
    ): Bitmap {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            val source = ImageDecoder.createSource(contentResolver, uri)
            ImageDecoder.decodeBitmap(source)
        } else {
            legacyImageRetrieval(uri, contentResolver)
        }
    }

    private fun legacyImageRetrieval(
        uri: Uri,
        contentResolver: ContentResolver
    ): Bitmap {
        val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
        val imageStream = contentResolver.openInputStream(uri)
        return if (imageStream != null) {
            rotateImage(retrieveImageRotation(imageStream), bitmap)
        } else {
            bitmap
        }
    }

    private fun retrieveImageRotation(imageStream: InputStream): Float {
        val exif = ExifInterface(imageStream)

        val orientation = exif.getAttributeInt(
            ExifInterface.TAG_ORIENTATION,
            ExifInterface.ORIENTATION_UNDEFINED,
        )

        return when (orientation) {
            ExifInterface.ORIENTATION_ROTATE_90 -> 90f
            ExifInterface.ORIENTATION_ROTATE_180 -> 180f
            ExifInterface.ORIENTATION_ROTATE_270 -> 270f
            else -> 0f
        }
    }

    private fun rotateImage(
        rotation: Float,
        image: Bitmap,
    ): Bitmap {
        val matrix = Matrix()
        matrix.postRotate(rotation)
        return Bitmap.createBitmap(
            image, 0, 0, image.width, image.height,
            matrix, true
        )
    }
}

在您启动图像检索意图的活动中,您可以这样做:
private val imageResultLauncher =
        registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()){
                uris.map{ ImageHandler.retrieveImageFromUri(it, contentResolver) }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接