ดูตัวอย่างกล้อง Android ยืด


136

ฉันกำลังทำกิจกรรมกล้องที่กำหนดเองของฉันบน Android แต่เมื่อหมุนกล้องอัตราส่วนของมุมมองพื้นผิวจะเลอะ

ในการสร้างกิจกรรมของฉันฉันตั้ง framelayout ซึ่งเก็บมุมมองพื้นผิวที่แสดงพารามิเตอร์ของกล้อง

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

    }

ป้อนคำอธิบายรูปภาพที่นี่

คุณจะเห็นว่าชายเลโก้โตขึ้นและผอมกว่าเมื่อหมุนโทรศัพท์:

ฉันจะมั่นใจได้อย่างไรว่าอัตราส่วนภาพสำหรับมุมมองกล้องของฉันถูกต้อง?


@ วิทยาศาสตร์สามารถ u กรุณาช่วยฉันที่ฉันควรเขียนวิธีการที่กำหนดฉันยังเผชิญปัญหาเดียวกัน
user3233280

ฉันดูเหมือนจะได้รับปัญหาที่คล้ายกัน แต่สิ่งที่คงที่มันถูกเรียกต่อไปนี้: mCamera.setDisplayOrientation (90) ใน surfaceCreated () วิธีการ 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 จะเลือกสิ่งที่ดีที่สุดสำหรับคุณจากรายการที่มีให้ รับรายการ PreSizeSize ที่คุณรองรับซึ่งวัตถุกล้องไม่เป็นโมฆะโดยใช้

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 ของรหัส CameraActivity คุณสามารถสร้างมันใน Eclipse):

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

และหนึ่งคำใบ้สำหรับคุณเพราะฉันทำเกือบเหมือนแอพเดียวกันกับคุณ แนวปฏิบัติที่ดีสำหรับกิจกรรมกล้องคือการซ่อน StatusBar แอปพลิเคชั่นอย่าง Instagram กำลังทำอยู่ มันลดค่าความสูงของหน้าจอและเปลี่ยนค่าอัตราส่วนของคุณ เป็นไปได้ที่จะได้รับขนาดหน้าตัวอย่างที่แปลกประหลาดในอุปกรณ์บางอย่าง (SurfaceView ของคุณจะถูกตัดเล็กน้อย)


และเพื่อตอบคำถามของคุณจะตรวจสอบว่าอัตราส่วนการดูตัวอย่างของคุณถูกต้องได้อย่างไร จากนั้นรับความสูงและความกว้างของพารามิเตอร์ที่คุณตั้งไว้:

mCamera.setParameters(parameters);

อัตราส่วนชุดของคุณเท่ากับความสูง / ความกว้าง หากคุณต้องการให้กล้องดูดีบนหน้าจออัตราส่วนความสูง / ความกว้างของพารามิเตอร์ที่คุณตั้งค่ากล้องจะต้องเท่ากับความสูง (แถบสถานะลบ) / อัตราส่วนความกว้างของหน้าจอ


2
ขอแค่คำถามเดียวที่ฉันเห็นคุณกำลังใช้อัตราส่วนคำนวณ 2 แตกต่างกัน: คู่ targetRatio = (คู่) H / wและอัตราส่วนคู่ = (คู่) size.width / size.height อันแรกคือความสูงหารด้วยความกว้างและอีกอันคือตรงกันข้ามความกว้างหารด้วยความสูง พวกเขาไม่ควรจะคำนวณแบบเดียวกันหรือไม่?
phyzalis

ไม่สำคัญเนื่องจากรายการขนาดคำนึงถึงความเป็นไปได้ทุกอย่างเช่นคุณจะพบบางอย่างเช่น 480x680 แต่ไม่ช้าก็เร็วจะมี 680x480 (สำหรับแนวนอน) ดังนั้นคุณต้องไปดูรายการนั้นแล้วค้นหาสิ่งที่ต้องการ ตรงกับความต้องการของคุณ คุณจะพบว่าไม่ช้าก็เร็ว
F1sher

นอกจากนี้หากคุณต้องการภาพของคุณจะเป็นขนาดนี้เช่นกัน (เช่นที่พวกเขาอาจจะ) parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height)คุณได้กำหนดขนาดภาพด้วย
Matt Logan

2
@ F1sher ฉันมักจะได้รับข้อยกเว้นตัวชี้โมฆะพารามิเตอร์. setPreviewSize (mPreviewSize.width, mPreviewSize.height); ไม่พบวิธีแก้ปัญหาใด ๆ
FIXI

3
@phyzalis ฉันเห็นด้วยกับคุณ ที่จริงแล้วฉันต้องทำการแก้ไขเพื่อให้โค้ดทำงานตามที่คาดไว้
fernio

149

ทางออกของ F1Sher ดี แต่บางครั้งก็ใช้งานไม่ได้ โดยเฉพาะอย่างยิ่งเมื่อ SurfaceView ของคุณไม่ครอบคลุมทั้งหน้าจอ ในกรณีนี้คุณต้องแทนที่เมธอด onMeasure () ฉันได้คัดลอกรหัสของฉันที่นี่สำหรับการอ้างอิงของคุณ

เนื่องจากฉันวัด 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

คุณต้องทำการแก้ไขบางอย่าง แต่ฉันยอมรับว่านี่เป็นวิธีแก้ปัญหา Up-ได้รับการโหวต
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 จากนั้นจะเพิ่มความกว้างและความสูงของกล้องตามอัตราส่วนความสูงใหม่และตั้งค่าขนาดที่วัดได้ตามนั้น

ป้อนคำอธิบายรูปภาพที่นี่

และสิ่งต่อไปที่คุณรู้ว่าคุณจบลงด้วยสิ่งนี้: D

ป้อนคำอธิบายรูปภาพที่นี่

มันใช้งานได้ดีกับกล้องหน้าของเขา ฉันเชื่อว่านี่เป็นวิธีที่ดีที่สุดที่จะไปเกี่ยวกับเรื่องนี้ ตอนนี้สิ่งเดียวที่เหลืออยู่สำหรับแอปของฉันคือการบันทึกหน้าตัวอย่างเมื่อคลิกที่ "จับภาพ" แต่ใช่นี่คือมัน


มันใช้งานได้ดีมาก !! แต่มันทำงานไม่ถูกต้องในภูมิประเทศ: /
Matan Dahan

โค้ดของคุณแก้ไขปัญหาช่องว่างที่ว่างเปล่า 2 เดือนของฉัน :)
karthik kolanji

1
มันใช้งานได้ แต่ในกรณีของฉัน .. ฉันมีแถบแอ็คชั่นอยู่ด้านบนดังนั้นแถบสีขาวก็เล็กลง แต่หลังจากเพิ่มรหัสของคุณกล้องของฉันจะซูม 40-50% เมื่อเปลี่ยนมาใช้กล้องหน้าหันหน้าเข้าหาฉันโดยทั่วไปฉันไม่สามารถมองเห็นใบหน้าของฉันตรง (เพียงเล็กน้อยของผม) เนื่องจากการซูมสูง
Makalele

@Yoosuf ฉันได้รับหน้าจอสีขาวเมื่อฉันตามรหัสของคุณ ในonMeasure(int widthMeasureSpec, int heightMeasureSpec)วิธีฉันมักจะได้รับความกว้าง = 0 และความสูง = 0;
เทือกเขาฮินดูกูช 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ผมเปลี่ยนprSupportedPreviewSizesและgetOptimalPreviewSize()เพื่อประชาชนMainActivityเพื่อให้สามารถใช้งานได้ใน จากนั้นผมจำเป็นต้องมีขนาดจอแสดงผล (ลบนำทาง / แถบสถานะถ้ามี) และได้รับการแต่งตั้งที่ดีที่สุดขนาดกล้อง ฉันพยายามรับขนาด RelativeLayout (หรือ FrameLayout) แทนขนาดจอแสดงผล แต่กลับเป็นค่าศูนย์ วิธีนี้ไม่ได้ผลสำหรับฉัน โครงร่างมีค่าเป็นหลัง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) หลังจากปรับความกว้างและความสูงให้เหมาะสม (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 และตั้งค่าความสูงและความกว้างของเค้าโครงพื้นผิวในวิธีการเปลี่ยนพื้นผิวดังกล่าวข้างต้น

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 (maximumSize.width, maximumSize.height); mCamera.setParameters (พารามิเตอร์);
นักวิทยาศาสตร์

-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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.