【问题标题】:Camera Preview Size too Large相机预览尺寸太大
【发布时间】:2014-10-15 11:35:11
【问题描述】:

在我的应用程序中,我想在布局顶部的 FrameLayout 中显示相机预览。 这是我想要实现的目标:

+---------------+
| Camera Prev.  |
|               |
+---------------+
|               |
|               |
+---------------+
|               |
|               |
+---------------+

屏幕分为 3 个区域,在最上面的区域我想看到相机预览。 预览应该有正确的纵横比,所以没有任何东西被拉伸。至少高度应该是固定的。宽度不必与整个屏幕尺寸宽度匹配。

这是我的 Layout.xml:

  <!--CAMERA PREVIEW-->
    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:layout_marginBottom="5dp"/>

    <!--ANTOTHER SURFACE VIEW-->
    <FrameLayout
        android:id="@+id/control_layer"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#FF000000"
        android:layout_weight="1"
        android:layout_marginBottom="5dp"/>

    <!--CONTROLS AND BUTTONS-->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center_horizontal"
        android:layout_weight="1">

        <Button
            android:id="@+id/buttonGraphGLStart"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#FF00FF00"
            android:layout_centerInParent="true"
            android:layout_alignParentTop="true"
            android:text="Start"/>
    </LinearLayout>

这种布局有效,我看到三个高度相等的区域。现在在第一个区域我要显示相机预览。以下是我的代码:

public class GraphGLFragment extends Fragment {

    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.fragment_graphgl, null);
        mCamera = getCameraInstance();
        mPreview = new CameraPreview(this.getActivity(), mCamera);
        FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.camera_preview);
        frameLayout.addView(mPreview);
        return view;
    }


    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.graph_video_menu, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    /**
     * A safe way to get an instance of the Camera object.
     */
    public static Camera getCameraInstance() {
        Camera c = null;
        try {
            c = Camera.open();
        } catch (Exception e) {
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }


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

            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);
                    }
                }
            }
            Log.d(TAG,optimalSize.width + " "+ optimalSize.height + "-----");
            return optimalSize;
        }
    }
}

没有错误,纵横比也正确(没有拉伸图像),但是预览太大。我没有看到整个预览。我猜预览大小没有根据托管camer_preview的可用大小正确计算FrameLayout
有人知道这里有什么问题吗?

在surfaceCHanged() 中,表面的 w = 1536 和 h= 2730 太大了...

【问题讨论】:

    标签: android android-layout android-camera


    【解决方案1】:

    您的设备处于纵向模式,预览会相应旋转。为了保持纵横比,你必须让表面高于而不是更宽

    ----------
    |        |  
    |        |  
    |        |  
    |        |  
    |        |  
    |        |  
    |        |  
    ----------
    

    要达到的效果:

    ----------
    |        |  
    |        |  
    |XXXXXXXX|  
    |XXXXXXXX|  
    |XXXXXXXX|  
    |XXXXXXXX|  
    |XXXXXXXX|  
    ----------
    

    你应该裁剪图片。最简单的方法是在表面上显示 UI 的其他部分。或者,您可以使用 SurfaceTexture 并操纵 OpenGL 变换来裁剪图像。

    几天前,我回答了一个类似的问题:How to get normal Camera view in Surfaceview android?。

    【讨论】:

    • 嗨,我不想裁剪图像。但我想在顶部区域以正确的比例查看完整预览。预览宽度与区域宽度不匹配也没关系。
    【解决方案2】:

    如果您想显示完整预览(即不裁剪),但可以牺牲预览表面的宽度(以保持纵横比),请更改onMeasure()中的代码:

    setMeasuredDimension((int)(height/ratio), height);
    setLayoutParams(new Frameayout.LayoutParams((int)(height/ratio), height, Gravity.CENTER));
    

    这是预期的布局:

    ----------
    |XX    XX|  
    |XX    XX|  
    |XX    XX|  
    |XXXXXXXX|  
    |XXXXXXXX|  
    |XXXXXXXX|  
    |XXXXXXXX|  
    ----------
    

    【讨论】:

    • 那么,当设备处于纵向模式时,无法查看横向预览(全预览,只是更小)?是这样吗?
    • 不,这是不可能的,因为相机传感器的物理布局是这样的。像素布局可以通过编程方式旋转,我们使用Camera.setDisplayOrientation(90) 实现了这一点,但这不会旋转传感器本身。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-24
    • 2022-06-14
    • 2014-03-07
    • 1970-01-01
    相关资源
    最近更新 更多