Предварительный просмотр камеры для Android - программирование
Подтвердить что ты не робот

Предварительный просмотр камеры для 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 выглядит странно Но это не работает для меня.

4b9b3361

Ответ 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);
                }
            }
        });