安卓 CameraX 资料
一、简介
最近CameraX发布了第一个beta版本,相较于alpha版本的api疯狂改动慢慢趋于稳定。 本篇文章主要内容包含CameraX
的简单拍照保存、图像分析(可用于二维码识别等用途)、缩放、对焦等相关内容
注:当前本文使用的CameraX
版本为1.0.0-beta01
二、基础使用
-
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}"``复制代码
-
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"
/>````复制代码
-
构建图像捕获用例
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();``}``复制代码
-
构建图像分析用例(可用于二维码识别等用途)
注意: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();`` ``});``}``复制代码
-
初始化相机
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);
}``复制代码
-
绑定预览
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();``}``复制代码
-
拍照保存图片
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实现手势事件(点击、双击、缩放...)
-
回掉接口
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);``}``复制代码
-
手势监听代码
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;``}``复制代码
六、源码
原文地址:http://www.zyiz.net/tech/detail-123779.html
其它参考资料:https://blog.csdn.net/weixin_42324979/article/details/113524023