一、简介

最近CameraX发布了第一个beta版本,相较于alpha版本的api疯狂改动慢慢趋于稳定。 本篇文章主要内容包含CameraX的简单拍照保存、图像分析(可用于二维码识别等用途)、缩放、对焦等相关内容

注:当前本文使用的CameraX版本为1.0.0-beta01

二、基础使用

  1. gradle依赖

    def camerax_version = "1.0.0-beta01"``implementation "androidx.camera:camera-camera2:${camerax_version}"``implementation "androidx.camera:camera-view:1.0.0-alpha08"``implementation "androidx.camera:camera-lifecycle:${camerax_version}"``复制代码

  2. xml布局

    ``<``androidx.camera.view.PreviewView`` ``android:id``=``"@+id/view_finder"`` ``android:layout_width``=``"0dp"`` ``android:layout_height``=``"0dp"`` ``app:layout_constraintBottom_toBottomOf``=``"parent"`` ``app:layout_constraintEnd_toEndOf``=``"parent"`` ``app:layout_constraintStart_toStartOf``=``"parent"`` ``app:layout_constraintTop_toTopOf``=``"parent" />````复制代码

  3. 构建图像捕获用例

    private void initImageCapture() { ``// 构建图像捕获用例`` ``mImageCapture = new ImageCapture.Builder()`` ``.setFlashMode(ImageCapture.FLASH_MODE_AUTO)`` ``.setTargetAspectRatio(AspectRatio.RATIO_4_3)`` ``.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)`` ``.build(); ``// 旋转监听`` ``OrientationEventListener orientationEventListener = new OrientationEventListener((Context) this) {`` ``@Override`` ``public void onOrientationChanged(int orientation) {`` ``int rotation; ``// Monitors orientation values to determine the target rotation value`` ``if (orientation >= 45 && orientation < ``135``) {`` ``rotation = ``Surface``.ROTATION_270;`` ``} else if (orientation >= 135 && orientation < ``225``) {`` ``rotation = ``Surface``.ROTATION_180;`` ``} else if (orientation >= 225 && orientation < 315) {`` ``rotation = Surface.ROTATION_90;`` ``} else {`` ``rotation = Surface.ROTATION_0;`` ``} ``mImageCapture.setTargetRotation(rotation);`` ``}`` ``}; ``orientationEventListener.enable();``}``复制代码

  4. 构建图像分析用例(可用于二维码识别等用途)

    注意:Analyzer回掉方法中如果不调用image.close()将不会获取到下一张图片

    private void initImageAnalysis() { ``mImageAnalysis = new ImageAnalysis.Builder()`` ``// 分辨率`` ``.setTargetResolution(new Size(1280, 720))`` ``// 仅将最新图像传送到分析仪,并在到达图像时将其丢弃。`` ``.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)`` ``.build(); ``mImageAnalysis.setAnalyzer(executor, image -> {`` ``int rotationDegrees = image.getImageInfo().getRotationDegrees();`` ``LogUtils.e("Analysis#rotationDegrees", rotationDegrees);`` ``ImageProxy.PlaneProxy[] planes = image.getPlanes(); ``ByteBuffer buffer = planes[0].getBuffer();`` ``// 转为byte[]`` ``// byte[] b = new byte[buffer.remaining()];`` ``// LogUtils.e(b);`` ``// TODO: 分析完成后关闭图像参考,否则会阻塞其他图像的产生`` ``// image.close();`` ``});``}``复制代码

  5. 初始化相机

    private Executor executor;``...``private void initCamera() { ``executor = ContextCompat.getMainExecutor(this); ``cameraProviderFuture = ProcessCameraProvider.getInstance(this);`` ``cameraProviderFuture.addListener(() -> {`` ``try {`` ``ProcessCameraProvider cameraProvider = cameraProviderFuture.get();`` ``// 绑定预览`` ``bindPreview(cameraProvider);`` ``} catch (ExecutionException | InterruptedException e) {`` ``// No errors need to be handled for this Future.`` ``// This should never be reached.`` ``}`` ``}, executor); }``复制代码

  6. 绑定预览

    private void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {`` ``Preview preview = new Preview.Builder()`` ``.build(); ``preview.setSurfaceProvider(mViewFinder.getPreviewSurfaceProvider()); ``CameraSelector cameraSelector = new CameraSelector.Builder()`` ``.requireLensFacing(CameraSelector.LENS_FACING_BACK)`` ``.build(); ``// mImageCapture 图像捕获用例`` ``// mImageAnalysis 图像分析用例`` ``Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, mImageCapture, mImageAnalysis, preview); ``mCameraInfo = camera.getCameraInfo();`` ``mCameraControl = camera.getCameraControl(); ``initCameraListener();``}``复制代码

  7. 拍照保存图片

    public void saveImage() {`` ``File file = new File(getExternalMediaDirs()[0], System.currentTimeMillis() + ".jpg");`` ``ImageCapture.OutputFileOptions outputFileOptions =`` ``new ImageCapture.OutputFileOptions.Builder(file).build();`` ``mImageCapture.takePicture(outputFileOptions, executor,`` ``new ImageCapture.OnImageSavedCallback() { ``@Override`` ``public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {`` ``String msg = "图片保存成功: " + file.getAbsolutePath();`` ``showMsg(msg);`` ``LogUtils.d(msg);`` ``Uri contentUri = Uri.fromFile(new File(file.getAbsolutePath()));`` ``Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, contentUri);`` ``sendBroadcast(mediaScanIntent);`` ``} ``@Override`` ``public void onError(@NonNull ImageCaptureException exception) {`` ``String msg = "图片保存失败: " + exception.getMessage();`` ``showMsg(msg);`` ``LogUtils.e(msg);`` ``}`` ``}`` ``);``}``复制代码

三、自定义PreviewView实现手势事件(点击、双击、缩放...)

  1. 回掉接口

    public interface CustomTouchListener {`` ``/**`` ``* 放大`` ``*/`` ``void zoom(); ``/**`` ``* 缩小`` ``*/`` ``void ZoomOut(); ``/**`` ``* 点击`` ``*/`` ``void click(float x, float y); ``/**`` ``* 双击`` ``*/`` ``void doubleClick(float x, float y); ``/**`` ``* 长按`` ``*/`` ``void longClick(float x, float y);``}``复制代码

  2. 手势监听代码

    public class CameraXCustomPreviewView extends PreviewView {`` ``private GestureDetector mGestureDetector;`` ``/**`` ``* 缩放相关`` ``*/`` ``private float currentDistance = 0;`` ``private float lastDistance = 0; ``... 省略部分构造方法 ``public CameraXCustomPreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {`` ``super(context, attrs, defStyleAttr, defStyleRes); ``mGestureDetector = new GestureDetector(context, onGestureListener);`` ``mGestureDetector.setOnDoubleTapListener(onDoubleTapListener); ``// mScaleGestureDetector = new ScaleGestureDetector(context, onScaleGestureListener);`` ``// 解决长按屏幕无法拖动,但是会造成无法识别长按事件`` ``// mGestureDetector.setIsLongpressEnabled(false);`` ``} ``@Override`` ``public boolean onTouchEvent(MotionEvent event) {`` ``// 接管onTouchEvent`` ``return mGestureDetector.onTouchEvent(event);`` ``} ``GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {`` ``@Override`` ``public boolean onDown(MotionEvent e) {`` ``LogUtils.i("onDown: 按下");`` ``return true;`` ``} ``@Override`` ``public void onShowPress(MotionEvent e) {`` ``LogUtils.i("onShowPress: 刚碰上还没松开");`` ``} ``@Override`` ``public boolean onSingleTapUp(MotionEvent e) {`` ``LogUtils.i("onSingleTapUp: 轻轻一碰后马上松开");`` ``return true;`` ``} ``@Override`` ``public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {`` ``LogUtils.i("onScroll: 按下后拖动");`` ``// 大于两个触摸点`` ``if (e2.getPointerCount() >= 2) { ``//event中封存了所有屏幕被触摸的点的信息,第一个触摸的位置可以通过event.getX(0)/getY(0)得到`` ``float offSetX = e2.getX(0) - e2.getX(1);`` ``float offSetY = e2.getY(0) - e2.getY(1);`` ``//运用三角函数的公式,通过计算X,Y坐标的差值,计算两点间的距离`` ``currentDistance = (float) Math.sqrt(offSetX * offSetX + offSetY * offSetY);`` ``if (lastDistance == 0) {//如果是第一次进行判断`` ``lastDistance = currentDistance;`` ``} else {`` ``if (currentDistance - lastDistance > 10) {`` ``// 放大`` ``if (mCustomTouchListener != null) {`` ``mCustomTouchListener.zoom();`` ``}`` ``} else if (lastDistance - currentDistance > 10) {`` ``// 缩小`` ``if (mCustomTouchListener != null) {`` ``mCustomTouchListener.ZoomOut();`` ``}`` ``}`` ``}`` ``//在一次缩放操作完成后,将本次的距离赋值给lastDistance,以便下一次判断`` ``//但这种方法写在move动作中,意味着手指一直没有抬起,监控两手指之间的变化距离超过10`` ``//就执行缩放操作,不是在两次点击之间的距离变化来判断缩放操作`` ``//故这种将本次距离留待下一次判断的方法,不能在两次点击之间使用`` ``lastDistance = currentDistance;`` ``}`` ``return true;`` ``} ``@Override`` ``public void onLongPress(MotionEvent e) {`` ``LogUtils.i("onLongPress: 长按屏幕");`` ``if (mCustomTouchListener != null) {`` ``mCustomTouchListener.longClick(e.getX(), e.getY());`` ``}`` ``} ``@Override`` ``public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {`` ``LogUtils.i("onFling: 滑动后松开");`` ``currentDistance = 0;`` ``lastDistance = 0;`` ``return true;`` ``}`` ``}; ``GestureDetector.OnDoubleTapListener onDoubleTapListener = new GestureDetector.OnDoubleTapListener() {`` ``@Override`` ``public boolean onSingleTapConfirmed(MotionEvent e) {`` ``LogUtils.i("onSingleTapConfirmed: 严格的单击");`` ``if (mCustomTouchListener != null) {`` ``mCustomTouchListener.click(e.getX(), e.getY());`` ``}`` ``return true;`` ``} ``@Override`` ``public boolean onDoubleTap(MotionEvent e) {`` ``LogUtils.i("onDoubleTap: 双击");`` ``if (mCustomTouchListener != null) {`` ``mCustomTouchListener.doubleClick(e.getX(), e.getY());`` ``}`` ``return true;`` ``} ``@Override`` ``public boolean onDoubleTapEvent(MotionEvent e) {`` ``LogUtils.i("onDoubleTapEvent: 表示发生双击行为");`` ``return true;`` ``}`` ``};``}``复制代码

四、双指滑动/双击缩放、点击对焦

private void initCameraListener() {``  ``LiveData<``ZoomState``> zoomState = mCameraInfo.getZoomState();``  ``float maxZoomRatio = zoomState.getValue().getMaxZoomRatio();``  ``float minZoomRatio = zoomState.getValue().getMinZoomRatio();``  ``LogUtils.e(maxZoomRatio);``  ``LogUtils.e(minZoomRatio);` `  ``mViewFinder.setCustomTouchListener(new CameraXCustomPreviewView.CustomTouchListener() {``    ``@Override``    ``public void zoom() {``      ``float zoomRatio = zoomState.getValue().getZoomRatio();``      ``if (zoomRatio < ``maxZoomRatio``) {``        ``mCameraControl.setZoomRatio((float) (zoomRatio + 0.1));``      ``}``    ``}` `    ``@Override``    ``public void ZoomOut() {``      ``float ``zoomRatio` `= ``zoomState``.getValue().getZoomRatio();``      ``if (zoomRatio > minZoomRatio) {``        ``mCameraControl.setZoomRatio((float) (zoomRatio - 0.1));``      ``}``    ``}` `    ``@Override``    ``public void click(float x, float y) {``      ``// TODO 对焦``      ``MeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f, 1.0f);``      ``MeteringPoint point = factory.createPoint(x, y);``      ``FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)``          ``// auto calling cancelFocusAndMetering in 3 seconds``          ``.setAutoCancelDuration(3, TimeUnit.SECONDS)``          ``.build();` `      ``mFocusView.startFocus(new Point((int) x, (int) y));``      ``ListenableFuture future = mCameraControl.startFocusAndMetering(action);``      ``future.addListener(() -> {``        ``try {``          ``FocusMeteringResult result = (FocusMeteringResult) future.get();``          ``if (result.isFocusSuccessful()) {``            ``mFocusView.onFocusSuccess();``          ``} else {``            ``mFocusView.onFocusFailed();``          ``}``        ``} catch (Exception e) {``        ``}``      ``}, executor);``    ``}` `    ``@Override``    ``public void doubleClick(float x, float y) {``      ``// 双击放大缩小``      ``float zoomRatio = zoomState.getValue().getZoomRatio();``      ``if (zoomRatio > minZoomRatio) {``        ``mCameraControl.setLinearZoom(0f);``      ``} else {``        ``mCameraControl.setLinearZoom(0.5f);``      ``}``    ``}` `    ``@Override``    ``public void longClick(float x, float y) {` `    ``}``  ``});``}``复制代码

五、闪光灯

switch (mImageCapture.getFlashMode()) {``  ``case ImageCapture.FLASH_MODE_AUTO:``    ``mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_ON);``    ``mBtnLight.setText("闪光灯:开");``    ``break;``  ``case ImageCapture.FLASH_MODE_ON:``    ``mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_OFF);``    ``mBtnLight.setText("闪光灯:关");``    ``break;``  ``case ImageCapture.FLASH_MODE_OFF:``    ``mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_AUTO);``    ``mBtnLight.setText("闪光灯:自动");``    ``break;``}``复制代码

六、源码

源码:github.com/sdwfqin/And…

原文地址:http://www.zyiz.net/tech/detail-123779.html

其它参考资料:https://blog.csdn.net/weixin_42324979/article/details/113524023

发表评论

邮箱地址不会被公开。 必填项已用*标注