Предварительный просмотр камеры для Android
Я создал приложение для камеры, основанное на учебнике. класс предварительного просмотра, который я использую, - это api-Demos "CameraPreview". Я добавил модификацию здесь (предварительный просмотр всегда поворачивался на 90 °). Так вот как я установил размер предварительного просмотра:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
mCamera.setDisplayOrientation(90);
}
if (display.getRotation() == Surface.ROTATION_90) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
}
if (display.getRotation() == Surface.ROTATION_180) {
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
}
if (display.getRotation() == Surface.ROTATION_270) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setDisplayOrientation(180);
}
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
Но предварительный просмотр отображается с неправильным соотношением сторон. Это из-за кода выше или, вероятно, из-за макета, который я использую?:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button_capture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/capture" />
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="100dp"
android:layout_height="match_parent"/>
Итак, как получить правильное соотношение сторон? Спасибо заранее.
P.S. я прочитал ответ от: Предварительный просмотр камеры Android выглядит странно
Но это не работает для меня.
Ответы
Ответ 1
Попробуйте изменить размеры предварительного просмотра с добавлением этой функции:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w/h;
if (sizes==null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Find size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
И настройка размеров из этих оптимизированных значений:
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
Надеюсь, это сработает:)
С наилучшими пожеланиями
Хенрик
Ответ 2
Следующий код изменяет ширину/высоту контейнера предварительного просмотра камеры в соответствии с форматом просмотра камеры.
Camera.Size size = camera.getParameters().getPreviewSize();
//landscape
float ratio = (float)size.width/size.height;
//portrait
//float ratio = (float)size.height/size.width;
preview = (FrameLayout) findViewById(R.id.camera_preview);
int new_width=0, new_height=0;
if(preview.getWidth()/preview.getHeight()<ratio){
new_width = Math.round(preview.getHeight()*ratio);
new_height = cameraPreview.getHeight();
}else{
new_width = preview.getWidth();
new_height = Math.round(preview.getWidth()/ratio);
}
preview.setLayoutParams(new FrameLayout.LayoutParams(new_width, new_height));
Ответ 3
проблема в том, что вы делаете макет.
в классе Preview
есть класс onLayout
. Идея его работы состоит в том, чтобы установить дочерний размер SurfaceView
в соответствии с найденным оптимальным Size
. но он не принимает во внимание ротацию, поэтому вам нужно сделать это самостоятельно:
if (mPreviewSize != null) {
previewWidth = mPreviewSize.height;
previewHeight = mPreviewSize.width;
}
вместо
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
Трюк состоит в том, чтобы поменять ширину и высоту, что сделано из-за поворота на 90 градусов, достигнутого
mCamera.setDisplayOrientation(90);
Вы также можете рассмотреть возможность изменения размера предварительного просмотра дочерних элементов в зависимости от ориентации, которую вы установили в
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
//...
}
(в представленном мной коде это всегда для 90-градусного поворота, для 180 вам не нужно ничего делать, а когда вы не устанавливаете никакого вращения, нет необходимости менять ширину и высоту)
Еще одна вещь, которую стоит упомянуть - при расчете getOptimalPreviewSize
для случая, когда у вас есть поворот, и вы меняете ширину и высоту ребенка, вы также должны передавать ширину и высоту родительского (Preview
) в onMeasure
:
if (mSupportedPreviewSizes != null) {
//noinspection SuspiciousNameCombination
final int previewWidth = height;
//noinspection SuspiciousNameCombination
final int previewHeight = width;
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, previewWidth, previewHeight);
}
Ответ 4
Используя приведенное выше решение, используя метод private Size getOptimalPreviewSize (размеры списка, int w, int h).
Отлично работает! У меня были проблемы с соотношением сторон на портретной ориентации: вот это мое решение. Смешивание с документацией на Android:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
Camera.Parameters params = mCamera.getParameters();
params.set("orientation", "portrait");
Size optimalSize=getOptimalPreviewSize(params.getSupportedPreviewSizes(), getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
params.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(params);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
Ответ 5
Ответ Henric не работал у меня, поэтому я создал другой метод, который определяет оптимальный размер предварительного просмотра для любой камеры с учетом текущей ширины и высоты представления цели, а также ориентации активности:
public static Size getOptimalPreviewSize(List<Camera.Size> cameraPreviewSizes, int targetWidth, int targetHeight, boolean isActivityPortrait) {
if (CommonUtils.isEmpty(cameraPreviewSizes)) {
return null;
}
int optimalHeight = Integer.MIN_VALUE;
int optimalWidth = Integer.MIN_VALUE;
for (Camera.Size cameraPreviewSize : cameraPreviewSizes) {
boolean isCameraPreviewHeightBigger = cameraPreviewSize.height > cameraPreviewSize.width;
int actualCameraWidth = cameraPreviewSize.width;
int actualCameraHeight = cameraPreviewSize.height;
if (isActivityPortrait) {
if (!isCameraPreviewHeightBigger) {
int temp = cameraPreviewSize.width;
actualCameraWidth = cameraPreviewSize.height;
actualCameraHeight = temp;
}
} else {
if (isCameraPreviewHeightBigger) {
int temp = cameraPreviewSize.width;
actualCameraWidth = cameraPreviewSize.height;
actualCameraHeight = temp;
}
}
if (actualCameraWidth > targetWidth || actualCameraHeight > targetHeight) {
// finds only smaller preview sizes than target size
continue;
}
if (actualCameraWidth > optimalWidth && actualCameraHeight > optimalHeight) {
// finds only better sizes
optimalWidth = actualCameraWidth;
optimalHeight = actualCameraHeight;
}
}
Size optimalSize = null;
if (optimalHeight != Integer.MIN_VALUE && optimalWidth != Integer.MIN_VALUE) {
optimalSize = new Size(optimalWidth, optimalHeight);
}
return optimalSize;
}
В этом случае используется пользовательский объект размера, потому что после API 21 доступен Size.
public class Size {
private int width;
private int height;
public Size(int width, int height) {
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
}
Вы можете определить ширину и высоту представления, прослушивая его глобальные изменения макета, а затем вы можете установить новые измерения. Это также показывает, как программно определять ориентацию деятельности:
cameraPreviewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// gets called after layout has been done but before display.
cameraPreviewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
boolean isActivityPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
Size optimalCameraPreviewSize = CustomUtils.getOptimalPreviewSize(cameraPreview.getCameraSizes(), cameraPreviewLayout.getWidth(), cameraPreviewLayout.getHeight(), isActivityPortrait);
if (optimalCameraPreviewSize != null) {
LinearLayout.LayoutParams cameraPreviewLayoutParams = new LinearLayout.LayoutParams(optimalCameraPreviewSize.getWidth(), optimalCameraPreviewSize.getHeight());
cameraPreviewLayout.setLayoutParams(cameraPreviewLayoutParams);
}
}
});