Попередній перегляд камери Android розтягнутий


136

Я працював над створенням власної активності камери на Android, але при обертанні камери співвідношення сторін у вигляд поверхні псується.

У своєму створенні для активності я встановлюю рамку, що містить вигляд поверхні, що відображає параметри камери.

//FrameLayout that will hold the camera preview
        FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview);

        //Setting camera's preview size to the best preview size
        Size optimalSize = null;
        camera = getCameraInstance();
        double aspectRatio = 0;
        if(camera != null){
            //Setting the camera's aspect ratio
            Camera.Parameters parameters = camera.getParameters();
            List<Size> sizes = parameters.getSupportedPreviewSizes();
            optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
            aspectRatio = (float)optimalSize.width/optimalSize.height;
        }

        if(optimalSize!= null){
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
            previewHolder.setLayoutParams(params);
            LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
            cameraPreview.setLayoutParams(surfaceParams);

        }

        cameraPreview.setCamera(camera);

        //Adding the preview to the holder
        previewHolder.addView(cameraPreview);

Потім у режимі «Поверхня» я встановлюю параметри камери для відображення

public void setCamera(Camera camera) {
        if (mCamera == camera) { return; }

        mCamera = camera;

        if (mCamera != null) {
            requestLayout();

            try {
                mCamera.setPreviewDisplay(mHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }


            if(mCamera != null){
                //Setting the camera's aspect ratio
                Camera.Parameters parameters = mCamera.getParameters();
                List<Size> sizes = parameters.getSupportedPreviewSizes();
                Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);

                parameters.setPreviewSize(optimalSize.width, optimalSize.height);
                mCamera.setParameters(parameters);
            }

            /*
              Important: Call startPreview() to start updating the preview surface. Preview must 
              be started before you can take a picture.
              */
            mCamera.startPreview();
        }

    }

введіть тут опис зображення

Ви можете бачити, що людина LEGO під час обертання телефону зростає і стрункіша:

Як я можу гарантувати правильність співвідношення сторін для перегляду камери?


@ наукова може у вас допомогти мені, де я повинен написати даний метод ім також стикаються з тією ж проблемою?
користувач3233280

У мене, здавалося, виникає подібна проблема, але те, що виправлено, викликало наступне: mCamera.setDisplayOrientation (90) у метод поверхні. Створений () метод мого SurfaceView для камери
Грег,

Відповіді:


163

Я використовую цей метод -> на основі демо-версій API, щоб отримати розмір попереднього перегляду:

private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio=(double)h / w;

        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        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;
    }

Як ви бачите, вам потрібно ввести ширину та висоту екрана. Цей метод обчислить коефіцієнт екрана на основі цих значень, а потім зі списку підтримуванихPreviewSize він вибере найкраще для вас із доступних. Отримайте ваш підтримуваний списокPreviewSize у тому місці, де об’єкт Camera не є нульовим, використовуючи

mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();

А потім у onMeasure ви зможете отримати оптимальний оптимізаційний розмір таким чином:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes != null) {
           mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }
    }

І тоді (у моєму коді методом surfaceChanged, як я вже сказав, я використовую API Demos структури коду CameraActivity, ви можете генерувати його в Eclipse):

Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();

І один натяк для вас, тому що я зробив майже те саме додаток, як і ви. Доброю практикою для діяльності камери є приховування статусу. Такі програми, як Instagram, роблять це. Це зменшує значення висоти екрана та змінює значення співвідношення. На деяких пристроях можна отримати дивні розміри попереднього перегляду (ваш SurfaceView трохи скоротить)


А щоб відповісти на ваше запитання, як перевірити, чи правильно співвідношення попереднього перегляду? Потім отримайте висоту та ширину параметрів, які ви задаєте:

mCamera.setParameters(parameters);

встановлене співвідношення дорівнює висоті / ширині. Якщо ви хочете, щоб камера добре виглядала на екрані, співвідношення висоти / ширини параметрів, встановлених для камери, повинно бути таким же, як співвідношення висоти (мінус смуги стану) / ширини екрана.


2
Тільки одне питання: я бачу ваші використовують 2 різних обчисленого відносини: подвійний targetRatio = (двічі) ч / б і подвійне ставлення = (подвійне) size.width / size.height . Перший - це висота, поділена на ширину, а друга - протилежна, ширина поділена на висоту. Чи не повинні вони бути однаковими обчисленнями?
фізаліс

Це не має значення, оскільки список розмірів враховує будь-яку можливість, наприклад: ви знайдете щось на зразок 480x680, але рано чи пізно буде 680x480 (для пейзажу), тому вам просто потрібно перейти через цей список і знайти той, що відповідає вашим потребам. Ви рано чи пізно його знайдете.
F1sher

Крім того, якщо ви хочете, щоб ваші фотографії були також такого розміру (як вони, мабуть, повинні бути), ви встановили розмір зображення parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height).
Метт Логан

2
@ F1sher, я завжди отримую нульові параметри виключення покажчика.setPreviewSize (mPreviewSize.width, mPreviewSize.height); Не вдається знайти рішення.
FIXI

3
@phyzalis Я з вами згоден. Насправді мені довелося зробити це виправлення, щоб код працював так, як очікувалося.
Ферніо

149

Рішення F1Sher добре, але іноді не працює. Особливо, коли ваш surfaceView не охоплює весь екран. У цьому випадку вам потрібно перекрити метод onMeasure (). Я скопіював тут свій код для ознайомлення.

Оскільки я вимірював поверхневий вигляд на основі ширини, то в кінці екрана у мене є невеликий білий проміжок, який я заповнив дизайн. Ви можете вирішити цю проблему, якщо зберегти висоту та збільшити ширину, помноживши її на співвідношення. Однак це злегка скосить поверхнюView.

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";

    private Context mContext;
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mContext = context;
        mCamera = camera;

        // supported preview sizes
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for(Camera.Size str: mSupportedPreviewSizes)
                Log.e(TAG, str.width + "/" + str.height);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // empty. surfaceChanged will take care of stuff
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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
        // start preview with new settings
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }

        if (mPreviewSize!=null) {
            float ratio;
            if(mPreviewSize.height >= mPreviewSize.width)
                ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
            else
                ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

            // One of these methods should be used, second method squishes preview slightly
            setMeasuredDimension(width, (int) (width * ratio));
  //        setMeasuredDimension((int) (width * ratio), height);
        }
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.height / size.width;
            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;
    }
}

11
це має бути правильна відповідь, він обробляє випадок, коли попередній перегляд камери охоплює не весь екран. +1.
Хоусін

1
коли я надуваю, у моїй діяльності додаток було закрито <com.app.camera.CameraPreview android: id = "@ + id / camera_preview" android: layout_width = "match_parent" android: layout_height = "match_parent" />
fish40

Вам потрібно внести деякі зміни, але я приймаю це як рішення. Вгору проголосували.
JaydeepW

2
Привіт @ adnan9011, цей підручник може бути корисним. airpair.com/android/android-camera-surface-view-fragment
Гесам

4
висоту та ширину, необхідні для включення, getOptimalPreviewSizeщоб це працювало для мене. це тому, що орієнтація дисплея була встановлена ​​на 90? В іншому випадку розрахунки співвідношення не були навіть близькими (ціль становила 1,7, а коефіцієнти камери були менше 1)
ono

23

ПРИМІТКА: МОЕ РІШЕННЯ - ПРОДОВЖЕННЯ РІШЕННЯ ГЕЗАМУ: https://stackoverflow.com/a/22758359/1718734

Що я звертаюся: Гесам сказав, що на деяких телефонах може з’явитися невеликий пробіл, наприклад:

ПРИМІТКА. Хоча співвідношення сторін правильне, камера не заповнює весь екран.

Гесам запропонував друге рішення, але це стискає попередній перегляд. А на деяких пристроях це сильно спотворює.

То як ми можемо вирішити цю проблему. Це просто ... шляхом множення пропорцій, поки воно не заповниться на екрані. Я помітив, кілька популярних додатків, таких як Snapchat, WhatsApp тощо, працюють однаково.

Все, що вам потрібно зробити, - це додати це до методу onMeasure:

float camHeight = (int) (width * ratio);
    float newCamHeight;
    float newHeightRatio;

    if (camHeight < height) {
        newHeightRatio = (float) height / (float) mPreviewSize.height;
        newCamHeight = (newHeightRatio * camHeight);
        Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight);
        setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);
        Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight);
    } else {
        newCamHeight = camHeight;
        setMeasuredDimension(width, (int) newCamHeight);
        Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight);
    }

Це обчислить висоту екрана і отримає співвідношення висоти екрана та висоти mPreviewSize. Потім він помножує ширину і висоту камери на нове співвідношення висоти і відповідно встановлює вимірюваний розмір.

введіть тут опис зображення

І наступне, що ви знаєте, ви закінчуєте це: D

введіть тут опис зображення

Це також добре працює з фронтальною камерою. Я вважаю, що це найкращий шлях для цього. Тепер єдине, що залишилося для мого додатка - це збереження самого попереднього перегляду, натиснувши на "Захопити". Але так, це все.


Це працює дуже добре !! але це не працює належним чином у Landscape: /
Matan Dahan

ваш код вирішив мою 2 місяці порожню проблему з чорним простором :)
karthik kolanji

1
Це працює, але в моєму випадку .. У мене вгорі панель дій, тому біла смуга була меншою, проте після додавання коду вигляд камери збільшувався на 40-50%. Перемикаючись на передню камеру, я в основному не можу бачити, як моє обличчя виглядає прямо (трохи волосся) через таке велике збільшення.
Макалеле

@Yoosuf Я отримую білий екран, коли я стежив за вашим кодом. У onMeasure(int widthMeasureSpec, int heightMeasureSpec)методі я завжди отримую ширину = 0 і висоту = 0;
Kush Patel

10

Гаразд, тому я думаю, що недостатньо відповіді на загальну проблему розтягування попереднього перегляду камери. Або принаймні я його не знайшов. Мій додаток також страждав від цього синдрому розтягування, і мені знадобилося певний час, щоб загадати рішення з усіх відповідей користувачів на цьому порталі та в Інтернеті.

Я намагався @ Hesam в розчині , але це не робота і залишив камеру попереднього перегляду важливо спотворені.

Спочатку я показую код свого рішення (важливі частини коду), а потім пояснюю, чому я зробив ці кроки. Є можливість модифікації продуктивності.

Основна діяльність xml макет:

<RelativeLayout 
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
</RelativeLayout>

Попередній перегляд камери:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder prHolder;
private Camera prCamera;
public List<Camera.Size> prSupportedPreviewSizes;
private Camera.Size prPreviewSize;

@SuppressWarnings("deprecation")
public YoCameraPreview(Context context, Camera camera) {
    super(context);
    prCamera = camera;

    prSupportedPreviewSizes = prCamera.getParameters().getSupportedPreviewSizes();

    prHolder = getHolder();
    prHolder.addCallback(this);
    prHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder) {
    try {
        prCamera.setPreviewDisplay(holder);
        prCamera.startPreview();
    } catch (IOException e) {
        Log.d("Yologram", "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    if (prHolder.getSurface() == null){
      return;
    }

    try {
        prCamera.stopPreview();
    } catch (Exception e){
    }

    try {
        Camera.Parameters parameters = prCamera.getParameters();
        List<String> focusModes = parameters.getSupportedFocusModes();
        if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        parameters.setPreviewSize(prPreviewSize.width, prPreviewSize.height);

        prCamera.setParameters(parameters);
        prCamera.setPreviewDisplay(prHolder);
        prCamera.startPreview();

    } catch (Exception e){
        Log.d("Yologram", "Error starting camera preview: " + e.getMessage());
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    setMeasuredDimension(width, height);

    if (prSupportedPreviewSizes != null) {
        prPreviewSize = 
            getOptimalPreviewSize(prSupportedPreviewSizes, width, height);
    }    
}

public Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) h / w;

    if (sizes == null)
        return null;

    Camera.Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    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;
}
}

Основна діяльність:

public class MainActivity extends Activity {

...

@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);     
    setContentView(R.layout.activity_main);

        maCamera = getCameraInstance();

        maLayoutPreview = (FrameLayout) findViewById(R.id.camera_preview);

        maPreview = new CameraPreview(this, maCamera);

        Point displayDim = getDisplayWH();
        Point layoutPreviewDim = calcCamPrevDimensions(displayDim, 
                maPreview.getOptimalPreviewSize(maPreview.prSupportedPreviewSizes, 
                    displayDim.x, displayDim.y));
        if (layoutPreviewDim != null) {
            RelativeLayout.LayoutParams layoutPreviewParams = 
                (RelativeLayout.LayoutParams) maLayoutPreview.getLayoutParams();
            layoutPreviewParams.width = layoutPreviewDim.x;
            layoutPreviewParams.height = layoutPreviewDim.y;
            layoutPreviewParams.addRule(RelativeLayout.CENTER_IN_PARENT);
            maLayoutPreview.setLayoutParams(layoutPreviewParams);
        }
        maLayoutPreview.addView(maPreview);
}

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private Point getDisplayWH() {

    Display display = this.getWindowManager().getDefaultDisplay();
    Point displayWH = new Point();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        display.getSize(displayWH);
        return displayWH;
    }
    displayWH.set(display.getWidth(), display.getHeight());
    return displayWH;
}

private Point calcCamPrevDimensions(Point disDim, Camera.Size camDim) {

    Point displayDim = disDim;
    Camera.Size cameraDim = camDim;

    double widthRatio = (double) displayDim.x / cameraDim.width;
    double heightRatio = (double) displayDim.y / cameraDim.height;

    // use ">" to zoom preview full screen
    if (widthRatio < heightRatio) {
        Point calcDimensions = new Point();
        calcDimensions.x = displayDim.x;
        calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width;
        return calcDimensions;
    }
    // use "<" to zoom preview full screen
    if (widthRatio > heightRatio) { 
        Point calcDimensions = new Point();
        calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height;
        calcDimensions.y = displayDim.y;
        return calcDimensions;
    }
    return null;
}   
}

Мій коментар:

Сенс всього цього в тому, що , хоча ви обчислити розмір оптимальної камери в getOptimalPreviewSize()вас тільки вибрати найближчий ставлення , щоб відповідати екрану. Тому, якщо співвідношення не буде абсолютно таким же, попередній перегляд розтягнеться.

Чому воно розтягнеться? Оскільки попередній перегляд камери FrameLayout встановлений у layout.xml, щоб він відповідав ширині та висоті. Тож тому попередній перегляд пошириться на весь екран.

Що потрібно зробити, це встановити ширину та висоту макета попереднього перегляду камери, щоб вони відповідали вибраному співвідношенню розміру камери , щоб попередній перегляд зберігав співвідношення сторін і не спотворював.

Я намагався використовувати CameraPreviewклас, щоб зробити всі обчислення та зміни верстки, але не зміг це зрозуміти. Я намагався застосувати це рішення , але SurfaceViewне визнає getChildCount ()або getChildAt (int index). Я думаю, я змусив це працювати врешті з посиланням на maLayoutPreview, але це було недоброзичливо і застосував встановлене співвідношення до всього мого додатка, і це було зроблено після того, як зроблено першу фотографію. Тож я відпустив це і перемістив модифікації верстки до MainActivity.

В CameraPreviewя змінив prSupportedPreviewSizesі getOptimalPreviewSize()на публіку , так що я можу використовувати його в MainActivity. Тоді мені знадобилися розміри дисплея (мінус навігаційна / смуга стану, якщо є) та вибрали оптимальний розмір камери . Я намагався отримати розмір RelativeLayout (або FrameLayout) замість розміру дисплея, але він повертав нульове значення. Це рішення не спрацювало для мене. Макет отримав це значення після onWindowFocusChanged(перевірено в журналі).

Тому я маю свої методи розрахунку розмірів макета, щоб відповідати співвідношенню сторін обраного розміру камери. Тепер вам просто потрібно встановити LayoutParamsмакет попереднього перегляду камери. Змініть ширину, висоту і відцентруйте її в батьківському.

Існує два варіанти обчислення розмірів попереднього перегляду. Ви хочете, щоб він розміщувався на екрані з чорними смугами (якщо встановлено значення "windowBackground") збоку або зверху / внизу. Або ви хочете, щоб попередній перегляд збільшено на весь екран . Я залишив коментар з додатковою інформацією в calcCamPrevDimensions().


6

Привіт, getOptimalPreview (), який тут не працював для мене, тому я хочу поділитися своєю версією:

private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

    if (sizes==null) return null;

    Camera.Size optimalSize = null;
    double ratio = (double)h/w;
    double minDiff = Double.MAX_VALUE;
    double newDiff;
    for (Camera.Size size : sizes) {
        newDiff = Math.abs((double)size.width/size.height - ratio);
        if (newDiff < minDiff) {
            optimalSize = size;
            minDiff = newDiff;
        }
    }
    return optimalSize;
}

вона перетворюється на квадратну камеру. Враховуючи вхід (1920x1080) після оптимізації ширини та висоти, яку він дав (1088x1088), Мій тестовий пристрій - Samsung S6. Чи можу я знати, в чому причина. будь ласка, поділіться, якщо у вас є ідеї щодо цього питання.
MohanRaj S

2

Просто, щоб зробити цю тему більш повною, я додаю свою версію відповіді:

Чого я хотів досягти: погляд на поверхні не повинен бути розтягнутим, а він повинен охоплювати весь екран. Більше того, у моєму додатку був лише пейзажний режим.

Рішення:

Рішення є надзвичайно невеликим розширенням до рішення F1sher:

=> Першим кроком є ​​інтеграція рішення F1sher.

=> Тепер у рішенні F1sher може виникнути сценарій, коли подання поверхні не охоплює весь екран. Рішення полягає в тому, щоб вид поверхні перевищував розміри екрана, щоб він охоплював весь екран для цього:

    size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight);

    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(size.width, size.height);


    mCamera.setParameters(parameters);      

    double screenRatio = (double) screenHeight / screenWidth;
    double previewRatio = (double) size.height / size.width;

    if (previewRatio > screenRatio)     /*if preview ratio is greater than screen ratio then we will have to recalculate the surface height while keeping the surface width equal to the screen width*/
    {
        RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
        params1.addRule(RelativeLayout.CENTER_IN_PARENT);
        flPreview.setLayoutParams(params1);

        flPreview.setClipChildren(false);

        LayoutParams surfaceParams = new LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
        surfaceParams.gravity = Gravity.CENTER;
        mPreview.setLayoutParams(surfaceParams);        
    }
    else     /*if preview ratio is smaller than screen ratio then we will have to recalculate the surface width while keeping the surface height equal to the screen height*/
    {
        RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
        params1.addRule(RelativeLayout.CENTER_IN_PARENT);
        flPreview.setLayoutParams(params1);
        flPreview.setClipChildren(false);

        LayoutParams surfaceParams = new LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
        surfaceParams.gravity = Gravity.CENTER;
        mPreview.setLayoutParams(surfaceParams);

    }       

    flPreview.addView(mPreview); 

  /*  The TopMost layout used is the RelativeLayout, flPreview is the FrameLayout in which Surface View is added, mPreview is an instance of a class which extends SurfaceView  */

1

Я зрозумів, в чому проблема - це з змінами орієнтації . Якщо ви зміните орієнтацію камери на 90 або 270 градусів, вам потрібно поміняти ширину та висоту підтримуваних розмірів, і все буде добре.

Також поверхневий вигляд повинен лежати в макеті рами і мати центральну вагу.

Ось приклад на C # (Xamarin):

public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
    _camera.StopPreview();

    // find best supported preview size

    var parameters = _camera.GetParameters();
    var supportedSizes = parameters.SupportedPreviewSizes;
    var bestPreviewSize = supportedSizes
        .Select(x => new { Width = x.Height, Height = x.Width, Original = x }) // HACK swap height and width because of changed orientation to 90 degrees
        .OrderBy(x => Math.Pow(Math.Abs(x.Width - width), 3) + Math.Pow(Math.Abs(x.Height - height), 2))
        .First();

    if (height == bestPreviewSize.Height && width == bestPreviewSize.Width)
    {
        // start preview if best supported preview size equals current surface view size

        parameters.SetPreviewSize(bestPreviewSize.Original.Width, bestPreviewSize.Original.Height);
        _camera.SetParameters(parameters);
        _camera.StartPreview();
    }
    else
    {
        // if not than change surface view size to best supported (SurfaceChanged will be called once again)

        var layoutParameters = _surfaceView.LayoutParameters;
        layoutParameters.Width = bestPreviewSize.Width;
        layoutParameters.Height = bestPreviewSize.Height;
        _surfaceView.LayoutParameters = layoutParameters;
    }
}

Зверніть увагу, що параметри камери повинні бути встановлені як оригінальний розмір (не замінюватись), а розмір перегляду поверхні повинен змінюватися.


1

Я спробував все рішення вище, але жоден з них не працює на мене. нарешті я вирішив це сам, і знайти насправді це досить просто. є два моменти, з якими потрібно бути обережними.

parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);

цей попередній розмір повинен бути однією з роздільних здатностей камери, яку можна отримати, як показано нижче:

List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes(); 

зазвичай один із rawSupportedSize дорівнює роздільній здатності пристрою.

По-друге, розмістіть SurfaceView у FrameLayout та встановіть висоту та ширину макета поверхні методом surfaceChanged, як вище

FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
layoutParams.height = cameraResolution.x;
layoutParams.width = cameraResolution.y;

Добре, що зроблено, сподіваюся, що це може вам допомогти.


0

Мої вимоги: попередній перегляд камери повинен бути повноекранним і зберігати співвідношення сторін. Рішення Гезама та Йосуфа було чудовим, але я чомусь бачу проблему із великим збільшенням.

Ідея та ж, мати центр контейнера для попереднього перегляду в батьківському, а збільшення ширини або висоти залежать від пропорцій, поки він не зможе охопити весь екран.

Варто зазначити, що розмір попереднього перегляду знаходиться в альбомному вигляді, оскільки ми встановлюємо орієнтацію дисплея.

camera.setDisplayOrientation (90);

Контейнер, до якого ми додамо подання SurfaceView:

<RelativeLayout
    android:id="@+id/camera_preview_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"/>

Додайте попередній перегляд до контейнера з центром у батьківській діяльності.

this.cameraPreview = new CameraPreview(this, camera);
cameraPreviewContainer.removeAllViews();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
cameraPreviewContainer.addView(cameraPreview, 0, params);

Всередині класу CameraPreview:

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // 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 (holder.getSurface() == null) {
        // preview surface does not exist
        return;
    }

    stopPreview();

    // set preview size and make any resize, rotate or
    // reformatting changes here
    try {
        Camera.Size nativePictureSize = CameraUtils.getNativeCameraPictureSize(camera);
        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(optimalSize.width, optimalSize.height);
        parameters.setPictureSize(nativePictureSize.width, nativePictureSize.height);
        camera.setParameters(parameters);
        camera.setDisplayOrientation(90);
        camera.setPreviewDisplay(holder);
        camera.startPreview();

    } catch (Exception e){
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    if (supportedPreviewSizes != null && optimalSize == null) {
        optimalSize = CameraUtils.getOptimalSize(supportedPreviewSizes, width, height);
        Log.i(TAG, "optimal size: " + optimalSize.width + "w, " + optimalSize.height + "h");
    }

    float previewRatio =  (float) optimalSize.height / (float) optimalSize.width;
    // previewRatio is height/width because camera preview size are in landscape.
    float measuredSizeRatio = (float) width / (float) height;

    if (previewRatio >= measuredSizeRatio) {
        measuredHeight = height;
        measuredWidth = (int) ((float)height * previewRatio);
    } else {
        measuredWidth = width;
        measuredHeight = (int) ((float)width / previewRatio);
    }
    Log.i(TAG, "Preview size: " + width + "w, " + height + "h");
    Log.i(TAG, "Preview size calculated: " + measuredWidth + "w, " + measuredHeight + "h");

    setMeasuredDimension(measuredWidth, measuredHeight);
}

-1

Ви повинні встановити cameraView.getLayoutParams (). Висоту та cameraView.getLayoutParams () .ширину відповідно до пропорційного розміру.


чи це інше, ніж встановити параметр моєї камери таким чином (що я зараз роблю): paramet.setPreviewSize (optimalSize.width, optimSize.height); mCamera.setParameters (параметри);
scientiffic

-2

Я відмовився від обчислень і просто отримав розмір подання, де я хочу переглянути попередній перегляд камери, і встановив розмір попереднього перегляду камери однаковим (просто перевернута ширина / висота через обертання) у моїй спеціальній реалізації SurfaceView:

@Override // CameraPreview extends SurfaceView implements SurfaceHolder.Callback { 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    Display display = ((WindowManager) getContext().getSystemService(
            Context.WINDOW_SERVICE)).getDefaultDisplay();

    if (display.getRotation() == Surface.ROTATION_0) {
        final Camera.Parameters params = camera.getParameters();
        // viewParams is from the view where the preview is displayed
        params.setPreviewSize(viewParams.height, viewParams.width);
        camera.setDisplayOrientation(90);
        requestLayout();
        camera.setParameters(params);
    }
    // I do not enable rotation, so this can otherwise stay as is
}

Документ Android каже: Під час встановлення розміру попереднього перегляду ви повинні використовувати значення з getSupportedPreviewSize (). Не встановлюйте довільних значень у методі setPreviewSize ().
G. Steigert
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.