Android-камера необъяснимое вращение при захвате для некоторых устройств (не в EXIF)
То, что я делаю, похоже, должно быть простым, но я все еще теряюсь после того, как прочитал каждый возможный ответ Stackoverflow, который я могу найти, и Googled каждую статью, которую я могу найти.
Я использую предварительный просмотр SurfaceView и захват изображения из действия, которое задано для screenOrientation = "landscape" в моем AndroidManifest.xml.
Я следил за образцом кода камеры и думал, что все работает до тех пор, пока я не попробовал свое приложение на нескольких устройствах Motorola, работающих под управлением 1.5.
У меня есть OrientationEventListener, работающий нормально, и я использую отражение, чтобы увидеть, задано ли вращение как таковое:
final int latchedOrientation = roundOrientation(mLastOrientation + 90);
Parameters parameters = preview.camera.getParameters();
JPLog.d("Setting camera rotation = %d", latchedOrientation);
try {
// if >= 2.0
Method method = Camera.Parameters.class.getMethod("setRotation",
int.class);
if(method != null) {
method.invoke(parameters, latchedOrientation);
}
} catch(Throwable t) {
// if < 2.0
parameters.set("rotation", latchedOrientation);
}
preview.camera.setParameters(parameters);
NexusOne (OS 2.2) - отлично работает. lathedOrientation = 0, изображение ОК без какого-либо вращения в заголовке EXIF.
T-Mobile G1 (OS 1.6) - также отлично работает. lathedOrientation = 0, изображение ОК.
Motorola Backflip (OS 1.5). Изображение повернуто. latchedOrientation = 0, изображение не имеет вращения EXIF.
Motorola CLIQ (OS 1.5). Изображение повернуто. latchedOrientation = 0, изображение не имеет вращения EXIF.
Что происходит с этими устройствами Motorola? Я думал, что моя проблема заключается в том, что драйвер камеры Motorola не вращает изображения, поэтому нашел классы чтения Sanselan EXIF для Android и готов был сам повернуть их. Смешно, что есть EXIF-заголовки, но нет элемента вращения.
Если я установил поворот вручную на 90 градусов, изображения получились превосходными устройствами Motorola, но теперь у G1 и NexusOne есть изображения, которые поворачиваются на 90 градусов (не то, что я хочу). Должно быть кое-что, что я не получаю здесь.
Я сомневаюсь, что это проблема 1.5, иначе кто-то разместил бы информацию об этом?
Ответы
Ответ 1
На самом деле это проблема, связанная с устройством, которая в основном затрагивает устройства Motorola. Разработчики google включили вызов setDisplayOrientation в API 8, чтобы обойти эту проблему. Основная ошибка представлена здесь.
Для тех, кто не может перейти в API 8, два общих решения:
Переопределить onDraw
Переопределите onDraw в верхней группе ViewGroup и поверните холст на 90 градусов, чтобы компенсировать вращение. Обратите внимание, что здесь есть оговорка, так как ваши сенсорные события также должны быть повернуты.
Использовать ландшафтный режим
Заблокируйте активность в ландшафтном режиме, но нарисуйте активы так, как если бы они были в портрете. Это означает, что вы делаете свой макет и поворачиваете свои изображения, чтобы выглядеть так, как будто вы находитесь в портретном режиме, чтобы изображение выглядело нормально. Это, к сожалению, затрудняет использование меню, так как меню открывается горизонтально.
Я также видел, что люди используют контроллер анимации для поворота представления. Недостатком здесь, который я не смог преодолеть, является то, что повернутое изображение не растягивается, чтобы заполнить экран. См. ответ Георга для примера реализации.
Ответ 2
У меня была эта проблема, и я использовал этот метод для захвата изображения. (Без создания пользовательской камеры)
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(intent, 0);
а остальное - в onActivityResult (int requestCode, int resultCode, Intent data) {}
Но исходное изображение (Фактическое изображение SD-карты) было правильным, и битмап был повернут, когда я выбрал это.
Bitmap bmp = BitmapFactory.decodeStream(..
Решение:
try {
File f = new File(SD_CARD_IMAGE_PATH);
ExifInterface exif = new ExifInterface(f.getPath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int angle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
angle = 90;
}
else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
angle = 180;
}
else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
angle = 270;
}
Matrix mat = new Matrix();
mat.postRotate(angle);
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
Bitmap correctBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
}
catch (IOException e) {
Log.w("TAG", "-- Error in setting image");
}
catch(OutOfMemoryError oom) {
Log.w("TAG", "-- OOM Error in setting image");
}
Ответ 3
Вот код, который я использовал onActivityResult() в своей деятельности. Возвращаемое намерение состояло в том, чтобы выбрать изображение типа image/*. Хорошо работает для меня!
Uri imageUri = intent.getData();
String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
int orientation = -1;
if (cur != null && cur.moveToFirst()) {
orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
}
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
Ответ 4
Похоже, что предложение "использовать ландшафтный режим" - единственное, что действительно работает. Кажется, что это нормально для того, чтобы это было либо в манифесте, либо сделано путем вызова setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
в операции onCreate.