* 更新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

@@ -194,8 +194,8 @@ CameraScan配置示例只需识别二维码的配置示例
//初始化解码配置
DecodeConfig decodeConfig = new DecodeConfig();
decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS)//如果只有识别二维码的需求,这样设置效率会更高
.setFullAreaScan(false)//设置是否全区域识别,默认true
.setAreaRectRatio(0.9f)//设置识别区域比例默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
.setFullAreaScan(false)//设置是否全区域识别,默认false
.setAreaRectRatio(0.8f)//设置识别区域比例默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
.setAreaRectVerticalOffset(0)//设置识别区域垂直方向偏移量默认为0为0表示居中可以为负数
.setAreaRectHorizontalOffset(0);//设置识别区域水平方向偏移量默认为0为0表示居中可以为负数
@@ -239,6 +239,12 @@ compileOptions {
## 版本记录
#### v2.0.12020-12-30
* 更新CameraX至v1.0.0-rc01
* 新增支持点击预览区域对焦目标
* 修改一些默认配置
* 优化细节
#### v2.0.02020-12-24
* 基于CameraX进行重构
* 抽象整体流程,可扩展性更高

View File

@@ -41,9 +41,9 @@ dependencies {
androidTestImplementation deps.test.runner
androidTestImplementation deps.test.espresso
implementation deps.support.design
implementation deps.support.appcompat
implementation deps.support.constraintlayout
implementation deps.androidx.design
implementation deps.androidx.appcompat
implementation deps.androidx.constraintlayout
implementation deps.kotlin
implementation deps.corektx

Binary file not shown.

View File

@@ -1 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":27,"versionName":"2.0.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":28,"versionName":"2.0.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]

View File

@@ -67,10 +67,10 @@ public class CustomCaptureActivity extends CaptureActivity {
decodeConfig.setHints(DecodeFormatManager.ALL_HINTS)////设置解码
.setSupportVerticalCode(true)//设置是否支持扫垂直的条码 (增强识别率,相应的也会增加性能消耗)
.setSupportLuminanceInvert(true)//设置是否支持识别反色码,黑白颜色反转(增强识别率,相应的也会增加性能消耗)
// .setAreaRectRatio(0.9f)//设置识别区域比例默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
.setAreaRectRatio(0.8f)//设置识别区域比例默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
// .setAreaRectVerticalOffset(0)//设置识别区域垂直方向偏移量默认为0为0表示居中可以为负数
// .setAreaRectHorizontalOffset(0)//设置识别区域水平方向偏移量默认为0为0表示居中可以为负数
.setFullAreaScan(true);//设置是否全区域识别,默认true
.setFullAreaScan(false);//设置是否全区域识别,默认false
//获取CameraScan里面有扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。
getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false

View File

@@ -41,8 +41,8 @@ public class QRCodeActivity extends CaptureActivity {
//初始化解码配置
DecodeConfig decodeConfig = new DecodeConfig();
decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS)//如果只有识别二维码的需求,这样设置效率会更高
.setFullAreaScan(false)//设置是否全区域识别,默认true
.setAreaRectRatio(0.9f)//设置识别区域比例默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
.setFullAreaScan(false)//设置是否全区域识别,默认false
.setAreaRectRatio(0.8f)//设置识别区域比例默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
.setAreaRectVerticalOffset(0)//设置识别区域垂直方向偏移量默认为0为0表示居中可以为负数
.setAreaRectHorizontalOffset(0);//设置识别区域水平方向偏移量默认为0为0表示居中可以为负数

View File

@@ -42,7 +42,7 @@ dependencies {
androidTestImplementation deps.test.runner
androidTestImplementation deps.test.espresso
api deps.support.appcompat
compileOnly deps.androidx.appcompat
api deps.zxing
api deps.camera_core
api deps.camera_camera2

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,7 +40,8 @@ public abstract class AreaRectAnalyzer extends ImageAnalyzer {
@Nullable
@Override
public Result analyze(byte[] data, int width, int height) {
if(mDecodeConfig == null || mDecodeConfig.isFullAreaScan()){
if(mDecodeConfig != null){
if(mDecodeConfig.isFullAreaScan()){
//mDecodeConfig为空或者支持全区域扫码识别时直接使用全区域进行扫码识别
return analyze(data,width,height,0,0,width,height);
}
@@ -43,10 +50,12 @@ public abstract class AreaRectAnalyzer extends ImageAnalyzer {
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,9 +54,10 @@ 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());
}

View File

@@ -1,7 +1,7 @@
//App
def app_version = [:]
app_version.versionCode = 27
app_version.versionName = "2.0.0"
app_version.versionCode = 28
app_version.versionName = "2.0.1"
ext.app_version = app_version
//build version
@@ -35,18 +35,18 @@ versions.coreKtx = "1.3.2"
//zxing
versions.zxing = "3.3.0"
versions.camerax = "1.0.0-beta12"
versions.camerax = "1.0.0-rc01"
versions.easypermissions = "3.0.0"
ext.versions = versions
//support
def support = [:]
support.design = "com.google.android.material:material:$versions.material"
support.appcompat = "androidx.appcompat:appcompat:$versions.appcompat"
support.constraintlayout = "androidx.constraintlayout:constraintlayout:$versions.constraintLayout"
deps.support = support
//androidx
def androidx = [:]
androidx.design = "com.google.android.material:material:$versions.material"
androidx.appcompat = "androidx.appcompat:appcompat:$versions.appcompat"
androidx.constraintlayout = "androidx.constraintlayout:constraintlayout:$versions.constraintLayout"
deps.androidx = androidx
//test
def test = [:]