我正在捕捉图像并将其设置为图像视图。

public void captureImage() {

    Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
    File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
    imageUri = Uri.fromFile(filePhoto);
    MyApplicationGlobal.imageUri = imageUri.getPath();
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intentCamera, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intentFromCamera) {
    super.onActivityResult(requestCode, resultCode, intentFromCamera);

    if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {

        if (intentFromCamera != null) {
            Bundle extras = intentFromCamera.getExtras();
            if (extras.containsKey("data")) {
                bitmap = (Bitmap) extras.get("data");
            }
            else {
                bitmap = getBitmapFromUri();
            }
        }
        else {
            bitmap = getBitmapFromUri();
        }
        // imageView.setImageBitmap(bitmap);
        imageView.setImageURI(imageUri);
    }
    else {
    }
}

public Bitmap getBitmapFromUri() {

    getContentResolver().notifyChange(imageUri, null);
    ContentResolver cr = getContentResolver();
    Bitmap bitmap;

    try {
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
        return bitmap;
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

但问题是,某些设备上的图像每次旋转时。例如,在三星设备上,它工作得很好,但在索尼Xperia上,图像旋转了90度,在东芝Thrive(平板电脑)上旋转了180度。


当前回答

如果你用的是Fresco,你可以用这个

final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
.setRotationOptions(RotationOptions.autoRotate())
.build();

mSimpleDraweeView.setController(
Fresco.newDraweeControllerBuilder()
    .setImageRequest(imageRequest)
    .build());

这将根据Exif数据自动旋转图像。

来源:https://frescolib.org/docs/rotation.html

其他回答

你可以像文档中的谷歌那样读取摄像头传感器的方向:https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html

SENSOR_ORIENTATION

Added in API level 21
Key<Integer> SENSOR_ORIENTATION
Clockwise angle through which the output image needs to be rotated to be upright on the device screen in its native orientation.

Also defines the direction of rolling shutter readout, which is from top to bottom in the sensor's coordinate system.

Units: Degrees of clockwise rotation; always a multiple of 90

Range of valid values:
0, 90, 180, 270

This key is available on all devices.

示例代码:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
int orientation = 0;
try {
    String cameraId = manager.getCameraIdList()[0];
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
    orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
}
catch (Exception e)
{
}

很容易检测图像方向和替换位图使用:

 /**
 * Rotate an image if required.
 * @param img
 * @param selectedImage
 * @return
 */
private static Bitmap rotateImageIfRequired(Context context,Bitmap img, Uri selectedImage) {

    // Detect rotation
    int rotation = getRotation(context, selectedImage);
    if (rotation != 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
    else{
        return img;
    }
}

/**
 * Get the rotation of the last image added.
 * @param context
 * @param selectedImage
 * @return
 */
private static int getRotation(Context context,Uri selectedImage) {

    int rotation = 0;
    ContentResolver content = context.getContentResolver();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                       new String[] { "orientation", "date_added" },
                                       null, null, "date_added desc");

    if (mediaCursor != null && mediaCursor.getCount() != 0) {
        while(mediaCursor.moveToNext()){
            rotation = mediaCursor.getInt(0);
            break;
        }
    }
    mediaCursor.close();
    return rotation;
}

为了避免大图像的内存不足,我建议您使用以下方法重新缩放图像:

private static final int MAX_HEIGHT = 1024;
private static final int MAX_WIDTH = 1024;
public static Bitmap decodeSampledBitmap(Context context, Uri selectedImage)
    throws IOException {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
    BitmapFactory.decodeStream(imageStream, null, options);
    imageStream.close();

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    imageStream = context.getContentResolver().openInputStream(selectedImage);
    Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);

    img = rotateImageIfRequired(img, selectedImage);
    return img;
}

由于Android操作系统的问题,不能使用ExifInterface来获取方向: https://code.google.com/p/android/issues/detail?id=19268

这里是calculateInSampleSize

/**
 * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
 * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
 * the closest inSampleSize that will result in the final decoded bitmap having a width and
 * height equal to or larger than the requested width and height. This implementation does not
 * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
 * results in a larger bitmap which isn't as useful for caching purposes.
 *
 * @param options   An options object with out* params already populated (run through a decode*
 *                  method with inJustDecodeBounds==true
 * @param reqWidth  The requested width of the resulting bitmap
 * @param reqHeight The requested height of the resulting bitmap
 * @return The value to be used for inSampleSize
 */
public 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) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
        // with both dimensions larger than or equal to the requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}

Jason Robinson和Sami Eltamawy的回答非常棒。

只是为了完成该方法的改进,您应该使用compat ExifInterface。

com.android.support: exifinterface: $ {lastLibVersion}

你将能够实例化ExifInterface(pior API <24)与InputStream(从ContentResolver)而不是uri路径避免“文件未找到异常”

https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html

首先,你应该知道,自Android 7.0以来,我们必须使用FileProvider和ContentUri,否则你会得到一个烦人的错误,试图调用你的意图。这是示例代码:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriFromPath(context, "[Your path to save image]"));
startActivityForResult(intent, CAPTURE_IMAGE_RESULT);

方法getUriFromPath(Context, String)基于用户版本的Android创建FileUri (file://…)或ContentUri (content://…)

public Uri getUriFromPath(Context context, String destination) {
    File file =  new File(destination);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        return FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
    } else {
        return Uri.fromFile(file);
    }
}

onActivityResult后,你可以捕捉图像被相机保存的uri,但现在你必须检测相机旋转,这里我们将使用修改后的@Jason Robinson回答:

首先,我们需要基于Uri创建ExifInterface

@Nullable
public ExifInterface getExifInterface(Context context, Uri uri) {
    try {
        String path = uri.toString();
        if (path.startsWith("file://")) {
            return new ExifInterface(path);
        }
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (path.startsWith("content://")) {
                InputStream inputStream = context.getContentResolver().openInputStream(uri);
                return new ExifInterface(inputStream);
            }
        }
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

上面的代码可以简化,但我想显示一切。因此,从FileUri我们可以创建基于字符串路径的ExifInterface,但从ContentUri我们不能,Android不支持。

在这种情况下,我们必须使用基于InputStream的其他构造函数。记住这个构造函数默认是不可用的,你必须添加额外的库:

compile "com.android.support:exifinterface:XX.X.X"

现在我们可以使用getExifInterface方法来获取我们的角度:

public float getExifAngle(Context context, Uri uri) {
    try {
        ExifInterface exifInterface = getExifInterface(context, uri);
        if(exifInterface == null) {
            return -1f;
        }

        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return 90f;
            case ExifInterface.ORIENTATION_ROTATE_180:
                return 180f;
            case ExifInterface.ORIENTATION_ROTATE_270:
                return 270f;
            case ExifInterface.ORIENTATION_NORMAL:
                return 0f;
            case ExifInterface.ORIENTATION_UNDEFINED:
                return -1f;
            default:
                return -1f;
        }
    }
    catch (Exception e) {
        e.printStackTrace();
        return -1f;
    }
}

现在你有角度适当旋转你的图像。

使用Glide库对我很有用。旋转是自动的。

Bitmap bitmap = Glide.with(myContext).asBitmap().load(imageFilePath).submit(SIZE_ORIGINAL, SIZE_ORIGINAL).get();

然后将该位图保存为JPEG格式的文件。

如果你只是想加载到一个ImageView而不是保存到一个文件:

Glide.with(myContext).load(imageFilePath).into(myImageView)