diff --git a/README.md b/README.md index 0c837c5..b1ef78f 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,11 @@ ZXingLite for Android 是ZXing的精简版,基于ZXing库优化扫码和生成 | frameColor | color |#7F1FB3E2| 扫描区边框的颜色 | | cornerColor | color |#FF1FB3E2| 扫描区边角的颜色 | | laserColor | color |#FF1FB3E2| 扫描区激光线的颜色 | -| resultPointColor | color |#C0EFBD21| 扫描区结果点的颜色 | | labelText | string | | 扫描提示文本信息 | | labelTextColor | color |#FFC0C0C0| 提示文本字体颜色 | | labelTextSize | dimension |14sp| 提示文本字体大小 | | labelTextPadding | dimension |24dp| 提示文本距离扫描区的间距 | | labelTextLocation | enum |bottom| 提示文本显示位置 | -| showResultPoint | boolean | false | 是否显示合适的扫码结果点 | | frameWidth | dimension | | 扫码框宽度 | | frameHeight | dimension | | 扫码框高度 | | laserStyle | enum | line | 扫描激光的样式 | @@ -50,33 +48,28 @@ ZXingLite for Android 是ZXing的精简版,基于ZXing库优化扫码和生成 | framePaddingTop | dimension | 0 | 扫码框上边的内间距 | | framePaddingRight | dimension | 0 | 扫码框右边的内间距 | | framePaddingBottom | dimension | 0 | 扫码框下边的内间距 | +| frameGravity | enum | center | 扫码框对齐方式 | ## 引入 -### Maven: -```maven - - com.king.zxing - zxing-lite - 1.1.9 - pom - -``` +最新版本 ### Gradle: ```gradle //AndroidX 版本 +implementation 'com.king.zxing:zxing-lite:2.0.0' + +``` + +以前 **v1.x** 旧版本 +```gradle +//AndroidX 版本 implementation 'com.king.zxing:zxing-lite:1.1.9-androidx' //Android 版本 implementation 'com.king.zxing:zxing-lite:1.1.9' ``` -### Lvy: -```lvy - - - -``` + ###### 如果Gradle出现compile失败的情况,可以在Project的build.gradle里面添加如下:(也可以使用上面的JitPack来compile) ```gradle @@ -88,47 +81,50 @@ allprojects { } ``` -## 引入的库: -```gradle -//AndroidX -api 'androidx.appcompat:appcompat:1.1.0' -api 'com.google.zxing:core:3.3.3' +## 版本说明 -//Android -api 'com.android.support:appcompat-v7:28.0.0' -api 'com.google.zxing:core:3.3.3' -``` +#### v2.x 相对于 v1.x 的优势 +* v2.x基于CameraX,抽象整体流程,可扩展性更高。 +* v2.x基于CameraX通过预览裁剪的方式确保预览界面不变形,无需铺满屏幕,就能适配(v1.x通过遍历Camera支持预览的尺寸,找到与屏幕最接近的比例,减少变形的可能性(需铺满屏幕,才能适配)) +* v2.x如果您是通过继承CaptureActivity或CaptureFragment实现扫码功能,那么动态权限申请相关都已经在CaptureActivity或CaptureFragment处理好了,无需您格外申请。 + +(【v1.1.9】)[https://github.com/jenly1314/ZXingLite/tree/androidx] 如果您正在使用 **1.x** 版本请调转,当前 **2.x** 版本已经基于 **Camerx** 进行重构,不支持升级,请在新项目中使用。 + +下面的示例和相关说明都是针对于当前最新版本,如果您使用的是v1.x旧版本,(请戳此处查看分支)[https://github.com/jenly1314/ZXingLite/tree/androidx]。 ## 示例 布局示例 -> 可自定义布局(覆写getLayoutId方法),布局内至少要保证有SurfaceView和ViewfinderView,控件id可根据覆写CaptureActivity 的 getSurfaceViewId 和 getViewfinderViewId方法自定义 +> 可自定义布局(覆写getLayoutId方法),布局内至少要保证有PreviewView。 -> ivTorch为 v1.1.4版本新增的手电筒按钮,如果想改ID可通过CaptureActivity中的getIvTorchId自定义ID +> PreviewView 用来预览,布局内至少要保证有PreviewView,如果是继承CaptureActivity或CaptureFragment,控件id可覆写getPreviewViewId方法自定义 + +> ViewfinderView 用来渲染扫码视图,给用户起到一个视觉效果,本身扫码识别本身没有关系,如果是继承CaptureActivity或CaptureFragment,控件id可复写getViewfinderViewId方法自定义,默认为previewView,返回0表示无需ViewfinderView + +> ivFlashlight 用来内置手电筒,如果是继承CaptureActivity或CaptureFragment,控件id可复写getFlashlightId方法自定义,默认为ivFlashlight。返回0表示无需内置手电筒。您也可以自己去定义 -> 如果是从v1.1.4以前版本升级至v1.1.4以上版本,请参考如下布局示例(新增ivTorch),也可忽略内置手电筒功能可直接将CaptureActivity中的getIvTorchId方法返回0 ```Xml - + - - - - + + + + ``` 或在你的布局中添加 @@ -152,6 +148,44 @@ api 'com.google.zxing:core:3.3.3' CodeUtils.parseQRCode(bitmapPath); ``` +CameraScan配置示例 +```java + //获取CameraScan,扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法,即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。 + getCameraScan().setPlayBeep(true)//设置是否播放音效,默认为false + .setVibrate(true)//设置是否震动,默认为false + .setCameraConfig(new CameraConfig())//设置相机配置信息,CameraConfig可覆写options方法自定义配置 + .setNeedAutoZoom(false)//二维码太小时可自动缩放,默认为false + .setNeedTouchZoom(true)//支持多指触摸捏合缩放,默认为true + .setDarkLightLux(45f)//设置光线足够暗的阈值(单位:lux),需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 + .setBrightLightLux(100f)//设置光线足够明亮的阈值(单位:lux),需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 + .bindFlashlightView(ivFlashlight)//绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒按钮 + .setOnScanResultCallback(this)//设置扫码结果回调,需要自己处理或者需要连扫时,可设置回调,自己去处理相关逻辑 + .setAnalyzer(new MultiFormatAnalyzer(new DecodeConfig()))//设置分析器,DecodeConfig可以配置一些解码时的配置信息,如果内置的不满足您的需求,你也可以自定义实现, + .setAnalyzeImage(true)//设置是否分析图片,默认为true。如果设置为false,相当于关闭了扫码识别功能 + .startCamera();//启动预览 + + + //设置闪光灯(手电筒)是否开启,需在startCamera之后调用才有效 + getCameraScan().enableTorch(torch); + +``` + +CameraScan配置示例(只需识别二维码的配置示例) +```java + //初始化解码配置 + DecodeConfig decodeConfig = new DecodeConfig(); + decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS)//如果只有识别二维码的需求,这样设置效率会更高 + .setFullAreaScan(false)//设置是否全区域识别,默认true + .setAreaRectRatio(0.9f)//设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 + .setAreaRectVerticalOffset(0)//设置识别区域垂直方向偏移量,默认为0,为0表示居中,可以为负数 + .setAreaRectHorizontalOffset(0);//设置识别区域水平方向偏移量,默认为0,为0表示居中,可以为负数 + + //在启动预览之前,设置分析器,只识别二维码 + getCameraScan() + .setVibrate(true)//设置是否震动,默认为false + .setAnalyzer(new MultiFormatAnalyzer(decodeConfig));//设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现 +``` + 如果直接使用CaptureActivity需在您项目的AndroidManifest中添加如下配置 ```Xml 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。(适用于大多场景,并无需关心扫码相关逻辑,自定义布局时需覆写getLayoutId方法) -> 3、在你项目的Activity或者Fragment中创建一个CaptureHelper并在相应的生命周期中调用CaptureHelper的周期。(适用于想在扫码界面写交互逻辑,又因为项目架构或其它原因,无法直接或间接继承CaptureActivity或CaptureFragment时使用) +> 3、在你项目的Activity或者Fragment中实例化一个CameraScan即可。(适用于想在扫码界面写交互逻辑,又因为项目架构或其它原因,无法直接或间接继承CaptureActivity或CaptureFragment时使用) -> 4、参照CaptureHelper写一个自定义的扫码帮助类,其它步骤同方式3。(扩展高级用法,谨慎使用) +> 4、继承CameraScan自己实现一个,可参照默认实现类DefaultCameraScan,其它步骤同方式3。(扩展高级用法,谨慎使用) ### 其他 @@ -186,6 +220,12 @@ compileOptions { ## 版本记录 +#### v2.0.0:2020-12-24 +* 基于CameraX进行重构 +* 抽象整体流程,可扩展性更高 +* 从2.x开始只支持AndroidX +* minSdk要求从 **16+** 改为 **21+** + #### v1.1.9:2020-4-28 * 修复1.1.8版本优化细节时,不小心改出个Bug(fix #86) @@ -199,7 +239,7 @@ compileOptions { #### v1.1.6:2019-12-27 * 生成条形码/二维码时支持自定义配置颜色 -* 支持识别反色码(增强识别率,默认不支持,需通过CaptureHelper.supportLuminanceInvert(true)开启) +* 支持识别反色码(增强识别率,默认不支持,需通过CameraScan.supportLuminanceInvert(true)开启) #### v1.1.5:2019-12-16 * 优化Camera初始化相关策略,减少出现卡顿的可能性 @@ -218,7 +258,7 @@ compileOptions { #### v1.1.1:2019-5-20 * 支持扫二维码过小时,自动缩放 -* 支持识别垂直条形码(增强条形码识别,默认不支持,需通过CaptureHelper.supportVerticalCode(true)开启) +* 支持识别垂直条形码(增强条形码识别,默认不支持,需通过CameraScan.supportVerticalCode(true)开启) #### v1.1.0:2019-4-19 * 将扫码相关逻辑与界面分离,ZXingLite使用更容易扩展 diff --git a/app/release/app-release.apk b/app/release/app-release.apk index bb4b4e2..5e343da 100644 Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ diff --git a/app/release/output.json b/app/release/output.json deleted file mode 100644 index 22cbfcc..0000000 --- a/app/release/output.json +++ /dev/null @@ -1 +0,0 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":26,"versionName":"1.1.9-androidx","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/java/com/king/zxing/app/CustomActivity.java b/app/src/main/java/com/king/zxing/app/CustomActivity.java index 99754be..4305c56 100644 --- a/app/src/main/java/com/king/zxing/app/CustomActivity.java +++ b/app/src/main/java/com/king/zxing/app/CustomActivity.java @@ -2,8 +2,6 @@ package com.king.zxing.app; import android.app.Activity; import android.os.Bundle; -import android.view.MotionEvent; -import android.view.SurfaceView; import android.view.View; import android.widget.TextView; import android.widget.Toast; @@ -11,7 +9,6 @@ import android.widget.Toast; import com.google.zxing.Result; import com.king.zxing.CameraScan; import com.king.zxing.DefaultCameraScan; -import com.king.zxing.ICameraScan; import com.king.zxing.ViewfinderView; import com.king.zxing.app.util.StatusBarUtils; @@ -63,9 +60,9 @@ public class CustomActivity extends AppCompatActivity implements CameraScan.OnSc isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false); mCameraScan = new DefaultCameraScan(this,previewView); - mCameraScan.setOnScanResultCallback(this); - - mCameraScan.startCamera(); + mCameraScan.setOnScanResultCallback(this) + .setVibrate(true) + .startCamera(); } diff --git a/app/src/main/java/com/king/zxing/app/CustomCaptureActivity.java b/app/src/main/java/com/king/zxing/app/CustomCaptureActivity.java index bb7a594..7484284 100644 --- a/app/src/main/java/com/king/zxing/app/CustomCaptureActivity.java +++ b/app/src/main/java/com/king/zxing/app/CustomCaptureActivity.java @@ -21,7 +21,11 @@ import android.widget.TextView; import android.widget.Toast; import com.google.zxing.Result; +import com.king.zxing.CameraConfig; import com.king.zxing.CaptureActivity; +import com.king.zxing.DecodeConfig; +import com.king.zxing.DecodeFormatManager; +import com.king.zxing.analyze.MultiFormatAnalyzer; import com.king.zxing.app.util.StatusBarUtils; @@ -58,9 +62,27 @@ public class CustomCaptureActivity extends CaptureActivity { @Override public void initCameraScan() { super.initCameraScan(); - //获取CaptureHelper,里面有扫码相关的配置设置 - getCameraScan().setPlayBeep(false)//播放音效 - .setVibrate(true);//震动 + //初始化解码配置 + DecodeConfig decodeConfig = new DecodeConfig(); + decodeConfig.setHints(DecodeFormatManager.DEFAULT_HINTS)////设置解码 +// .setAreaRectRatio(0.9f)//设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 +// .setAreaRectVerticalOffset(0)//设置识别区域垂直方向偏移量,默认为0,为0表示居中,可以为负数 +// .setAreaRectHorizontalOffset(0)//设置识别区域水平方向偏移量,默认为0,为0表示居中,可以为负数 + .setFullAreaScan(true);//设置是否全区域识别,默认true + + //获取CameraScan,里面有扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法,即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。 + getCameraScan().setPlayBeep(true)//设置是否播放音效,默认为false + .setVibrate(true)//设置是否震动,默认为false + .setCameraConfig(new CameraConfig())//设置相机配置信息,CameraConfig可覆写options方法自定义配置 + .setNeedAutoZoom(false)//二维码太小时可自动缩放,默认为false + .setNeedTouchZoom(true)//支持多指触摸捏合缩放,默认为true + .setDarkLightLux(45f)//设置光线足够暗的阈值(单位:lux),需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 + .setBrightLightLux(100f)//设置光线足够明亮的阈值(单位:lux),需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 + .bindFlashlightView(ivFlashlight)//绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒按钮 + .setOnScanResultCallback(this)//设置扫码结果回调,需要自己处理或者需要连扫时,可设置回调,自己去处理相关逻辑 + .setAnalyzer(new MultiFormatAnalyzer(decodeConfig))//设置分析器,DecodeConfig可以配置一些解码时的配置信息,如果内置的不满足您的需求,你也可以自定义实现, + .setAnalyzeImage(true)//设置是否分析图片,默认为true。如果设置为false,相当于关闭了扫码识别功能 + .startCamera();//启动预览 } /** diff --git a/app/src/main/java/com/king/zxing/app/QRCodeActivity.java b/app/src/main/java/com/king/zxing/app/QRCodeActivity.java index 8ebd4c1..221b14e 100644 --- a/app/src/main/java/com/king/zxing/app/QRCodeActivity.java +++ b/app/src/main/java/com/king/zxing/app/QRCodeActivity.java @@ -5,6 +5,7 @@ import android.widget.TextView; import com.google.zxing.Result; import com.king.zxing.CaptureActivity; +import com.king.zxing.DecodeConfig; import com.king.zxing.DecodeFormatManager; import com.king.zxing.analyze.MultiFormatAnalyzer; import com.king.zxing.app.util.StatusBarUtils; @@ -36,10 +37,20 @@ public class QRCodeActivity extends CaptureActivity { @Override public void initCameraScan() { super.initCameraScan(); - //在启动预览之前,设置分析器,只识别二维码,如果只有识别二维码的需求,这样效率更多高 + + //初始化解码配置 + DecodeConfig decodeConfig = new DecodeConfig(); + decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS)//如果只有识别二维码的需求,这样设置效率会更高 + .setFullAreaScan(false)//设置是否全区域识别,默认true + .setAreaRectRatio(0.9f)//设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 + .setAreaRectVerticalOffset(0)//设置识别区域垂直方向偏移量,默认为0,为0表示居中,可以为负数 + .setAreaRectHorizontalOffset(0);//设置识别区域水平方向偏移量,默认为0,为0表示居中,可以为负数 + + //在启动预览之前,设置分析器,只识别二维码 getCameraScan() - .setVibrate(true) - .setAnalyzer(new MultiFormatAnalyzer(DecodeFormatManager.QR_CODE_HINTS)); + .setVibrate(true)//设置是否震动,默认为false + .setNeedAutoZoom(true)//二维码太小时可自动缩放,默认为false + .setAnalyzer(new MultiFormatAnalyzer(decodeConfig));//设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现 } @Override diff --git a/app/src/main/res/layout/custom_activity.xml b/app/src/main/res/layout/custom_activity.xml index 7d751f3..f624eba 100644 --- a/app/src/main/res/layout/custom_activity.xml +++ b/app/src/main/res/layout/custom_activity.xml @@ -18,7 +18,6 @@ app:laserColor="@color/colorAccent" app:frameColor="@color/colorPrimary" app:cornerColor="@color/colorPrimary" - app:resultPointColor="@color/colorAccent" app:labelTextLocation="bottom" app:laserStyle="grid" app:gridHeight="0dp"/> diff --git a/app/src/main/res/layout/custom_capture_activity.xml b/app/src/main/res/layout/custom_capture_activity.xml index d4c9548..75aa57d 100644 --- a/app/src/main/res/layout/custom_capture_activity.xml +++ b/app/src/main/res/layout/custom_capture_activity.xml @@ -18,10 +18,8 @@ app:laserColor="@color/colorAccent" app:frameColor="@color/colorPrimary" app:cornerColor="@color/colorPrimary" - app:resultPointColor="@color/colorAccent" app:labelTextLocation="bottom" - app:laserStyle="grid" - app:showResultPoint="true"/> + app:laserStyle="grid" /> + app:laserStyle="grid" /> Jenly */ public class CameraConfig { diff --git a/lib/src/main/java/com/king/zxing/CameraScan.java b/lib/src/main/java/com/king/zxing/CameraScan.java index 2069446..6fd2999 100644 --- a/lib/src/main/java/com/king/zxing/CameraScan.java +++ b/lib/src/main/java/com/king/zxing/CameraScan.java @@ -1,27 +1,23 @@ package com.king.zxing; import android.content.Intent; -import android.view.MotionEvent; import android.view.View; import com.google.zxing.Result; +import com.google.zxing.qrcode.QRCodeReader; import com.king.zxing.analyze.Analyzer; -import com.king.zxing.util.LogUtils; +import com.king.zxing.analyze.AreaRectAnalyzer; +import com.king.zxing.analyze.BarcodeFormatAnalyzer; +import com.king.zxing.analyze.ImageAnalyzer; +import com.king.zxing.analyze.MultiFormatAnalyzer; import androidx.annotation.Nullable; import androidx.camera.core.CameraSelector; -import static com.king.zxing.CameraScan.*; - /** * @author Jenly */ -public abstract class CameraScan implements ICameraScan { - - /** - * 默认触控误差值 - */ - private static final int DEVIATION = 6; +public abstract class CameraScan implements ICamera,ICameraControl { public static String SCAN_RESULT = "SCAN_RESULT"; @@ -34,14 +30,20 @@ public abstract class CameraScan implements ICameraScan { /** * 是否需要支持自动缩放 */ - private boolean isNeedAutoZoom = true; + private boolean isNeedAutoZoom = false; /** * 是否需要支持触摸缩放 */ private boolean isNeedTouchZoom = true; - private float mOldDistance; + /** + * 是否需要支持触摸缩放 + * @return + */ + protected boolean isNeedTouchZoom() { + return isNeedTouchZoom; + } /** @@ -72,46 +74,6 @@ public abstract class CameraScan implements ICameraScan { return this; } - protected boolean onTouchEvent(MotionEvent event) { - if(getCamera() != null && isNeedTouchZoom){ - LogUtils.d("action:" + (event.getAction() & MotionEvent.ACTION_MASK)); - if(event.getPointerCount() > 1) { - switch (event.getAction() & MotionEvent.ACTION_MASK) {//多点触控 - case MotionEvent.ACTION_POINTER_DOWN: - mOldDistance = calcFingerSpacing(event); - break; - case MotionEvent.ACTION_MOVE: - float newDistance = calcFingerSpacing(event); - - if (newDistance > mOldDistance + DEVIATION) { - zoomIn(); - } else if (newDistance < mOldDistance - DEVIATION) { - zoomOut(); - } - mOldDistance = newDistance; - break; - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_UP: - return false; - } - - } - return true; - } - return false; - } - - /** - * 计算两指间距离 - * @param event - * @return - */ - private float calcFingerSpacing(MotionEvent event) { - float x = event.getX(0) - event.getX(1); - float y = event.getY(0) - event.getY(1); - return (float) Math.sqrt(x * x + y * y); - } - /** * 设置相机配置,请在{@link #startCamera()}之前调用 * @param cameraConfig @@ -125,29 +87,18 @@ public abstract class CameraScan implements ICameraScan { public abstract CameraScan setAnalyzeImage(boolean analyze); /** - * 设置分析器 + * 设置分析器,内置了一些{@link Analyzer}的实现类如下 + * @see {@link MultiFormatAnalyzer} + * @see {@link AreaRectAnalyzer} + * @see {@link ImageAnalyzer} + * + * @see {@link BarcodeFormatAnalyzer} + * @see {@link QRCodeReader} + * * @param analyzer */ public abstract CameraScan setAnalyzer(Analyzer analyzer); - /** - * 设置手电筒是否开启 - * @param torch - */ - public abstract CameraScan enableTorch(boolean torch); - - /** - * 手电筒是否开启 - * @return - */ - public abstract boolean isTorchEnabled(); - - /** - * 是否支持闪光灯 - * @return - */ - public abstract boolean hasFlashUnit(); - /** * 设置是否震动 * @param vibrate @@ -172,6 +123,17 @@ public abstract class CameraScan implements ICameraScan { */ public abstract CameraScan bindFlashlightView(@Nullable View v); + /** + * 设置光线足够暗的阈值(单位:lux),需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 + * @param lightLux + */ + public abstract CameraScan setDarkLightLux(float lightLux); + + /** + * 设置光线足够明亮的阈值(单位:lux),需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 + * @param lightLux + */ + public abstract CameraScan setBrightLightLux(float lightLux); public interface OnScanResultCallback{ boolean onScanResultCallback(Result result); diff --git a/lib/src/main/java/com/king/zxing/CaptureActivity.java b/lib/src/main/java/com/king/zxing/CaptureActivity.java index e539722..b1d6f08 100644 --- a/lib/src/main/java/com/king/zxing/CaptureActivity.java +++ b/lib/src/main/java/com/king/zxing/CaptureActivity.java @@ -15,23 +15,26 @@ */ package com.king.zxing; +import android.Manifest; import android.os.Bundle; -import android.view.MotionEvent; import android.view.View; import com.google.zxing.Result; +import com.king.zxing.util.LogUtils; +import com.king.zxing.util.PermissionUtils; import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.view.PreviewView; -import androidx.core.app.ActivityCompat; /** * @author Jenly */ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback{ + private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86; protected PreviewView previewView; protected ViewfinderView viewfinderView; @@ -62,32 +65,50 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS if(ivFlashlightId != 0){ ivFlashlight = findViewById(ivFlashlightId); if(ivFlashlight != null){ - ivFlashlight.setOnClickListener(v -> toggleTorch()); + ivFlashlight.setOnClickListener(v -> toggleTorchState()); } } initCameraScan(); startCamera(); } + /** + * 初始化CameraScan + */ public void initCameraScan(){ mCameraScan = new DefaultCameraScan(this,previewView); mCameraScan.setOnScanResultCallback(this); } + /** + * 启动相机预览 + */ public void startCamera(){ if(mCameraScan != null){ - mCameraScan.startCamera(); + if(PermissionUtils.checkPermission(this,Manifest.permission.CAMERA)){ + mCameraScan.startCamera(); + }else{ + LogUtils.d("checkPermissionResult != PERMISSION_GRANTED"); + PermissionUtils.requestPermission(this,Manifest.permission.CAMERA,CAMERA_PERMISSION_REQUEST_CODE); + } } } + + /** + * 释放相机 + */ private void releaseCamera(){ if(mCameraScan != null){ mCameraScan.release(); } } - protected void toggleTorch(){ + /** + * 切换闪光灯状态(开启/关闭) + */ + protected void toggleTorchState(){ if(mCameraScan != null){ boolean isTorch = mCameraScan.isTorchEnabled(); mCameraScan.enableTorch(!isTorch); @@ -97,6 +118,27 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if(requestCode == CAMERA_PERMISSION_REQUEST_CODE){ + requestCameraPermissionResult(permissions,grantResults); + } + } + + /** + * 请求Camera权限回调结果 + * @param permissions + * @param grantResults + */ + public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults){ + if(PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA,permissions,grantResults)){ + startCamera(); + }else{ + finish(); + } + } + @Override protected void onDestroy() { releaseCamera(); diff --git a/lib/src/main/java/com/king/zxing/CaptureFragment.java b/lib/src/main/java/com/king/zxing/CaptureFragment.java index bf5ce96..09098fa 100644 --- a/lib/src/main/java/com/king/zxing/CaptureFragment.java +++ b/lib/src/main/java/com/king/zxing/CaptureFragment.java @@ -15,12 +15,15 @@ */ package com.king.zxing; +import android.Manifest; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.google.zxing.Result; +import com.king.zxing.util.LogUtils; +import com.king.zxing.util.PermissionUtils; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; @@ -32,6 +35,8 @@ import androidx.fragment.app.Fragment; */ public class CaptureFragment extends Fragment implements CameraScan.OnScanResultCallback { + private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86; + private View mRootView; protected PreviewView previewView; @@ -73,31 +78,48 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult if(ivFlashlightId != 0){ ivFlashlight = mRootView.findViewById(ivFlashlightId); if(ivFlashlight != null){ - ivFlashlight.setOnClickListener(v -> toggleTorch()); + ivFlashlight.setOnClickListener(v -> toggleTorchState()); } } initCameraScan(); startCamera(); } + /** + * 初始化CameraScan + */ public void initCameraScan(){ mCameraScan = new DefaultCameraScan(this,previewView); mCameraScan.setOnScanResultCallback(this); } + /** + * 启动相机预览 + */ public void startCamera(){ if(mCameraScan != null){ - mCameraScan.startCamera(); + if(PermissionUtils.checkPermission(getContext(), Manifest.permission.CAMERA)){ + mCameraScan.startCamera(); + }else{ + LogUtils.d("checkPermissionResult != PERMISSION_GRANTED"); + PermissionUtils.requestPermission(this,Manifest.permission.CAMERA,CAMERA_PERMISSION_REQUEST_CODE); + } } } + /** + * 释放相机 + */ private void releaseCamera(){ if(mCameraScan != null){ mCameraScan.release(); } } - protected void toggleTorch(){ + /** + * 切换闪光灯状态(开启/关闭) + */ + protected void toggleTorchState(){ if(mCameraScan != null){ boolean isTorch = mCameraScan.isTorchEnabled(); mCameraScan.enableTorch(!isTorch); @@ -107,6 +129,27 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if(requestCode == CAMERA_PERMISSION_REQUEST_CODE){ + requestCameraPermissionResult(permissions,grantResults); + } + } + + /** + * 请求Camera权限回调结果 + * @param permissions + * @param grantResults + */ + public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults){ + if(PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA,permissions,grantResults)){ + startCamera(); + }else{ + getActivity().finish(); + } + } + @Override public void onDestroy() { releaseCamera(); diff --git a/lib/src/main/java/com/king/zxing/DecodeConfig.java b/lib/src/main/java/com/king/zxing/DecodeConfig.java index 02c638b..9f5094e 100644 --- a/lib/src/main/java/com/king/zxing/DecodeConfig.java +++ b/lib/src/main/java/com/king/zxing/DecodeConfig.java @@ -2,6 +2,7 @@ package com.king.zxing; import android.graphics.Rect; +import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import com.google.zxing.common.GlobalHistogramBinarizer; import com.google.zxing.common.HybridBinarizer; @@ -12,6 +13,30 @@ import androidx.annotation.FloatRange; /** + * 解码配置:主要用于在扫码识别时,提供一些配置,便于扩展。通过配置可决定内置分析器的能力,从而间接的控制并简化扫码识别的流程 + *

+ * 设置解码 {@link #setHints(Map)}内置的一些解码可参见如下: + * @see {@link DecodeFormatManager#DEFAULT_HINTS} + * @see {@link DecodeFormatManager#ALL_HINTS} + * @see {@link DecodeFormatManager#CODE_128_HINTS} + * @see {@link DecodeFormatManager#QR_CODE_HINTS} + * @see {@link DecodeFormatManager#ONE_DIMENSIONAL_HINTS} + * @see {@link DecodeFormatManager#TWO_DIMENSIONAL_HINTS} + * @see {@link DecodeFormatManager#DEFAULT_HINTS} + * + * 如果不满足您也可以通过{@link DecodeFormatManager#createDecodeHints(BarcodeFormat...)}自己配置支持的格式 + * + *

+ * 识别区域可设置的方式有如下几种: + * {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高 + * {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 + * {@link #setAreaRectRatio(float)} 设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 + * + * 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的,所以这里的区域并不是用户所能预览到的区域,而是指Camera预览的真实区域, + * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 + * + * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} + *

* * @author Jenly */ @@ -75,6 +100,18 @@ public class DecodeConfig { /** * 设置解码 * @param hints {@link DecodeFormatManager} + * + * 内置的一些解码可参见如下: + * @see {@link DecodeFormatManager#DEFAULT_HINTS} + * @see {@link DecodeFormatManager#ALL_HINTS} + * @see {@link DecodeFormatManager#CODE_128_HINTS} + * @see {@link DecodeFormatManager#QR_CODE_HINTS} + * @see {@link DecodeFormatManager#ONE_DIMENSIONAL_HINTS} + * @see {@link DecodeFormatManager#TWO_DIMENSIONAL_HINTS} + * @see {@link DecodeFormatManager#DEFAULT_HINTS} + * + * 如果不满足您也可以通过{@link DecodeFormatManager#createDecodeHints(BarcodeFormat...)}自己配置支持的格式 + * * @return */ public DecodeConfig setHints(Map hints) { @@ -184,8 +221,19 @@ public class DecodeConfig { } /** - * 设置需要分析识别区域,当设置了指定的分析区域时,识别区域比例和识别区域相关参数都将无效 + * 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 * @param analyzeAreaRect + * + * 识别区域可设置的方式有如下几种: + * {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高 + * {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 + * {@link #setAreaRectRatio(float)} 设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 + * + * 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的,所以这里的区域并不是用户所能预览到的区域,而是指Camera预览的真实区域, + * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 + * + * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} + * * @return */ public DecodeConfig setAnalyzeAreaRect(Rect analyzeAreaRect) { @@ -202,8 +250,18 @@ public class DecodeConfig { } /** - * 设置是否支持全区域扫码识别,优先级比识别区域比例高 + * 设置是否支持全区域扫码识别,优先级比识别区域高 * @param fullAreaScan 默认为{@code true} + * + * 识别区域可设置的方式有如下几种: + * {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高 + * {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 + * {@link #setAreaRectRatio(float)} 设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 + * + * 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的,所以这里的区域并不是用户所能预览到的区域,而是指Camera预览的真实区域, + * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 + * + * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} * @return */ public DecodeConfig setFullAreaScan(boolean fullAreaScan) { @@ -220,8 +278,19 @@ public class DecodeConfig { } /** - * 设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 + * 设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 * @param areaRectRatio + * + * 识别区域可设置的方式有如下几种: + * {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高 + * {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 + * {@link #setAreaRectRatio(float)} 设置识别区域比例,默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 + * + * 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的,所以这里的区域并不是用户所能预览到的区域,而是指Camera预览的真实区域, + * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 + * + * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} + * * @return */ public DecodeConfig setAreaRectRatio(@FloatRange(from = 0.5,to = 1.0) float areaRectRatio) { diff --git a/lib/src/main/java/com/king/zxing/DefaultCameraScan.java b/lib/src/main/java/com/king/zxing/DefaultCameraScan.java index cab3584..9f68151 100644 --- a/lib/src/main/java/com/king/zxing/DefaultCameraScan.java +++ b/lib/src/main/java/com/king/zxing/DefaultCameraScan.java @@ -4,7 +4,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.util.DisplayMetrics; -import android.view.MotionEvent; +import android.view.ScaleGestureDetector; import android.view.View; import com.google.common.util.concurrent.ListenableFuture; @@ -24,6 +24,7 @@ import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageAnalysis; import androidx.camera.core.Preview; import androidx.camera.core.TorchState; +import androidx.camera.core.ZoomState; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.view.PreviewView; import androidx.core.content.ContextCompat; @@ -87,18 +88,38 @@ public class DefaultCameraScan extends CameraScan { initData(); } + private ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener(){ + @Override + public boolean onScale(ScaleGestureDetector detector) { + float scale = detector.getScaleFactor(); + if(mCamera != null){ + float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio(); + zoomTo(ratio * scale); + } + return true; + } + + }; + private void initData(){ mResultLiveData = new MutableLiveData<>(); mResultLiveData.observe(mLifecycleOwner, result -> { handleAnalyzeResult(result); }); + + ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener); mPreviewView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { LogUtils.d("click"); } }); - mPreviewView.setOnTouchListener((v, event) -> onTouchEvent(event)); + mPreviewView.setOnTouchListener((v, event) -> { + if(isNeedTouchZoom()){ + return scaleGestureDetector.onTouchEvent(event); + } + return false; + }); DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); mScreenWidth = displayMetrics.widthPixels; @@ -109,14 +130,16 @@ public class DefaultCameraScan extends CameraScan { mAmbientLightManager.register(); mAmbientLightManager.setOnLightSensorEventListener((dark, lightLux) -> { if(flashlightView != null){ - flashlightView.setSelected(!dark); if(dark){ if(flashlightView.getVisibility() != View.VISIBLE){ flashlightView.setVisibility(View.VISIBLE); + flashlightView.setSelected(isTorchEnabled()); } - }else if(flashlightView.getVisibility() == View.VISIBLE){ + }else if(flashlightView.getVisibility() == View.VISIBLE && !isTorchEnabled()){ flashlightView.setVisibility(View.INVISIBLE); + flashlightView.setSelected(false); } + } }); } @@ -131,7 +154,6 @@ public class DefaultCameraScan extends CameraScan { } } - @Override public CameraScan setCameraConfig(CameraConfig cameraConfig) { if(cameraConfig != null){ @@ -288,10 +310,15 @@ public class DefaultCameraScan extends CameraScan { } } + @Override public void zoomTo(float ratio) { if(mCamera != null){ - mCamera.getCameraControl().setZoomRatio(ratio); + ZoomState zoomState = mCamera.getCameraInfo().getZoomState().getValue(); + float maxRatio = zoomState.getMaxZoomRatio(); + float minRatio = zoomState.getMinZoomRatio(); + float zoom = Math.max(Math.min(ratio,maxRatio),minRatio); + mCamera.getCameraControl().setZoomRatio(zoom); } } @@ -323,11 +350,10 @@ public class DefaultCameraScan extends CameraScan { } @Override - public CameraScan enableTorch(boolean torch) { + public void enableTorch(boolean torch) { if(mCamera != null && hasFlashUnit()){ mCamera.getCameraControl().enableTorch(torch); } - return this; } @Override @@ -401,4 +427,18 @@ public class DefaultCameraScan extends CameraScan { return this; } + public CameraScan setDarkLightLux(float lightLux){ + if(mAmbientLightManager != null){ + mAmbientLightManager.setDarkLightLux(lightLux); + } + return this; + } + + public CameraScan setBrightLightLux(float lightLux){ + if(mAmbientLightManager != null){ + mAmbientLightManager.setBrightLightLux(lightLux); + } + return this; + } + } diff --git a/lib/src/main/java/com/king/zxing/ICamera.java b/lib/src/main/java/com/king/zxing/ICamera.java new file mode 100644 index 0000000..df4ddf1 --- /dev/null +++ b/lib/src/main/java/com/king/zxing/ICamera.java @@ -0,0 +1,33 @@ +package com.king.zxing; + + +import androidx.annotation.Nullable; +import androidx.camera.core.Camera; + +/** + * @author Jenly + */ +public interface ICamera { + + /** + * 启动相机预览 + */ + void startCamera(); + + /** + * 停止相机预览 + */ + void stopCamera(); + + /** + * 获取{@link Camera} + * @return + */ + @Nullable Camera getCamera(); + + /** + * 释放 + */ + void release(); + +} diff --git a/lib/src/main/java/com/king/zxing/ICameraScan.java b/lib/src/main/java/com/king/zxing/ICameraControl.java similarity index 61% rename from lib/src/main/java/com/king/zxing/ICameraScan.java rename to lib/src/main/java/com/king/zxing/ICameraControl.java index 9433233..5bdbf96 100644 --- a/lib/src/main/java/com/king/zxing/ICameraScan.java +++ b/lib/src/main/java/com/king/zxing/ICameraControl.java @@ -1,29 +1,11 @@ package com.king.zxing; - -import android.view.MotionEvent; - -import com.google.zxing.Result; - import androidx.annotation.FloatRange; -import androidx.annotation.Nullable; -import androidx.camera.core.Camera; /** * @author Jenly */ -public interface ICameraScan { - - /** - * 启动相机预览 - */ - void startCamera(); - - /** - * 停止相机预览 - */ - void stopCamera(); - +public interface ICameraControl { /** * 放大 @@ -42,12 +24,12 @@ public interface ICameraScan { void zoomTo(float ratio); /** - * 放大 + * 线性放大 */ void lineZoomIn(); /** - * 缩小 + * 线性缩小 */ void lineZoomOut(); @@ -57,13 +39,21 @@ public interface ICameraScan { */ void lineZoomTo(@FloatRange(from = 0.0,to = 1.0) float linearZoom); + /** + * 设置闪光灯(手电筒)是否开启 + * @param torch + */ + void enableTorch(boolean torch); /** - * 获取{@link Camera} + * 闪光灯(手电筒)是否开启 * @return */ - @Nullable Camera getCamera(); - - void release(); + boolean isTorchEnabled(); + /** + * 是否支持闪光灯 + * @return + */ + boolean hasFlashUnit(); } diff --git a/lib/src/main/java/com/king/zxing/ViewfinderView.java b/lib/src/main/java/com/king/zxing/ViewfinderView.java index 4006a3b..c875ee0 100644 --- a/lib/src/main/java/com/king/zxing/ViewfinderView.java +++ b/lib/src/main/java/com/king/zxing/ViewfinderView.java @@ -42,6 +42,8 @@ import androidx.annotation.ColorRes; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; +import static com.king.zxing.ViewfinderView.FrameGravity.*; + /** * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial * transparency outside it, as well as the laser scanner animation and result points. @@ -79,10 +81,10 @@ public class ViewfinderView extends View { * 扫码框四角颜色 */ private int cornerColor; - /** - * 结果点颜色 - */ - private int resultPointColor; +// /** +// * 结果点颜色 +// */ +// private int resultPointColor; /** * 提示文本与扫码框的边距 @@ -118,14 +120,6 @@ public class ViewfinderView extends View { */ private boolean isShowResultPoint; - /** - * 屏幕宽 - */ - private int screenWidth; - /** - * 屏幕高 - */ - private int screenHeight; /** * 扫码框宽 */ @@ -185,13 +179,17 @@ public class ViewfinderView extends View { */ private float frameRatio; + /** + * 扫码框内间距 + */ private float framePaddingLeft; private float framePaddingTop; private float framePaddingRight; private float framePaddingBottom; - - private List possibleResultPoints; - private List lastPossibleResultPoints; + /** + * 扫码框对齐方式 + */ + private FrameGravity frameGravity; public enum LaserStyle{ NONE(0),LINE(1),GRID(2); @@ -201,13 +199,11 @@ public class ViewfinderView extends View { } private static LaserStyle getFromInt(int value){ - for(LaserStyle style : LaserStyle.values()){ if(style.mValue == value){ return style; } } - return LaserStyle.LINE; } } @@ -222,17 +218,33 @@ public class ViewfinderView extends View { } private static TextLocation getFromInt(int value){ - for(TextLocation location : TextLocation.values()){ if(location.mValue == value){ return location; } } - return TextLocation.TOP; } + } + public enum FrameGravity { + CENTER(0), LEFT(1), TOP(2), RIGHT(3), BOTTOM(4); + + private int mValue; + + FrameGravity(int value) { + mValue = value; + } + + private static FrameGravity getFromInt(int value) { + for (FrameGravity gravity : values()) { + if (gravity.mValue == value) { + return gravity; + } + } + return CENTER; + } } public ViewfinderView(Context context) { @@ -256,7 +268,7 @@ public class ViewfinderView extends View { frameColor = array.getColor(R.styleable.ViewfinderView_frameColor, ContextCompat.getColor(context,R.color.viewfinder_frame)); cornerColor = array.getColor(R.styleable.ViewfinderView_cornerColor, ContextCompat.getColor(context,R.color.viewfinder_corner)); laserColor = array.getColor(R.styleable.ViewfinderView_laserColor, ContextCompat.getColor(context,R.color.viewfinder_laser)); - resultPointColor = array.getColor(R.styleable.ViewfinderView_resultPointColor, ContextCompat.getColor(context,R.color.viewfinder_result_point_color)); +// resultPointColor = array.getColor(R.styleable.ViewfinderView_resultPointColor, ContextCompat.getColor(context,R.color.viewfinder_result_point_color)); labelText = array.getString(R.styleable.ViewfinderView_labelText); labelTextColor = array.getColor(R.styleable.ViewfinderView_labelTextColor, ContextCompat.getColor(context,R.color.viewfinder_text_color)); @@ -264,7 +276,7 @@ public class ViewfinderView extends View { labelTextPadding = array.getDimension(R.styleable.ViewfinderView_labelTextPadding,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,24,getResources().getDisplayMetrics())); labelTextLocation = TextLocation.getFromInt(array.getInt(R.styleable.ViewfinderView_labelTextLocation,0)); - isShowResultPoint = array.getBoolean(R.styleable.ViewfinderView_showResultPoint,false); +// isShowResultPoint = array.getBoolean(R.styleable.ViewfinderView_showResultPoint,false); frameWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameWidth,0); frameHeight = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameHeight,0); @@ -284,27 +296,12 @@ public class ViewfinderView extends View { framePaddingTop = array.getDimension(R.styleable.ViewfinderView_framePaddingTop,0); framePaddingRight = array.getDimension(R.styleable.ViewfinderView_framePaddingRight,0); framePaddingBottom = array.getDimension(R.styleable.ViewfinderView_framePaddingBottom,0); + frameGravity = FrameGravity.getFromInt(array.getInt(R.styleable.ViewfinderView_frameGravity, CENTER.mValue)); array.recycle(); paint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - possibleResultPoints = new ArrayList<>(5); - lastPossibleResultPoints = null; - - screenWidth = getDisplayMetrics().widthPixels; - screenHeight = getDisplayMetrics().heightPixels; - - int size = (int)(Math.min(screenWidth,screenHeight) * frameRatio); - - if(frameWidth<=0 || frameWidth > screenWidth){ - frameWidth = size; - } - - if(frameHeight<=0 || frameHeight > screenHeight){ - frameHeight = size; - } - } private DisplayMetrics getDisplayMetrics(){ @@ -327,13 +324,43 @@ public class ViewfinderView extends View { this.labelTextSize = textSize; } + @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - //扫码框默认居中,支持利用内距偏移扫码框 - int leftOffset = (int)((screenWidth - frameWidth) / 2 + framePaddingLeft - framePaddingRight); - int topOffset = (int)((screenHeight - frameHeight) / 2 + framePaddingTop - framePaddingBottom); - frame = new Rect(leftOffset, topOffset, leftOffset + frameWidth, topOffset + frameHeight); + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + initFrame(w,h); + } + + private void initFrame(int width,int height){ + + int size = (int)(Math.min(width,height) * frameRatio); + + if(frameWidth <= 0 || frameWidth > width){ + frameWidth = size; + } + + if(frameHeight <= 0 || frameHeight > height){ + frameHeight = size; + } + + float leftOffsets = (width - frameWidth) / 2 + framePaddingLeft - framePaddingRight; + float topOffsets = (height - frameHeight) / 2 + framePaddingTop - framePaddingBottom; + switch (frameGravity){ + case LEFT: + leftOffsets = framePaddingLeft; + break; + case TOP: + topOffsets = framePaddingTop; + break; + case RIGHT: + leftOffsets = width - frameWidth + framePaddingRight; + break; + case BOTTOM: + topOffsets = height - frameHeight + framePaddingBottom; + break; + } + + frame = new Rect((int)leftOffsets, (int)topOffsets, (int)leftOffsets + frameWidth, (int)topOffsets + frameHeight); } @Override @@ -361,8 +388,6 @@ public class ViewfinderView extends View { drawCorner(canvas, frame); //绘制提示信息 drawTextInfo(canvas, frame); - //绘制扫码结果点 - drawResultPoint(canvas,frame); // Request another update at the animation interval, but only repaint the laser line, // not the entire viewfinder mask. postInvalidateDelayed(scannerAnimationDelay, @@ -385,11 +410,10 @@ public class ViewfinderView extends View { StaticLayout staticLayout = new StaticLayout(labelText,textPaint,canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL,1.0f,0.0f,true); if(labelTextLocation == TextLocation.BOTTOM){ canvas.translate(frame.left + frame.width() / 2,frame.bottom + labelTextPadding); - staticLayout.draw(canvas); }else{ canvas.translate(frame.left + frame.width() / 2,frame.top - labelTextPadding - staticLayout.getHeight()); - staticLayout.draw(canvas); } + staticLayout.draw(canvas); } } @@ -529,53 +553,16 @@ public class ViewfinderView extends View { * @param height */ private void drawExterior(Canvas canvas, Rect frame, int width, int height) { - paint.setColor(maskColor); - canvas.drawRect(0, 0, width, frame.top, paint); - canvas.drawRect(0, frame.top, frame.left, frame.bottom, paint); - canvas.drawRect(frame.right, frame.top, width, frame.bottom, paint); - canvas.drawRect(0, frame.bottom, width, height, paint); - } - - /** - * 绘制扫码结果点 - * @param canvas - * @param frame - */ - private void drawResultPoint(Canvas canvas,Rect frame){ - - if(!isShowResultPoint){ - return; - } - - List currentPossible = possibleResultPoints; - List currentLast = lastPossibleResultPoints; - - if (currentPossible.isEmpty()) { - lastPossibleResultPoints = null; - } else { - possibleResultPoints = new ArrayList<>(5); - lastPossibleResultPoints = currentPossible; - paint.setAlpha(CURRENT_POINT_OPACITY); - paint.setColor(resultPointColor); - synchronized (currentPossible) { - float radius = POINT_SIZE / 2.0f; - for (ResultPoint point : currentPossible) { - canvas.drawCircle( point.getX(),point.getY(), radius, paint); - } - } - } - if (currentLast != null) { - paint.setAlpha(CURRENT_POINT_OPACITY / 2); - paint.setColor(resultPointColor); - synchronized (currentLast) { - float radius = POINT_SIZE / 2.0f; - for (ResultPoint point : currentLast) { - canvas.drawCircle( point.getX(),point.getY(), radius, paint); - } - } + if(maskColor != 0){ + paint.setColor(maskColor); + canvas.drawRect(0, 0, width, frame.top, paint); + canvas.drawRect(0, frame.top, frame.left, frame.bottom, paint); + canvas.drawRect(frame.right, frame.top, width, frame.bottom, paint); + canvas.drawRect(0, frame.bottom, width, height, paint); } } + public void drawViewfinder() { invalidate(); } @@ -597,21 +584,5 @@ public class ViewfinderView extends View { } - public void addPossibleResultPoint(ResultPoint point) { - if(isShowResultPoint){ - List points = possibleResultPoints; - synchronized (points) { - points.add(point); - int size = points.size(); - if (size > MAX_RESULT_POINTS) { - // trim it - points.subList(0, size - MAX_RESULT_POINTS / 2).clear(); - } - } - } - - } - - } \ No newline at end of file diff --git a/lib/src/main/java/com/king/zxing/analyze/ImageAnalyzer.java b/lib/src/main/java/com/king/zxing/analyze/ImageAnalyzer.java index 936804b..76ff570 100644 --- a/lib/src/main/java/com/king/zxing/analyze/ImageAnalyzer.java +++ b/lib/src/main/java/com/king/zxing/analyze/ImageAnalyzer.java @@ -4,6 +4,7 @@ import android.annotation.SuppressLint; import android.graphics.ImageFormat; import com.google.zxing.Result; +import com.king.zxing.util.LogUtils; import java.nio.ByteBuffer; @@ -35,6 +36,7 @@ public abstract class ImageAnalyzer implements Analyzer { buffer.get(data); return analyze(data,image.getWidth(),image.getHeight()); } + LogUtils.w("imageFormat: " + image.getFormat()); return null; } diff --git a/lib/src/main/java/com/king/zxing/util/PermissionUtils.java b/lib/src/main/java/com/king/zxing/util/PermissionUtils.java new file mode 100644 index 0000000..cb880dd --- /dev/null +++ b/lib/src/main/java/com/king/zxing/util/PermissionUtils.java @@ -0,0 +1,111 @@ +package com.king.zxing.util; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.Fragment; + +/** + * @author Jenly + */ +public class PermissionUtils { + + private PermissionUtils(){ + throw new AssertionError(); + } + + /** + * 检测是否授权 + * @param context + * @param permission + * @return 返回{@code true} 表示已授权,{@code false}表示未授权 + */ + public static boolean checkPermission(@NonNull Context context, @NonNull String permission){ + return ActivityCompat.checkSelfPermission(context,permission) == PackageManager.PERMISSION_GRANTED; + } + + /** + * 请求权限 + * @param activity + * @param permission + * @param requestCode + */ + public static void requestPermission(@NonNull Activity activity,@NonNull String permission, @IntRange(from = 0) int requestCode){ + requestPermissions(activity,new String[]{permission},requestCode); + } + + /** + * 请求权限 + * @param fragment + * @param permission + * @param requestCode + */ + public static void requestPermission(@NonNull Fragment fragment, @NonNull String permission, @IntRange(from = 0) int requestCode){ + requestPermissions(fragment,new String[]{permission},requestCode); + } + + /** + * 请求权限 + * @param activity + * @param permissions + * @param requestCode + */ + public static void requestPermissions(@NonNull Activity activity,@NonNull String[] permissions, @IntRange(from = 0) int requestCode){ + ActivityCompat.requestPermissions(activity,permissions,requestCode); + } + + /** + * 请求权限 + * @param fragment + * @param permissions + * @param requestCode + */ + public static void requestPermissions(@NonNull Fragment fragment,@NonNull String[] permissions, @IntRange(from = 0) int requestCode){ + fragment.requestPermissions(permissions,requestCode); + } + + /** + * 请求权限结果 + * @param requestPermission 请求的权限 + * @param permissions + * @param grantResults + * @return 返回{@code true} 表示已授权,{@code false}表示未授权 + */ + public static boolean requestPermissionsResult(@NonNull String requestPermission, @NonNull String[] permissions, @NonNull int[] grantResults){ + int length = permissions.length; + for(int i = 0; i < length; i++){ + if(requestPermission.equals(permissions[i])){ + if(grantResults[i] == PackageManager.PERMISSION_GRANTED){ + return true; + } + } + } + return false; + } + + /** + * 请求权限结果 + * @param requestPermissions 请求的权限 + * @param permissions + * @param grantResults + * @return 返回{@code true} 表示全部已授权,{@code false}表示未全部授权 + */ + public static boolean requestPermissionsResult(@NonNull String[] requestPermissions, @NonNull String[] permissions, @NonNull int[] grantResults){ + int length = permissions.length; + for(int i = 0; i < length; i++){ + for(int j = 0; j < requestPermissions.length; j++){ + if(requestPermissions[j].equals(permissions[i])){ + if(grantResults[i] != PackageManager.PERMISSION_GRANTED){ + return false; + } + } + } + } + return true; + } + +} diff --git a/lib/src/main/res/drawable-xxhdpi/zxl_torch_off.png b/lib/src/main/res/drawable-xxhdpi/zxl_flashlight_off.png similarity index 100% rename from lib/src/main/res/drawable-xxhdpi/zxl_torch_off.png rename to lib/src/main/res/drawable-xxhdpi/zxl_flashlight_off.png diff --git a/lib/src/main/res/drawable-xxhdpi/zxl_torch_on.png b/lib/src/main/res/drawable-xxhdpi/zxl_flashlight_on.png similarity index 100% rename from lib/src/main/res/drawable-xxhdpi/zxl_torch_on.png rename to lib/src/main/res/drawable-xxhdpi/zxl_flashlight_on.png diff --git a/lib/src/main/res/drawable/zxl_torch_selector.xml b/lib/src/main/res/drawable/zxl_flashlight_selector.xml similarity index 68% rename from lib/src/main/res/drawable/zxl_torch_selector.xml rename to lib/src/main/res/drawable/zxl_flashlight_selector.xml index b360cc4..8a1e4a7 100644 --- a/lib/src/main/res/drawable/zxl_torch_selector.xml +++ b/lib/src/main/res/drawable/zxl_flashlight_selector.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/lib/src/main/res/layout/zxl_capture.xml b/lib/src/main/res/layout/zxl_capture.xml index bf347e3..e8b9b67 100644 --- a/lib/src/main/res/layout/zxl_capture.xml +++ b/lib/src/main/res/layout/zxl_capture.xml @@ -1,16 +1,4 @@ - @@ -28,6 +16,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:src="@drawable/zxl_torch_selector" - android:layout_marginTop="@dimen/torchMarginTop" /> + android:src="@drawable/zxl_flashlight_selector" + android:layout_marginTop="@dimen/zxl_flashlight_margin_top" /> \ No newline at end of file diff --git a/lib/src/main/res/values/attrs.xml b/lib/src/main/res/values/attrs.xml index 8b89a1c..7238699 100644 --- a/lib/src/main/res/values/attrs.xml +++ b/lib/src/main/res/values/attrs.xml @@ -4,7 +4,6 @@ - @@ -13,7 +12,6 @@ - @@ -34,6 +32,13 @@ + + + + + + + diff --git a/lib/src/main/res/values/colors.xml b/lib/src/main/res/values/colors.xml index 384529a..ed3984c 100644 --- a/lib/src/main/res/values/colors.xml +++ b/lib/src/main/res/values/colors.xml @@ -5,10 +5,9 @@ #7F1FB3E2 #FF1FB3E2 #FF1FB3E2 - #C0FFBD21 #FFC0C0C0 - #00000000 - #00000000 + #00000000 + #00000000 \ No newline at end of file diff --git a/lib/src/main/res/values/dimens.xml b/lib/src/main/res/values/dimens.xml index efcd3d5..edc5b93 100644 --- a/lib/src/main/res/values/dimens.xml +++ b/lib/src/main/res/values/dimens.xml @@ -1,4 +1,4 @@ - 80dp + 80dp \ No newline at end of file diff --git a/lib/src/main/res/values/ids.xml b/lib/src/main/res/values/ids.xml deleted file mode 100644 index 0a2d9e0..0000000 --- a/lib/src/main/res/values/ids.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/lib/src/main/res/values/strings.xml b/lib/src/main/res/values/strings.xml deleted file mode 100644 index 0a33168..0000000 --- a/lib/src/main/res/values/strings.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/lib/src/main/res/values/styles.xml b/lib/src/main/res/values/styles.xml index e04b29e..24bdf76 100644 --- a/lib/src/main/res/values/styles.xml +++ b/lib/src/main/res/values/styles.xml @@ -8,8 +8,8 @@ @android:color/black true true - @color/capture_status_bar_color - @color/capture_navigation_bar_color + @color/zxl_capture_status_bar_color + @color/zxl_capture_navigation_bar_color \ No newline at end of file diff --git a/versions.gradle b/versions.gradle index fa49ccd..df9f18c 100644 --- a/versions.gradle +++ b/versions.gradle @@ -1,7 +1,7 @@ //App def app_version = [:] app_version.versionCode = 27 -app_version.versionName = "2.0.0-beta1" +app_version.versionName = "2.0.0" ext.app_version = app_version //build version