Android 카메라 미리보기 확장


136

Android에서 사용자 정의 카메라 작업을 수행하려고 노력했지만 카메라를 회전하면 표면보기의 가로 세로 비율이 엉망이됩니다.

활동에 대한 oncreate에서 카메라의 매개 변수를 표시하는 표면보기를 유지하는 프레임 레이아웃을 설정했습니다.

//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);

그런 다음 Surface 뷰에서 카메라의 매개 변수가 표시되도록 설정했습니다.

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 남자가 키가 커지고 날씬해 짐을 알 수 있습니다.

카메라 뷰의 종횡비가 올바른지 어떻게 확인할 수 있습니까?


@ scientific u pls는 같은 문제에 직면하고 주어진 방법을 어디에서 작성해야합니까?
user3233280

비슷한 문제가 생겼지 만 다음과 같이 수정했습니다. 카메라에 대한 SurfaceView의 surfaceCreated () 메서드에서 mCamera.setDisplayOrientation (90)
Greg

답변:


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

보시다시피 화면의 너비와 높이를 입력해야합니다. 이 방법은 해당 값을 기준으로 화면 비율을 계산 한 다음 supportedPreviewSizes 목록에서 사용 가능한 값 중에서 가장 적합한 것을 선택합니다. 다음을 사용하여 Camera 객체가 null이 아닌 곳에 supportedPreviewSize 목록을 가져옵니다.

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 메서드의 코드에서 CameraActivity 코드의 API 데모 구조를 사용한다고 말한 것처럼 Eclipse에서 생성 할 수 있습니다.

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

그리고 당신과 같은 응용 프로그램을 거의했기 때문에 힌트가 하나 있습니다. 카메라 활동에 대한 모범 사례는 StatusBar를 숨기는 것입니다. Instagram과 같은 응용 프로그램이하고 있습니다. 화면 높이 값이 줄어들고 비율 값이 변경됩니다. 일부 장치에서 이상한 미리보기 크기를 얻을 수 있습니다 (SurfaceView가 약간 잘림).


질문에 대답하기 위해 미리보기 비율이 올바른지 확인하는 방법은 무엇입니까? 그런 다음 설정 한 매개 변수의 높이와 너비를 가져옵니다.

mCamera.setParameters(parameters);

설정 비율은 높이 / 너비와 같습니다. 카메라를 화면에서보기 좋게하려면 카메라로 설정 한 매개 변수의 높이 / 너비 비율이 화면의 높이 (빼기 상태 표시 줄) / 너비 비율과 같아야합니다.


2
단 하나의 질문 : 가지 다른 계산 비율을 사용하고 있습니다 : double targetRatio = (double) h / wdouble ratio = (double) size.width / size.height . 첫 번째는 높이를 너비로 나눈 값이고 다른 하나는 너비를 높이로 나눈 것입니다. 그들은 같은 계산이 아니어야합니까?
phyzalis

크기 목록은 모든 가능성을 고려하기 때문에 중요하지 않습니다. 예를 들어 480x680과 같은 것을 찾을 수 있지만 조만간 680x480 (풍경)이 있으므로 해당 목록을 살펴보고 그 중 하나를 찾아야합니다. 당신의 요구와 일치합니다. 조만간 찾을 수 있습니다.
F1sher

또한 사진의 크기를 원하는 크기로 설정하려면 사진 크기를로 설정하십시오 parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height).
Matt Logan

2
@ F1sher, 나는 항상 null 포인터 예외 매개 변수를 얻습니다 .setPreviewSize (mPreviewSize.width, mPreviewSize.height); 해결책을 찾을 수 없습니다.
FIXI

3
@phyzalis 동의합니다. 실제로 코드가 제대로 작동하도록 수정해야했습니다.
fernio

149

F1Sher의 솔루션은 훌륭하지만 때때로 작동하지 않습니다. 특히, SurfaceView가 전체 화면을 덮지 않는 경우. 이 경우 onMeasure () 메서드를 재정의해야합니다. 참조를 위해 여기에 코드를 복사했습니다.

너비를 기준으로 surfaceView를 측정 했으므로 화면 끝에 약간의 흰색 간격이있어 디자인으로 채 웠습니다. 높이를 유지하고 너비를 비율에 곱하여 높이면이 문제를 해결할 수 있습니다. 그러나 surfaceView가 약간 손상됩니다.

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
Houcine

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
Hesam

4
getOptimalPreviewSize이것이 작동 하려면 높이와 너비를 전환해야했습니다 . 표시 방향이 90으로 설정 되었습니까? 그렇지 않으면 비율 계산이 거의 끝나지 않았습니다 (목표는 1.7이고 카메라 비율은 1보다 작습니다)
ono

23

참고 : 내 솔루션은 HESAM 솔루션의 연속입니다. https://stackoverflow.com/a/22758359/1718734

내가 다루는 내용 : Hesam은 다음과 같이 일부 전화기에 약간의 공백이 나타날 수 있다고 말했습니다.

참고 : 화면비는 정확하지만 카메라가 전체 화면을 채우지는 않습니다.

Hesam은 두 번째 해결책을 제안했지만 미리보기를 무너 뜨 렸습니다. 그리고 일부 장치에서는 심하게 왜곡됩니다.

이 문제를 어떻게 해결합니까? 화면이 가득 찰 때까지 종횡비를 곱하면 간단합니다. 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 높이의 비율을 가져옵니다. 그런 다음 카메라의 너비와 높이에 새로운 높이 비율을 곱하고 그에 따라 측정 된 치수를 설정합니다.

여기에 이미지 설명을 입력하십시오

그리고 다음으로 아는 것은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

이것은 전면 카메라에서도 잘 작동합니다. 이것이 가장 좋은 방법이라고 생각합니다. 이제 내 앱에 남은 것은 "캡처"를 클릭하면 미리보기 자체를 저장하는 것입니다. 하지만 나중에 요.


이것은 매우 잘 작동합니다! 그러나 풍경에서 제대로 작동하지 않습니다 : /
Matan Dahan

코드 내 2개월 빈 검은 공간 문제 : 해결
KARTHIK kolanji

1
작동하지만 내 경우에는 .. 상단에 작업 표시 줄이있어 흰색 막대가 더 작지만 코드를 추가 한 후 카메라가 40-50 % 확대되었습니다. 전면 카메라로 전환하면 기본적으로 높은 줌으로 인해 얼굴이 똑바로 보이는 것을 볼 수 없습니다.
Makalele

@Yoosuf 코드를 따라갈 때 흰색 화면이 나타납니다. 에서 onMeasure(int widthMeasureSpec, int heightMeasureSpec)방법 나는 항상 GET 폭 = 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 에서 너비와 높이 가 match_parent 로 설정되어 있기 때문 입니다. 그래서 미리보기가 전체 화면으로 확대됩니다.

수행 할 작업은 선택한 카메라 크기 비율 에 맞게 카메라 미리보기 레이아웃 너비와 높이 를 설정 하여 미리보기가 가로 세로 비율을 유지하고 왜곡되지 않도록하는 것입니다.

CameraPreview클래스 를 사용하여 모든 계산 및 레이아웃 변경을 시도했지만 이해할 수 없었습니다. 나는 적용하려고 이 솔루션을 하지만, SurfaceView인식하지 못 getChildCount ()하거나 getChildAt (int index). 나는 결국에 대한 참조로 작동한다고 생각 maLayoutPreview했지만 잘못 작동하여 전체 응용 프로그램에 설정된 비율을 적용했으며 첫 번째 사진을 찍은 후 그렇게했습니다. 그래서 레이아웃을 수정하고 레이아웃 수정을MainActivity .

에서 CameraPreview내가 변경 prSupportedPreviewSizesgetOptimalPreviewSize()공공 난에서 사용할 수 있습니다 MainActivity. 그런 다음 디스플레이 크기 (탐색 / 상태 표시 줄이없는 경우)를 빼고 최적의 카메라 크기를 선택했습니다 . 디스플레이 크기 대신 RelativeLayout (또는 FrameLayout) 크기를 얻으려고했지만 0 값을 반환했습니다. 이 솔루션 은 저에게 효과적이지 않았습니다. 레이아웃은 가치가 있습니다.onWindowFocusChanged (로그에서 확인).

선택한 카메라 크기의 종횡비와 일치하도록 레이아웃 크기를 계산하는 방법이 있습니다. 이제 설정 만하면됩니다.LayoutParams 카메라 미리보기 레이아웃 됩니다. 너비, 높이를 변경하고 부모 중심에 배치하십시오.

미리보기 크기를 계산하는 방법에는 두 가지가 있습니다. 측면 또는 상단 / 하단에 검은 색 막대 (windowBackground가 null로 설정된 경우)가 있는 화면에 맞출 수 있습니다 . 또는 미리보기 를 전체 화면으로 확대 하고자합니다 . 에 더 많은 정보가 담긴 의견을 남겼습니다 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) 주어진 입력은 (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);

이 previewSize는 카메라에서 지원되는 해상도 중 하나 여야하며 다음과 같이 얻을 수 있습니다.

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

내 요구 사항은 카메라 미리보기가 전체 화면이어야하고 종횡비를 유지해야한다는 것입니다. Hesam과 Yoosuf의 솔루션은 훌륭했지만 어떤 이유로 든 확대 / 축소 문제가 발생합니다.

아이디어는 동일합니다. 미리보기 컨테이너 중심을 부모로두고 전체 화면을 덮을 수있을 때까지 가로 세로 비율에 따라 너비 또는 높이를 늘리십시오.

주목할 것은 디스플레이 방향을 설정하기 때문에 미리보기 크기가 가로 방향이라는 것입니다.

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 (). height 및 cameraView.getLayoutParams (). width를 설정해야합니다.


카메라 뷰의 매개 변수를 현재와 같이 설정하는 것과 다릅니다. parameters.setPreviewSize (optimalSize.width, optimizeSize.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 문서에 따르면 : 미리보기 크기를 설정할 때 getSupportedPreviewSizes ()의 값을 사용해야합니다. setPreviewSize () 메소드에서 임의의 값을 설정하지 마십시오.
G. Steigert 2012
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.