Android相机Camera2的简单分析

        近期学习了解Android相机的使用,如何预览及切图,看了一些资料,在github上找到了google对于Camera2使用的Demo,就这份Demo做一个个人分析理解。

        示例项目中有两份代码,一份Java编写,一份Kotlin编写,将分别做分析记录。

一、Java示例


        目录结构非常简单,只有一个Activity,一个Fragment,一个自定义View。

        Activity里面只有一个Fragment,这里不做解释。

        接下来我们看这个自定义View是什么样的。

/**
 * A {@link TextureView} that can be adjusted to a specified aspect ratio.
 */
public class AutoFitTextureView extends TextureView {

    private int mRatioWidth = 0;
    private int mRatioHeight = 0;

    public AutoFitTextureView(Context context) {
        this(context, null);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
     * calculated from the parameters. Note that the actual sizes of parameters don't matter, that
     * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
     *
     * @param width  Relative horizontal size
     * @param height Relative vertical size
     */
    public void setAspectRatio(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Size cannot be negative.");
        }
        mRatioWidth = width;
        mRatioHeight = height;
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mRatioWidth || 0 == mRatioHeight) {
            setMeasuredDimension(width, height);
        } else {
            if (width < height * mRatioWidth / mRatioHeight) {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            } else {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }

}

        可以看到,这是一个自定义的TextureView,在相机使用的时候,TextureView将作为载体去展示相机获取的内容,就是我们展示使用的控件。

        同时,这个自定义TextureView重写了onMeasure方法,大家都知道这个方法是改变控件大小使用的,接下来我们看内部实现。mRatioHeight和mRatioWidth是一个从外部设置的宽高,当我们的默认要展示的宽高比和设定的宽高比不一样的时候,重新计算,让最短的(宽或高)充满控件,另一个(高或宽)按比例确定其长度。这样做的目的是让相机获取的比例和我们控件所一致,否则会造成图片拉伸的效果。另外,当我们每次设置新的数据进来的时候,会调用requestLayout方法,此方法会重新测量控件宽高,保证显示的准确性。

    

        在看Fragment中使用Camera2进行开发之前,我们先简单了解一下Camera2如何使用。

        1、Camera2的显示需要一个TextureView作为载体,所以我们需要一个TextureView。(SurfaceView也可以)

        2、当TextureView创建完成后,6.0之后需要先做相机的权限请求验证(Manifest.permission.CAMERA),验证通过后,获取CameraManager对象activity.getSystemService(Context.CAMARA_SERVICE),然后用CameraManager打开摄像头,调用openCamera(@NotNull String : cameraId, @NotNull  final CameraDevice.StateCallback callback,

 @Nullable Handler : handler)方法打开摄像头。

        该方法第一个参数为相机id(通常0为后置摄像头,1为前置摄像头),第二个参数为相机状态的回调,后面详细解析,第三个参数为接收消息使用的Handler,作用是表示在哪个线程调用,如果为null,就在当前线程使用,附上源码注释:

     * @param cameraId
     *             The unique identifier of the camera device to open
     * @param callback
     *             The callback which is invoked once the camera is opened
     * @param handler
     *             The handler on which the callback should be invoked, or
     *             {@code null} to use the current thread's {@link android.os.Looper looper}.

        3、第二步我们需要一个CameraDevice.StateCallback对象,接下来我们就需要创建这个对象,这个抽象类有三个实现,分别是onOpen、onDisconnect、onError,按名称就知道其含义了。

        4、在回调onOpen的时候,就证明是相机打开成功了,此时还需要创建CameraCaptureSession,调用CameraDevice的creatCaptureSession方法,源码和部分注释如下

 /**
     * <p>Create a new camera capture session by providing the target output set of Surfaces to the
     * camera device.</p>
     * @param outputs The new set of Surfaces that should be made available as
     *                targets for captured image data.
     * @param callback The callback to notify about the status of the new capture session.
     * @param handler The handler on which the callback should be invoked, or {@code null} to use
     *                the current thread's {@link android.os.Looper looper}.
     *
     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
     *                                  the callback is null, or the handler is null but the current
     *                                  thread has no looper.
     * @throws CameraAccessException if the camera device is no longer connected or has
     *                               encountered a fatal error
     * @throws IllegalStateException if the camera device has been closed
     */
    public abstract void createCaptureSession(@NonNull List<Surface> outputs,
            @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
            throws CameraAccessException;

        第一个参数是一个Surface的List,其作用是需要将要显示在这个Surface上面,此处我们需要两个Surface,一个是显示在页面上,另一个是拍照的时候保存用的,先通过我们前面的TextureView获取显示用的Surface。

            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;

            // We configure the size of default buffer to be the size of camera preview we want.
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

            // This is the output Surface we need to start preview.
            Surface surface = new Surface(texture);

        在拍照截图的时候,我们还需要一个ImageReader对象,此时将ImageReader的Surface也传入进去。

        第二个参数是CameraCaptureSession.StateCallback,看名称即知道是状态回调。

        第三个参数是handler,和之前的作用一样,不做解释了。

        五、在CameraCaptureSession.StateCallback的回调onConfigured中,我们可以取到CameraCaptureSession的实例,

接下来,我们需要调用CaptureSession的setRepeatingRequest方法,部分注释如下

    /**
     * Request endlessly repeating capture of images by this capture session.
     *
     * <p>With this method, the camera device will continually capture images
     * using the settings in the provided {@link CaptureRequest}, at the maximum
     * rate possible.</p>
     *
     * @param request the request to repeat indefinitely
     * @param listener The callback object to notify every time the
     * request finishes processing. If null, no metadata will be
     * produced for this stream of requests, although image data will
     * still be produced.
     * @param handler the handler on which the listener should be invoked, or
     * {@code null} to use the current thread's {@link android.os.Looper
     * looper}.
     *
     * @return int A unique capture sequence ID used by
     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
     *
     * @throws CameraAccessException if the camera device is no longer connected or has
     *                               encountered a fatal error
     * @throws IllegalStateException if this session is no longer active, either because the session
     *                               was explicitly closed, a new session has been created
     *                               o
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值