Android:从文件中旋转和显示图像

9
我有一个非常简单的布局,其中包含一个ImageView。我的应用程序打开相机,保存图像,然后使用BitmapFactory.decodeFile在ImageView中显示该图像。唯一的问题是它被旋转了。我明白a)这是由于手机相机默认为横向,因此需要在代码中处理,以及b)图像处理必须在与UI分开的线程中进行。
Android培训文档似乎更适合从资源ID而不是文件路径加载图像。这只会使事情变得复杂,因为我能够按照教程进行操作,但最终难以协调差异。
我肯定是新手,如果有人可以给我一个高级别的概述,说明需要完成什么,我会非常感激。以下代码是我目前将位图添加到ImageView的位置。我假设我对独立线程的调用将在onCreate中进行?
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_display_image);

    Bitmap bm = BitmapFactory.decodeFile(MainActivity.image_path);

    displayImageView = (ImageView) findViewById(R.id.myImageView);

    displayImageView.setImageBitmap(bm);
}

你只想旋转图片? - Rod_Algonquin
正确。ImageView本身定位正确。只是其中的图像总是逆时针旋转90度显示。然而,文件本身定位正确,所以我只需要在应用程序中将其旋转即可。 - user2864874
4个回答

9

使用ExifInterface进行旋转

picturePath = getIntent().getStringExtra("path");
Bitmap bitmap = BitmapFactory.decodeFile(picturePath);
ExifInterface exif = new ExifInterface(picturePath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);

Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
    matrix.postRotate(90);
    break;
case ExifInterface.ORIENTATION_ROTATE_180:
    matrix.postRotate(180);
    break;
case ExifInterface.ORIENTATION_ROTATE_270:
    matrix.postRotate(270);
    break;
default:
    break;
}

myImageView.setImageBitmap(bitmap);
bitmap.recycle();

注意:这里的picturePath是从相册中选择的图片路径


这个程序是可以工作的,但是它引发了一个额外的问题,我想确认一下。我在一个单独的线程中进行操作,所以直到onPostExecute方法被调用之前,我实际上无法渲染位图。再次强调 - 它的工作非常好,但我只是想确保当传递到onPostExecute时,它不会创建额外的位图。据我理解,Java只传递一个_引用_,因此整个过程实际上只创建了一个位图。这是正确的吗? - user2864874
不要传递位图,而是传递“picturePath”,并在“onPostExecute”中处理位图。 - Kaushik
onPostExecute 在 UI 线程上运行。虽然我可能对此有错误的理解,但我的印象是所有位图处理都需要在非 UI 线程中完成。然而,我突然想到,我只是加载了位图的缩小版本,并且它是从内存中加载的 - 而不是通过网络或其他方式加载的。只要我没有加载整个未缩放的图像,这样在 UI 线程中完成是否可以呢? - user2864874
ه°†ن»£ç پ移ه…¥onActivityResult(...)ن¸­ï¼Œن¸چ需è¦پscaled bitmapم€‚ - Kaushik
仍在逐渐熟悉设备监视器,但似乎使用采样图像而非未采样图像可以节省一些内存。该文件由相机生成,因此原始位图相当大。 - user2864874

2
我能够使用以下方法使其工作。我认为您可以在没有AsyncTask的情况下完成此操作,但最佳实践表明应在非UI线程中执行图像处理(请随时更正-我是新手)。
对此的一个改进是以更细粒度的方式处理缩放。inSampleSize是一个很好的工具,但它只会按2的幂进行缩放。另一个改进是最初仅将元数据/ EXIF数据读入bmOriginal,因为该变量的唯一用途是检索高度和宽度。
ImageView XML-几乎标准。android:layout_width/height设置为fill_parent。
从Activity的Java文件中-
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_display_image);

    new ImageRotator().execute(MainActivity.image_path);
}

private class ImageRotator extends AsyncTask<String, Void, Bitmap>{
    protected Bitmap doInBackground(String... image_path){

        BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
        bitmapOptions.inSampleSize = 4;
        Bitmap bmOriginal = BitmapFactory.decodeFile(image_path[0], bitmapOptions);

        Matrix matrix = new Matrix();

        matrix.postRotate(90);

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

        return bmOriginal;
    }

    protected void onPostExecute(Bitmap result) {
        myImageView = (ImageView) findViewById(R.id.myImageView);

        myImageView.setImageBitmap(result);
    }
}

在这里发现了扩展过程,这里有一个关于AsyncTask特性的很好的解释

非常感谢所有回复的人,再次强调-如果这样做有偏差甚至只是有更好的方法,请随时让我知道。


1

为什么要旋转图像。正如您所说,“ImageView本身已经正确定位。只是其中的图像始终以逆时针90度的方式显示。”,那么只需将ImageView顺时针旋转90度,图像就会正确显示。

    displayImageView.setRotation(90);

这样确实可以正确地定位图像,但现在周围有很多白色空间。我的android:layout_width/height设置为fill_parent。似乎最终ImageView的高度是我的显示器宽度。我假设旋转发生在活动创建视图之后。是否有一种方法可以“刷新”并重新应用fill_parent?谢谢。 - user2864874
@user2864874,你能在你原来的问题中发布一个截图吗? - Code-Apprentice
如果您将ImageView的layout_height/width设置为fill/match_parent,则图像外部的空间应该是透明的。您提到的白色空间应该是底层视图的颜色。也许您需要缩放ImageView以填充空间? - wrkwrk

0

您可以使用矩阵或在xml中设置旋转来旋转图像。但是,如果您想要更多的控制权,可以查看这篇教程,它是为了旋转图像并保存到磁盘而编写的:

如何在Android中旋转图像

希望这有所帮助。


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