* 更新CameraX至v1.0.0-rc01
* 新增支持点击预览区域对焦目标
* 修改一些默认配置
* 优化细节
This commit is contained in:
Jenly
2020-12-30 16:45:50 +08:00
parent c89c465d61
commit f7869595bf
15 changed files with 139 additions and 41 deletions

View File

@@ -44,6 +44,8 @@ public class DecodeConfig {
private Map<DecodeHintType,Object> hints = DecodeFormatManager.DEFAULT_HINTS;
public static final float DEFAULT_AREA_RECT_RATIO = 0.8f;
/**
* 是否支持使用多解码
*/
@@ -74,12 +76,12 @@ public class DecodeConfig {
/**
* 是否支持全区域扫码识别
*/
private boolean isFullAreaScan = true;
private boolean isFullAreaScan = false;
/**
* 识别区域比例默认0.9
* 识别区域比例默认0.8
*/
private float areaRectRatio = 0.9f;
private float areaRectRatio = DEFAULT_AREA_RECT_RATIO;
/**
* 识别区域垂直方向偏移量
*/

View File

@@ -1,16 +1,21 @@
package com.king.zxing;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.DisplayMetrics;
import android.util.Size;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.detector.MathUtils;
import com.king.zxing.analyze.Analyzer;
import com.king.zxing.analyze.MultiFormatAnalyzer;
import com.king.zxing.util.LogUtils;
@@ -21,7 +26,9 @@ import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.MeteringPoint;
import androidx.camera.core.Preview;
import androidx.camera.core.TorchState;
import androidx.camera.core.ZoomState;
@@ -38,6 +45,20 @@ import androidx.lifecycle.MutableLiveData;
*/
public class DefaultCameraScan extends CameraScan {
/**
* Defines the maximum duration in milliseconds between a touch pad
* touch and release for a given touch to be considered a tap (click) as
* opposed to a hover movement gesture.
*/
private static final int HOVER_TAP_TIMEOUT = 150;
/**
* Defines the maximum distance in pixels that a touch pad touch can move
* before being released for it to be considered a tap (click) as opposed
* to a hover movement gesture.
*/
private static final int HOVER_TAP_SLOP = 20;
private FragmentActivity mFragmentActivity;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
@@ -68,9 +89,14 @@ public class DefaultCameraScan extends CameraScan {
private BeepManager mBeepManager;
private AmbientLightManager mAmbientLightManager;
private int mOrientation;
private int mScreenWidth;
private int mScreenHeight;
private long mLastAutoZoomTime;
private long mLastHoveTapTime;
private boolean isClickTap;
private float mDownX;
private float mDownY;
public DefaultCameraScan(FragmentActivity activity, PreviewView previewView){
this.mFragmentActivity = activity;
@@ -107,8 +133,10 @@ public class DefaultCameraScan extends CameraScan {
handleAnalyzeResult(result);
});
mOrientation = mContext.getResources().getConfiguration().orientation;
ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
mPreviewView.setOnTouchListener((v, event) -> {
handlePreviewViewClickTap(event);
if(isNeedTouchZoom()){
return scaleGestureDetector.onTouchEvent(event);
}
@@ -139,6 +167,37 @@ public class DefaultCameraScan extends CameraScan {
}
}
private void handlePreviewViewClickTap(MotionEvent event){
if(event.getPointerCount() == 1){
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
isClickTap = true;
mDownX = event.getX();
mDownY = event.getY();
mLastHoveTapTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
isClickTap = MathUtils.distance(mDownX,mDownY,event.getX(),event.getY()) < HOVER_TAP_SLOP;
break;
case MotionEvent.ACTION_UP:
if(isClickTap && mLastHoveTapTime + HOVER_TAP_TIMEOUT > System.currentTimeMillis()){
startFocusAndMetering(event.getX(),event.getY());
}
break;
}
}
}
private void startFocusAndMetering(float x, float y){
if(mCamera != null){
LogUtils.d("startFocusAndMetering:" + x + "," + y);
MeteringPoint point = mPreviewView.getMeteringPointFactory().createPoint(x,y);
mCamera.getCameraControl().startFocusAndMetering(new FocusMeteringAction.Builder(point).build());
}
}
private void initConfig(){
if(mCameraConfig == null){
mCameraConfig = new CameraConfig();
@@ -148,6 +207,7 @@ public class DefaultCameraScan extends CameraScan {
}
}
@Override
public CameraScan setCameraConfig(CameraConfig cameraConfig) {
if(cameraConfig != null){
@@ -176,7 +236,7 @@ public class DefaultCameraScan extends CameraScan {
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST));
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), image -> {
if(isAnalyze && !isAnalyzeResult && mAnalyzer != null){
Result result = mAnalyzer.analyze(image);
Result result = mAnalyzer.analyze(image,mOrientation);
if(result != null){
mResultLiveData.postValue(result);
}

View File

@@ -5,6 +5,7 @@ import com.google.zxing.Result;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.ImageProxy;
import android.content.res.Configuration;
/**
* 分析器
@@ -12,6 +13,12 @@ import androidx.camera.core.ImageProxy;
*/
public interface Analyzer {
/**
* Analyzes an image to produce a result.
* @param image The image to analyze
* @param orientation {@link Configuration#ORIENTATION_LANDSCAPE}, {@link Configuration#ORIENTATION_PORTRAIT}.
* @return
*/
@Nullable
Result analyze(@NonNull ImageProxy image);
Result analyze(@NonNull ImageProxy image,int orientation);
}

View File

@@ -19,12 +19,18 @@ public abstract class AreaRectAnalyzer extends ImageAnalyzer {
DecodeConfig mDecodeConfig;
Map<DecodeHintType,?> mHints;
boolean isMultiDecode = true;
private float mAreaRectRatio = DecodeConfig.DEFAULT_AREA_RECT_RATIO;
private int mAreaRectHorizontalOffset = 0;
private int mAreaRectVerticalOffset = 0;
public AreaRectAnalyzer(@Nullable DecodeConfig config){
this.mDecodeConfig = config;
if(config != null){
mHints = config.getHints();
isMultiDecode = config.isMultiDecode();
mAreaRectRatio = config.getAreaRectRatio();
mAreaRectHorizontalOffset = config.getAreaRectHorizontalOffset();
mAreaRectVerticalOffset = config.getAreaRectVerticalOffset();
}else{
mHints = DecodeFormatManager.DEFAULT_HINTS;
}
@@ -34,19 +40,22 @@ public abstract class AreaRectAnalyzer extends ImageAnalyzer {
@Nullable
@Override
public Result analyze(byte[] data, int width, int height) {
if(mDecodeConfig == null || mDecodeConfig.isFullAreaScan()){
//mDecodeConfig为空或者支持全区域扫码识别时,直接使用全区域进行扫码识别
return analyze(data,width,height,0,0,width,height);
if(mDecodeConfig != null){
if(mDecodeConfig.isFullAreaScan()){
//mDecodeConfig为空或者支持全区域扫码识别时直接使用全区域进行扫码识别
return analyze(data,width,height,0,0,width,height);
}
Rect rect = mDecodeConfig.getAnalyzeAreaRect();
if(rect != null){//如果分析区域不为空,则使用指定的区域进行扫码识别
return analyze(data,width,height,rect.left,rect.top,rect.width(),rect.height());
}
}
Rect rect = mDecodeConfig.getAnalyzeAreaRect();
if(rect != null){//如果分析区域不为空,则使用指定的区域进行扫码识别
return analyze(data,width,height,rect.left,rect.top,rect.width(),rect.height());
}
//如果分析区域为空,则通过识别区域比例和相关的偏移量计算出最终的区域进行扫码识别
int size = (int)(Math.min(width,height) * mDecodeConfig.getAreaRectRatio());
int left = (width-size)/2 + mDecodeConfig.getAreaRectHorizontalOffset();
int top = (height-size)/2 + mDecodeConfig.getAreaRectVerticalOffset();
int size = (int)(Math.min(width,height) * mAreaRectRatio);
int left = (width-size)/2 + mAreaRectHorizontalOffset;
int top = (height-size)/2 + mAreaRectVerticalOffset;
return analyze(data,width,height,left,top,size,size);

View File

@@ -48,9 +48,10 @@ public abstract class BarcodeFormatAnalyzer extends AreaRectAnalyzer {
if(rawResult == null && mDecodeConfig != null){
if(rawResult == null && mDecodeConfig.isSupportVerticalCode()){
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
for (int y = 0; y < dataHeight; y++) {
for (int x = 0; x < dataWidth; x++){
rotatedData[x * dataHeight + dataHeight - y - 1] = data[x + y * dataWidth];
}
}
rawResult = decodeInternal(new PlanarYUVLuminanceSource(rotatedData,dataHeight,dataWidth,top,left,height,width,false),mDecodeConfig.isSupportVerticalCodeMultiDecode());
}

View File

@@ -1,6 +1,7 @@
package com.king.zxing.analyze;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import com.google.zxing.Result;
@@ -28,13 +29,24 @@ public abstract class ImageAnalyzer implements Analyzer {
public abstract Result analyze(byte[] data, int width, int height);
@Override
public Result analyze(@NonNull ImageProxy image) {
public Result analyze(@NonNull ImageProxy image,int orientation) {
if(image.getFormat() == ImageFormat.YUV_420_888){
@SuppressLint("UnsafeExperimentalUsageError")
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
return analyze(data,image.getWidth(),image.getHeight());
int width = image.getWidth();
int height = image.getHeight();
if(orientation == Configuration.ORIENTATION_PORTRAIT){
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++){
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
}
return analyze(rotatedData,height,width);
}
return analyze(data,width,height);
}
LogUtils.w("imageFormat: " + image.getFormat());
return null;

View File

@@ -54,8 +54,9 @@ public class MultiFormatAnalyzer extends AreaRectAnalyzer {
if(rawResult == null && mDecodeConfig.isSupportVerticalCode()){
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < dataHeight; y++) {
for (int x = 0; x < dataWidth; x++)
for (int x = 0; x < dataWidth; x++){
rotatedData[x * dataHeight + dataHeight - y - 1] = data[x + y * dataWidth];
}
}
rawResult = decodeInternal(new PlanarYUVLuminanceSource(rotatedData,dataHeight,dataWidth,top,left,height,width,false),mDecodeConfig.isSupportVerticalCodeMultiDecode());
}