Android从图库中获取图像会旋转

Posted

技术标签:

【中文标题】Android从图库中获取图像会旋转【英文标题】:Android getting an image from gallery comes rotated 【发布时间】:2015-11-02 17:32:19 【问题描述】:

我试图让用户从图库中选择个人资料图片。我的问题是有些图片向右旋转。

我像这样启动图像选择器:

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);

【问题讨论】:

您的解决方案是否适用于所有软件/设备?我也面临同样的问题... 谢谢。它就像一个魅力..下面的答案在我的情况下不起作用.. This 从 URI 中获取正确方向的位图 这不起作用我将图片文件清空 【参考方案1】:

您可以使用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 中获取图像的绝对路径,请检查 this answer

【讨论】:

我有 OutOfMemoryError 这个解决方案 你提到的从 uri 获取图像的绝对路径的答案是坏主意。一个人不应该那样做。请参阅 CommonsWare 对答案的评论。【参考方案2】:

2 使用 Picasso 和 glide 库的一条线解决方案

在花了很多时间解决图像旋转问题后,我终于找到了两个简单的解决方案。我们不需要做任何额外的工作。

使用毕加索图书馆 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 可以) 使用 Glide 获取位图:Glide.with(context).asBitmap().load(uri).into(new SimpleTarget<Bitmap>() @Override public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) /* now you have Bitmap resource */ ); 从我的角度来看,如果图像被转换为​​位图,它就不起作用了。如果将 uri 直接传递给 Glide,它会有所帮助。感谢分享解决方案。【参考方案3】:

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

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 调用。【参考方案4】:

我是这样做的:

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);
    

【讨论】:

【参考方案5】:

这是用 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)

【讨论】:

【参考方案6】:

使用毕加索:

Picasso
.get()
.load("Your image path")
.into(imageView);

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

毕加索图书馆:https://github.com/square/picasso

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

Picasso 将负责图像自动旋转。

【讨论】:

以上是关于Android从图库中获取图像会旋转的主要内容,如果未能解决你的问题,请参考以下文章

Android:从图库中获取图像,在回收器适配器中显示它们

Android:从图库加载的位图在 ImageView 中旋转

Android从图库中获取图像

从图库中选择图像会出现错误 Android N

[Android - Kitkat ]以编程方式从 Android 的内置图库应用程序中获取/选择图像

Qt 和 Android - 从图库中的图像获取路径