Xem trước máy ảnh Android kéo dài


136

Tôi đã làm việc để thực hiện hoạt động camera tùy chỉnh của mình trên Android, nhưng khi xoay camera, tỷ lệ khung hình của chế độ xem bề mặt bị rối tung.

Để hỗ trợ cho hoạt động, tôi đặt framelayout giữ chế độ xem bề mặt hiển thị các thông số của máy ảnh.

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

Sau đó, trong chế độ xem Surface, tôi đặt các thông số của máy ảnh được hiển thị

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

    }

nhập mô tả hình ảnh ở đây

Bạn có thể thấy rằng người đàn ông LEGO phát triển cao hơn và gầy hơn khi điện thoại được xoay:

Làm cách nào tôi có thể đảm bảo tỷ lệ khung hình cho chế độ xem camera của mình là chính xác?


@scientific bạn có thể vui lòng giúp tôi nên viết phương thức đã cho tôi cũng gặp phải vấn đề tương tự không?
dùng323280

Tôi dường như gặp phải một vấn đề tương tự, nhưng đã khắc phục nó bằng cách gọi như sau: mCamera.setDisplayOrientation (90) trong phương thức SurfaceCreated () của SurfaceView cho máy ảnh của tôi
Greg

Câu trả lời:


163

Tôi đang sử dụng phương pháp này -> dựa trên các Bản trình diễn API để có được Kích thước xem trước của mình:

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

Như bạn có thể thấy bạn phải nhập chiều rộng và chiều cao của màn hình. Phương pháp này sẽ tính toán tỷ lệ màn hình dựa trên các giá trị đó và sau đó từ danh sách được hỗ trợPreviewSizes, nó sẽ chọn mức tốt nhất cho bạn từ các giá trị sẵn có. Nhận danh sách được hỗ trợPreviewSize của bạn ở vị trí mà đối tượng Camera không null bằng cách sử dụng

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

Và sau đó trong onMeasure bạn có thể có được bản xem trước tối ưu Kích thước như thế:

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

Và sau đó (trong mã của tôi theo phương thức SurfaceChanged, như tôi đã nói tôi đang sử dụng cấu trúc API Demos của mã CameraActivity, bạn có thể tạo nó trong Eclipse):

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

Và một gợi ý cho bạn, bởi vì tôi đã làm gần như cùng một ứng dụng như bạn. Thực hành tốt cho Hoạt động máy ảnh là ẩn StatusBar. Các ứng dụng như Instagram đang làm điều đó. Nó làm giảm giá trị chiều cao màn hình của bạn và thay đổi giá trị tỷ lệ của bạn. Có thể nhận được Kích thước xem trước lạ trên một số thiết bị (SurfaceView của bạn sẽ bị cắt giảm một chút)


Và để trả lời câu hỏi của bạn, làm thế nào để kiểm tra xem tỷ lệ xem trước của bạn có đúng không? Sau đó lấy chiều cao và chiều rộng của các tham số mà bạn đặt:

mCamera.setParameters(parameters);

tỷ lệ thiết lập của bạn bằng chiều cao / chiều rộng. Nếu bạn muốn camera trông đẹp trên màn hình thì tỷ lệ chiều cao / chiều rộng của thông số bạn đặt thành camera phải giống với chiều cao (trừ thanh trạng thái) / tỷ lệ chiều rộng của màn hình.


2
Chỉ cần một câu hỏi: Tôi thấy bạn đang sử dụng 2 tỷ lệ được tính toán khác nhau: double targetRatio = (double) h / wdouble ratio = (double) size. Thong / size.height . Cái đầu tiên là chiều cao chia cho chiều rộng và cái kia ngược lại, chiều rộng chia cho chiều cao. Họ có nên không cùng tính toán?
phyzalis

Chẳng quan trọng vì danh sách các kích thước sẽ xem xét mọi khả năng, ví dụ: bạn sẽ tìm thấy thứ gì đó như 480x680, nhưng sớm hay muộn sẽ có 680x480 (đối với phong cảnh), vì vậy bạn chỉ cần đi qua danh sách đó và tìm một danh sách đó phù hợp với nhu cầu của bạn. Bạn sẽ tìm thấy nó sớm hay muộn.
F1sher

Hơn nữa, nếu bạn muốn hình ảnh của mình cũng có kích thước này (có lẽ chúng phải như vậy), bạn đã đặt kích thước hình ảnh với parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height).
Matt Logan

2
@ F1sher, tôi luôn nhận được các tham số ngoại lệ con trỏ null.setPreviewSize (mPreviewSize. Thong, mPreviewSize.height); Không thể tìm thấy bất kỳ giải pháp.
FIXI

3
@phyzalis Tôi đồng ý với bạn. Trong thực tế, tôi đã phải thực hiện chỉnh sửa đó để mã hoạt động như mong đợi.
fernio 19/03/2015

149

Giải pháp của F1Sher là tốt nhưng đôi khi không hoạt động. Đặc biệt, khi SurfaceView của bạn không bao phủ toàn bộ màn hình. Trong trường hợp này, bạn cần ghi đè phương thức onMeasure (). Tôi đã sao chép mã của tôi ở đây để bạn tham khảo.

Vì tôi đã đo bề mặt dựa trên chiều rộng nên tôi có một khoảng trắng nhỏ ở cuối màn hình mà tôi đã lấp đầy nó theo thiết kế. Bạn có thể khắc phục sự cố này nếu bạn giữ chiều cao và tăng chiều rộng bằng cách nhân nó với tỷ lệ. Tuy nhiên, nó sẽ squish SurfaceView một chút.

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
Đây phải là câu trả lời đúng, nó xử lý trường hợp khi bản xem trước của máy ảnh không bao phủ toàn bộ màn hình. +1.
Houcine

1
khi tôi thổi phồng trong ứng dụng hoạt động của tôi đã được đóng <com.app.camera.CameraPreview android: id = "@ + id / camera_preview" android: layout_width = "match_parent" android: layout_height = "match_parent" />
fish40

Bạn cần phải thực hiện một số sửa đổi mặc dù tôi chấp nhận đây là một giải pháp. Bình chọn lên.
JaydeepW

2
Xin chào @ adnan9011, hướng dẫn này có thể hữu ích. air Pair.com/android/android-camera-surface-view-fragment
Hesam

4
chiều cao và chiều rộng cần thiết để được chuyển đổi getOptimalPreviewSizeđể làm việc cho tôi. Đây có phải là vì hướng hiển thị được đặt thành 90? Nếu không, các tính toán tỷ lệ không được thậm chí gần (mục tiêu là 1,7 và tỷ lệ máy ảnh ít hơn 1)
ono

23

LƯU Ý: GIẢI PHÁP CỦA TÔI LÀ TIẾP TỤC GIẢI PHÁP CỦA HESAM: https://stackoverflow.com/a/22758359/1718734

Những gì tôi giải quyết: Hesam's nói có một khoảng trắng nhỏ có thể xuất hiện trên một số điện thoại, như thế này:

GHI CHÚ: Mặc dù tỷ lệ khung hình là chính xác, máy ảnh không lấp đầy toàn bộ màn hình.

Hesam đề xuất một giải pháp thứ hai, nhưng đó là squish xem trước. Và trên một số thiết bị, nó bị biến dạng nặng nề.

Vậy làm thế nào để chúng tôi khắc phục vấn đề này. Thật đơn giản ... bằng cách nhân tỷ lệ khung hình cho đến khi nó lấp đầy màn hình. Tôi đã nhận thấy, một số ứng dụng phổ biến như Snapchat, WhatsApp, v.v ... hoạt động theo cùng một cách.

Tất cả bạn phải làm là thêm điều này vào phương thức 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);
    }

Điều này sẽ tính chiều cao màn hình và lấy tỷ lệ giữa chiều cao màn hình và chiều cao mPreviewSize. Sau đó, nó nhân chiều rộng và chiều cao của máy ảnh theo tỷ lệ chiều cao mới và đặt kích thước đo phù hợp.

nhập mô tả hình ảnh ở đây

Và điều tiếp theo bạn biết, bạn kết thúc với điều này: D

nhập mô tả hình ảnh ở đây

Điều này cũng hoạt động tốt với máy ảnh phía trước. Tôi tin rằng đây là cách tốt nhất để đi về điều này. Bây giờ, điều duy nhất còn lại cho ứng dụng của tôi là lưu bản xem trước khi nhấp vào "Chụp". Nhưng ya, đây là nó.


Điều này hoạt động rất tốt !! nhưng nó không hoạt động đúng trong Cảnh: /
Matan Dahan

mã của bạn đã giải quyết vấn đề không gian đen trống 2 tháng của tôi :)
karthik kolanji 14/03/2016

1
Nó hoạt động, nhưng trong trường hợp của tôi .. Tôi có thanh hành động ở trên cùng để thanh màu trắng nhỏ hơn, tuy nhiên sau khi thêm mã của bạn, camera của tôi trông phóng to lên 40-50%. Khi chuyển sang máy ảnh mặt trước, về cơ bản tôi không thể nhìn thấy khuôn mặt của mình nhìn thẳng (chỉ một chút tóc), vì khả năng zoom cao.
Makalele

@Yoosuf Tôi nhận được màn hình trắng khi tôi theo mã của bạn. Trong onMeasure(int widthMeasureSpec, int heightMeasureSpec)phương thức tôi luôn nhận được width = 0 và height = 0;
Kush Patel

10

OK, vì vậy tôi nghĩ rằng không có câu trả lời đầy đủ cho vấn đề kéo dài xem trước máy ảnh nói chung. Hoặc ít nhất tôi đã không tìm thấy một. Ứng dụng của tôi cũng bị hội chứng kéo dài này và tôi phải mất một thời gian để cùng nhau giải đáp một giải pháp từ tất cả các câu trả lời của người dùng trên cổng thông tin và internet này.

Tôi đã thử giải pháp của @ Hesam nhưng nó không hoạt động và khiến cho bản xem trước máy ảnh của tôi bị biến dạng lớn.

Đầu tiên tôi hiển thị mã của giải pháp của tôi (các phần quan trọng của mã) và sau đó tôi giải thích lý do tại sao tôi thực hiện các bước đó. Có chỗ để sửa đổi hiệu suất.

Bố cục xml hoạt động chính:

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

Xem trước máy ảnh:

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

Hoạt động chủ yêu:

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

Bình luận của tôi:

Vấn đề của tất cả là, mặc dù bạn tính kích thước camera tối ưu trong getOptimalPreviewSize()bạn chỉ chọn tỷ lệ gần nhất để vừa với màn hình của bạn. Vì vậy, trừ khi tỷ lệ là chính xác như nhau , bản xem trước sẽ kéo dài.

Tại sao nó sẽ kéo dài? Bởi vì bản xem trước máy ảnh FrameLayout của bạn được đặt trong layout.xml thành match_parent về chiều rộng và chiều cao. Vì vậy, đó là lý do tại sao xem trước sẽ kéo dài đến toàn màn hình.

Điều cần làm là thiết lập chiều rộng và chiều cao bố cục của máy ảnh để phù hợp với tỷ lệ kích thước máy ảnh đã chọn , để chế độ xem trước giữ tỷ lệ khung hình của nó và sẽ không bị biến dạng.

Tôi đã cố gắng sử dụng CameraPreviewlớp để thực hiện tất cả các tính toán và thay đổi bố cục, nhưng tôi không thể tìm ra nó. Tôi đã thử áp dụng giải pháp này , nhưng SurfaceViewkhông nhận ra getChildCount ()hoặc getChildAt (int index). Tôi nghĩ rằng, cuối cùng tôi đã làm cho nó hoạt động với một tham chiếu đến maLayoutPreview, nhưng nó đã hoạt động sai và áp dụng tỷ lệ thiết lập cho toàn bộ ứng dụng của tôi và nó đã làm như vậy sau khi bức ảnh đầu tiên được chụp. Vì vậy, tôi để nó đi và di chuyển các sửa đổi bố trí đến MainActivity.

Trong CameraPreviewTôi đã thay đổi prSupportedPreviewSizesgetOptimalPreviewSize()để công chúng vì vậy tôi có thể sử dụng nó trong MainActivity. Sau đó, tôi cần kích thước hiển thị (trừ thanh điều hướng / trạng thái nếu có) và chọn kích thước camera tối ưu . Tôi đã cố gắng để có được kích thước RelativeLayout (hoặc FrameLayout) thay vì kích thước hiển thị, nhưng nó đã trả về giá trị bằng không. Giải pháp này không hiệu quả với tôi. Bố cục có giá trị sau onWindowFocusChanged(kiểm tra trong nhật ký).

Vì vậy, tôi có các phương pháp của mình để tính toán kích thước bố cục để phù hợp với tỷ lệ khung hình của kích thước máy ảnh được chọn. Bây giờ bạn chỉ cần thiết lập LayoutParamsbố trí xem trước máy ảnh của bạn. Thay đổi chiều rộng, chiều cao và căn giữa nó trong cha mẹ.

Có hai sự lựa chọn làm thế nào để tính toán kích thước xem trước. Bạn muốn màn hình vừa với màn hình với các thanh màu đen (nếu windowBackground được đặt thành null) ở hai bên hoặc trên / dưới. Hoặc bạn muốn xem trước phóng to toàn màn hình . Tôi để lại bình luận với nhiều thông tin hơn calcCamPrevDimensions().


6

Xin chào getOptimalPreview () ở đây không hoạt động với tôi vì vậy tôi muốn chia sẻ phiên bản của mình:

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

nó chuyển đổi thành một camera vuông, Đầu vào được đưa ra là (1920x1080) sau khi tối ưu hóa chiều rộng và chiều cao mà nó đã cho (1088x1088), Thiết bị thử nghiệm của tôi là Samsung S6. Tôi có thể biết lý do là gì. hãy chia sẻ nếu bạn có bất kỳ ý tưởng nào liên quan đến vấn đề này.
MohanRaj S

2

Để làm cho chủ đề này hoàn thiện hơn, tôi đang thêm phiên bản trả lời của mình:

Những gì tôi muốn đạt được: Chế độ xem bề mặt không nên được kéo dài và nó sẽ bao phủ toàn bộ màn hình, Hơn nữa, chỉ có một chế độ ngang trong ứng dụng của tôi.

Giải pháp:

Giải pháp là một phần mở rộng cực kỳ nhỏ cho giải pháp của F1sher:

=> Bước đầu tiên là tích hợp giải pháp của F1sher.

=> Bây giờ, có thể phát sinh một kịch bản trong giải pháp của F1sher khi chế độ xem bề mặt không bao phủ toàn bộ màn hình, Giải pháp là làm cho chế độ xem bề mặt lớn hơn kích thước màn hình để nó bao phủ toàn bộ màn hình, vì điều đó:

    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

Tôi đã tìm ra vấn đề là gì - đó là với sự thay đổi định hướng . Nếu bạn thay đổi hướng máy ảnh thành 90 hoặc 270 độ, bạn cần trao đổi chiều rộng và chiều cao của các kích thước được hỗ trợ và tất cả sẽ ổn.

Ngoài ra bề mặt phải nằm trong một bố cục khung và có trọng lực trung tâm.

Dưới đây là ví dụ về 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;
    }
}

Hãy chú ý rằng các thông số máy ảnh nên được đặt thành kích thước ban đầu (không bị tráo đổi) và kích thước chế độ xem bề mặt phải được hoán đổi.


1

tôi đã thử tất cả các giải pháp trên nhưng không có giải pháp nào phù hợp với tôi. cuối cùng tôi đã tự mình giải quyết nó và thấy thực sự nó khá dễ. Có hai điểm bạn cần cẩn thận.

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

bản xem trước này Kích thước phải là một trong những độ phân giải được hỗ trợ của máy ảnh, có thể nhận được như sau:

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

thường là một trong các rawSupportedSize bằng với độ phân giải của thiết bị.

Thứ hai, đặt SurfaceView của bạn vào FrameLayout và đặt chiều cao và chiều rộng của bố cục bề mặt trong phương thức SurfaceChanged như trên

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

Ok, mọi thứ đã xong, hy vọng điều này có thể giúp bạn.


0

Yêu cầu của tôi là xem trước máy ảnh cần phải ở chế độ toàn màn hình và giữ tỷ lệ khung hình. Giải pháp của Hesam và Yoosuf là tuyệt vời nhưng tôi thấy một vấn đề zoom cao vì một số lý do.

Ý tưởng là như nhau, có trung tâm chứa bản xem trước trong phần gốc và tăng chiều rộng hoặc chiều cao tùy thuộc vào tỷ lệ khung hình cho đến khi nó có thể bao phủ toàn bộ màn hình.

Một điều cần lưu ý là kích thước xem trước nằm ngang vì chúng tôi đặt hướng hiển thị.

máy ảnh.setDisplayOrientation (90);

Container mà chúng ta sẽ thêm khung nhìn SurfaceView vào:

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

Thêm bản xem trước vào thùng chứa của nó với trung tâm là cha mẹ trong hoạt động của bạn.

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

Bên trong lớp 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

Bạn phải đặt cameraView.getLayoutParams (). Chiều cao và cameraView.getLayoutParams (). Chiều rộng theo tỷ lệ khung hình bạn muốn.


điều này khác với cài đặt tham số của chế độ xem máy ảnh của tôi theo cách này (mà tôi hiện đang làm): tham số.setPreviewSize (tối ưu hóa kích thước, tối ưu hóa, tối ưu hóa kích thước); mCamera.setParameter (tham số);
khoa học

-2

Tôi đã từ bỏ các tính toán và chỉ cần lấy kích thước của chế độ xem mà tôi muốn xem trước máy ảnh được hiển thị và đặt kích thước xem trước của máy ảnh giống nhau (chỉ lật chiều rộng / chiều cao do xoay) trong triển khai SurfaceView tùy chỉnh của tôi:

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

Tài liệu Android cho biết: Khi đặt kích thước xem trước, bạn phải sử dụng các giá trị từ getSupportedPreviewSizes (). Không đặt các giá trị tùy ý trong phương thức setPreviewSize ().
G. Steigert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.