更新CameraX至v1.2.1

This commit is contained in:
Jenly
2023-03-04 20:54:37 +08:00
parent b2fa6aaec0
commit a85d793149
53 changed files with 1566 additions and 884 deletions

226
README.md
View File

@@ -24,35 +24,44 @@ ZXingLite for Android 是ZXing的精简极速版基于ZXing库优化扫码和
## ViewfinderView属性说明 ## ViewfinderView属性说明
| 属性 | 值类型 | 默认值 | 说明 |
| :------| :------ | :------ | :------ | | 属性 | 属性类型 | 默认值 | 属性说明 |
| maskColor | color |<font color=#000000>#60000000</font>| 扫描区外遮罩的颜色 | | :------|:----------| :------ | :------ |
| frameColor | color |<font color=#1FB3E2>#7F1FB3E2</font>| 扫描区边框的颜色 | | maskColor | color |<font color=#000000>#60000000</font>| 扫描区外遮罩的颜色 |
| cornerColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区边的颜色 | | frameColor | color |<font color=#1FB3E2>#7F1FB3E2</font>| 扫描区边的颜色 |
| laserColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区激光线的颜色 | | cornerColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区边角的颜色 |
| labelText | string | | 扫描提示文本信息 | | laserColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区激光线的颜色 |
| labelTextColor | color |<font color=#C0C0C0>#FFC0C0C0</font>| 提示文本字体颜色 | | labelText | string | | 扫描提示文本信息 |
| labelTextColor | color |<font color=#C0C0C0>#FFC0C0C0</font>| 提示文本字体颜色 |
| labelTextSize | dimension |14sp| 提示文本字体大小 | | labelTextSize | dimension |14sp| 提示文本字体大小 |
| labelTextPadding | dimension |24dp| 提示文本距离扫描区的间距 | | labelTextPadding | dimension |24dp| 提示文本距离扫描区的间距 |
| labelTextWidth | dimension | | 提示文本的宽度默认为View的宽度 | | labelTextWidth | dimension | | 提示文本的宽度默认为View的宽度 |
| labelTextLocation | enum |bottom| 提示文本显示位置 | | labelTextLocation | enum |bottom| 提示文本显示位置 |
| frameWidth | dimension | | 扫码框宽度 | | frameWidth | dimension | | 扫码框宽度 |
| frameHeight | dimension | | 扫码框高度 | | frameHeight | dimension | | 扫码框高度 |
| laserStyle | enum | line | 扫描激光的样式 | | laserStyle | enum | line | 扫描激光的样式 |
| gridColumn | integer | 20 | 网格扫描激光列数 | | gridColumn | integer | 20 | 网格扫描激光列数 |
| gridHeight | integer | 40dp | 网格扫描激光高度为0dp时表示动态铺满 | | gridHeight | integer | 40dp | 网格扫描激光高度为0dp时表示动态铺满 |
| cornerRectWidth | dimension | 4dp | 扫描区边角的宽 | | cornerRectWidth | dimension | 4dp | 扫描区边角的宽 |
| cornerRectHeight | dimension | 16dp | 扫描区边角的高 | | cornerRectHeight | dimension | 16dp | 扫描区边角的高 |
| scannerLineMoveDistance | dimension | 2dp | 扫描线每次移动距离 | | scannerLineMoveDistance | dimension | 2dp | 扫描线每次移动距离 |
| scannerLineHeight | dimension | 5dp | 扫描线高度 | | scannerLineHeight | dimension | 5dp | 扫描线高度 |
| frameLineWidth | dimension | 1dp | 边框线宽度 | | frameLineWidth | dimension | 1dp | 边框线宽度 |
| scannerAnimationDelay | integer | 20 | 扫描动画延迟间隔时间,单位:毫秒 | | scannerAnimationDelay | integer | 20 | 扫描动画延迟间隔时间,单位:毫秒 |
| frameRatio | float | 0.625f | 扫码框与屏幕占比 | | frameRatio | float | 0.625f | 扫码框与屏幕占比 |
| framePaddingLeft | dimension | 0 | 扫码框左边的内间距 | | framePaddingLeft | dimension | 0 | 扫码框左边的内间距 |
| framePaddingTop | dimension | 0 | 扫码框上边的内间距 | | framePaddingTop | dimension | 0 | 扫码框上边的内间距 |
| framePaddingRight | dimension | 0 | 扫码框右边的内间距 | | framePaddingRight | dimension | 0 | 扫码框右边的内间距 |
| framePaddingBottom | dimension | 0 | 扫码框下边的内间距 | | framePaddingBottom | dimension | 0 | 扫码框下边的内间距 |
| frameGravity | enum | center | 扫码框对齐方式 | | frameGravity | enum | center | 扫码框对齐方式 |
| pointColor | color | <font color=#1FB3E2>#FF1FB3E2</font> | 结果点的颜色 |
| pointStrokeColor | color | <font color=#FFFFFF>#FFFFFFFF</font> | 结果点描边的颜色 |
| pointRadius | dimension | 15dp | 结果点的半径 |
| pointStrokeRatio | float | 1.2 | 结果点描边半径与结果点半径的比例 |
| pointDrawable | reference | | 结果点自定义图片 |
| showPointAnim | boolean | true | 是否显示结果点的动画 |
| laserDrawable | reference | | 扫描激光自定义图片 |
| viewfinderStyle | enum | classic | 取景框样式支持classic经典样式带扫码框那种、popular流行样式不带扫码框 |
## 引入 ## 引入
@@ -73,8 +82,8 @@ allprojects {
2. 在Module的 **build.gradle** 里面添加引入依赖项 2. 在Module的 **build.gradle** 里面添加引入依赖项
```gradle ```gradle
//AndroidX 版本 // AndroidX 版本
implementation 'com.github.jenly1314:zxing-lite:2.3.0' implementation 'com.github.jenly1314:zxing-lite:2.3.1'
``` ```
@@ -89,81 +98,75 @@ implementation 'com.github.jenly1314:zxing-lite:2.3.0'
**v1.x** 旧版本 [v1.1.9](https://github.com/jenly1314/ZXingLite/tree/androidx) **v1.x** 旧版本 [v1.1.9](https://github.com/jenly1314/ZXingLite/tree/androidx)
```gradle ```gradle
//AndroidX 版本 // AndroidX 版本
implementation 'com.king.zxing:zxing-lite:1.1.9-androidx' implementation 'com.king.zxing:zxing-lite:1.1.9-androidx'
//Android Support 版本 // Android Support 版本
implementation 'com.king.zxing:zxing-lite:1.1.9' implementation 'com.king.zxing:zxing-lite:1.1.9'
``` ```
## 使用说明
### 快速实现扫码有以下几种方式: ### 快速实现扫码识别有以下几种方式:
> 1、直接使用CaptureActivity或者CaptureFragment。(纯洁的扫码,无任何添加剂) > 1、直接使用CaptureActivity或者CaptureFragment。(默认的扫码实现)
> 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。适用于大多场景并无需关心扫码相关逻辑自定义布局时需覆写getLayoutId方法实现示例[CustomCaptureActivity](app/src/main/java/com/king/zxing/app/CustomCaptureActivity.java) 和 [QRCodeActivity](app/src/main/java/com/king/zxing/app/QRCodeActivity.java) > 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。适用于大多场景并无需关心扫码相关逻辑自定义布局时需覆写getLayoutId方法实现示例[CustomCaptureActivity](app/src/main/java/com/king/zxing/app/CustomCaptureActivity.java) 和 [QRCodeActivity](app/src/main/java/com/king/zxing/app/QRCodeActivity.java)
> 3、在你项目的Activity或者Fragment中实例化一个CameraScan即可。适用于想在扫码界面写交互逻辑又因为项目架构或其它原因无法直接或间接继承CaptureActivity或CaptureFragment时使用实现示例[CustomActivity](app/src/main/java/com/king/zxing/app/CustomActivity.java) > 3、在你项目的Activity或者Fragment中实例化一个CameraScan即可。适用于想在扫码界面写交互逻辑又因为项目架构或其它原因无法直接或间接继承CaptureActivity或CaptureFragment时使用实现示例[CustomFullScanActivity](app/src/main/java/com/king/zxing/app/CustomFullScanActivity.java)
> 4、继承CameraScan自己实现一个可参照默认实现类DefaultCameraScan其它步骤同方式3。扩展高级用法谨慎使用 > 4、继承CameraScan自己实现一个可参照默认实现类DefaultCameraScan其它步骤同方式3。扩展高级用法谨慎使用
### 关于 CameraScan
## 示例 **CameraScan** 作为相机扫描的(核心)基类;所有与相机扫描相关的都是基于此类来直接或间接进行控制的。
布局示例 ### 关于 CameraConfig
> 可自定义布局覆写getLayoutId方法布局内至少要保证有PreviewView。
> PreviewView 用来预览布局内至少要保证有PreviewView如果是继承CaptureActivity或CaptureFragment控件id可覆写getPreviewViewId方法自定义 主要是相机相关的配置;如:摄像头的前置后置、相机预览相关、图像分析相关等配置。
> ViewfinderView 用来渲染扫码视图给用户起到一个视觉效果本身扫码识别本身没有关系如果是继承CaptureActivity或CaptureFragment控件id可复写getViewfinderViewId方法自定义默认为previewView返回0表示无需ViewfinderView > 你可以直接库中内置实现的相机配置: **CameraConfig** 、**AspectRatioCameraConfig** 和 **ResolutionCameraConfig**。
> ivFlashlight 用来内置手电筒如果是继承CaptureActivity或CaptureFragment控件id可复写getFlashlightId方法自定义默认为ivFlashlight。返回0表示无需内置手电筒。您也可以自己去定义 #### 这里简单说下各自的特点:
* **CameraConfig**:默认的相机配置。
* **AspectRatioCameraConfig**:根据纵横比配置相机,使输出分析的图像尽可能的接近屏幕的比例
* **ResolutionCameraConfig**:根据尺寸配置相机的目标图像大小,使输出分析的图像的分辨率尽可能的接近屏幕尺寸
```Xml > 你也可以自定义或覆写 **CameraConfig** 中的 **options** 方法,根据需要定制配置。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView
android:id="@+id/viewfinderView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/ivFlashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/zxl_flashlight_selector"
android:layout_marginTop="@dimen/zxl_flashlight_margin_top" />
</FrameLayout>
```
或在你的布局中添加 这里特别温馨提示默认配置在未配置相机的目标分析图像大小时会优先使用横屏640 * 480 竖屏480 * 640
```Xml 根据这个图像质量顺便说下默认配置的优缺点:
<include layout="@layout/zxl_capture"/>
```
代码示例 (二维码/条形码) * 优点因为图像质量不高所以在低配置的设备上使用也能hold住这样就能尽可能的适应各种设备
```Java * 缺点:正是由于图像质量不高,从而可能会对检测识别率略有影响,比如在某些机型上体验欠佳。
//跳转的默认扫码界面 * 结论:在适配、性能与体验之间得有所取舍,找到平衡点。
startActivityForResult(new Intent(context,CaptureActivity.class),requestCode);
//生成二维码 > 当使用默认的 **CameraConfig** 在某些机型上体验欠佳时,你可以尝试使用 **AspectRatioCameraConfig** 或
CodeUtils.createQRCode(content,600,logo); **ResolutionCameraConfig** 会有意想不到奇效。
//生成条形码
CodeUtils.createBarCode(content, BarcodeFormat.CODE_128,800,200); ### 关于 **Analyzer**
//解析条形码/二维码
CodeUtils.parseCode(bitmap); **Analyzer** 为定义的分析器接口;主要用于分析相机预览的帧数据;通过实现 **Analyzer** 可以自定义分析过程。
//解析二维码
CodeUtils.parseQRCode(bitmap); ### 关于 **CaptureActivity** 和 **CaptureFragment**
```
**CaptureActivity****CaptureFragment** 作为扫描预览界面的基类,主要目的是便于快速实现扫码识别。
> 扫描预览界面内部持有 **CameraScan**,并处理了 **CameraScan** 的初始化(如:相机权限、相机预览、生命周期等细节)
## 使用示例
### CameraScan配置示例
**CameraScan** 里面包含部分支持链式调用的方法,即调用返回是 **CameraScan** 本身的一些配置建议在调用 **startCamera()** 方法之前调用。
> 如果是通过继承 **CaptureActivity** 或者 **CaptureFragment** 或其子类实现的相机扫描,可以在
**initCameraScan()** 方法中获取 **CameraScan** ,然后根据需要修改相关配置。
示例1
CameraScan配置示例
```java ```java
//获取CameraScan扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。 //获取CameraScan扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。
getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false
@@ -176,16 +179,17 @@ CameraScan配置示例
.bindFlashlightView(ivFlashlight)//绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒按钮 .bindFlashlightView(ivFlashlight)//绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒按钮
.setOnScanResultCallback(this)//设置扫码结果回调,需要自己处理或者需要连扫时,可设置回调,自己去处理相关逻辑 .setOnScanResultCallback(this)//设置扫码结果回调,需要自己处理或者需要连扫时,可设置回调,自己去处理相关逻辑
.setAnalyzer(new MultiFormatAnalyzer(new DecodeConfig()))//设置分析器,DecodeConfig可以配置一些解码时的配置信息如果内置的不满足您的需求你也可以自定义实现 .setAnalyzer(new MultiFormatAnalyzer(new DecodeConfig()))//设置分析器,DecodeConfig可以配置一些解码时的配置信息如果内置的不满足您的需求你也可以自定义实现
.setAnalyzeImage(true)//设置是否分析图片默认为true。如果设置为false相当于关闭了扫码识别功能 .setAnalyzeImage(true);//设置是否分析图片默认为true。如果设置为false相当于关闭了扫码识别功能
.startCamera();//启动预览如果是通过继承CaptureActivity或CaptureFragment实现扫码无需调用这句startCamera
// 启动预览如果是通过继承CaptureActivity或CaptureFragment实现的则无需调用startCamera
getCameraScan().startCamera();
//设置闪光灯(手电筒)是否开启,需在startCamera之后调用才有效 // 设置闪光灯(手电筒)是否开启,需在startCamera之后调用才有效
getCameraScan().enableTorch(torch); getCameraScan().enableTorch(torch);
``` ```
CameraScan配置示例(只需识别二维码的配置示例) 示例2(只需识别二维码的配置示例)
```java ```java
//初始化解码配置 //初始化解码配置
DecodeConfig decodeConfig = new DecodeConfig(); DecodeConfig decodeConfig = new DecodeConfig();
@@ -201,7 +205,65 @@ CameraScan配置示例只需识别二维码的配置示例
.setAnalyzer(new MultiFormatAnalyzer(decodeConfig));//设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现 .setAnalyzer(new MultiFormatAnalyzer(decodeConfig));//设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现
``` ```
通过继承CaptureActivity实现扫二维码完整示例 ### 布局示例
**PreviewView** 用来预览,布局内至少要保证有 **PreviewView**,如果是继承 **CaptureActivity****CaptureFragment**控件id可覆写`getPreviewViewId`方法自定义
**ViewfinderView** 用来渲染扫码视图,给用户起到一个视觉效果,本身扫码识别本身没有关系,如果是继承 **CaptureActivity****CaptureFragment**控件ID可复写`getViewfinderViewId`方法自定义,默认为 **previewView**返回0表示无需 **ViewfinderView**
**ivFlashlight** 是布局内置的手电筒,如果是继承 **CaptureActivity****CaptureFragment**控件id可复写`getFlashlightId`方法自定义,默认为 **ivFlashlight**。返回0表示无需内置手电筒。您也可以自己去定义
> 可自定义布局(覆写`getLayoutId方法`),布局内至少要保证有 **PreviewView**。
```Xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.king.zxing.ViewfinderView
android:id="@+id/viewfinderView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/ivFlashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/zxl_flashlight_margin_top"
android:contentDescription="@null"
android:src="@drawable/zxl_flashlight_selector" />
</FrameLayout>
```
或在你的布局中添加
```Xml
<include layout="@layout/zxl_capture"/>
```
### 代码示例
**工具类CodeUtils的使用示例二维码/条形码)**
```Java
// 生成二维码
CodeUtils.createQRCode(content,600,logo);
// 生成条形码
CodeUtils.createBarCode(content, BarcodeFormat.CODE_128,800,200);
// 解析条形码/二维码
CodeUtils.parseCode(bitmap);
// 解析二维码
CodeUtils.parseQRCode(bitmap);
```
**通过继承CaptureActivity实现扫二维码完整示例**
```java ```java
public class QRCodeActivity extends CaptureActivity { public class QRCodeActivity extends CaptureActivity {
@@ -259,16 +321,19 @@ public class QRCodeActivity extends CaptureActivity {
``` ```
如果直接使用CaptureActivity需在您项目的AndroidManifest中添加如下配置
### 其他
#### AndroidManifest
如果你直接使用了默认 **CaptureActivity** 则需在你项目的AndroidManifest中注册 **CaptureActivity**,配置如下
```Xml ```Xml
<activity <activity
android:name="com.king.zxing.CaptureActivity" android:name="com.king.zxing.CaptureActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/CaptureTheme"/> android:theme="@style/CaptureTheme"/>
``` ```
#### JDK版本
### 其他
需使用JDK8+编译在你项目中的build.gradle的android{}中添加配置: 需使用JDK8+编译在你项目中的build.gradle的android{}中添加配置:
@@ -290,7 +355,7 @@ compileOptions {
## 版本说明 ## 版本说明
### v2.x 基于CameraX重构震撼发布 ### v2.x 基于CameraX进行了重构
#### v2.x 相对于 v1.x 的优势 #### v2.x 相对于 v1.x 的优势
@@ -304,7 +369,7 @@ compileOptions {
#### v1.x 说明 #### v1.x 说明
[【v1.1.9】](https://github.com/jenly1314/ZXingLite/tree/androidx) 如果您正在使用 **1.x** 版本请点击下面的链接查看分支版本,当前 **2.x** 版本已经基于 **CameraX** 进行重构,不支持升级,请在新项目中使用 [【v1.1.9】](https://github.com/jenly1314/ZXingLite/tree/androidx) 如果您正在使用 **1.x** 版本请点击下面的链接查看分支版本,当前 **2.x** 版本已经基于 **CameraX** 进行重构,API变化较大谨慎升级
查看AndroidX版 **1.x** 分支 [请戳此处](https://github.com/jenly1314/ZXingLite/tree/androidx) 查看AndroidX版 **1.x** 分支 [请戳此处](https://github.com/jenly1314/ZXingLite/tree/androidx)
@@ -314,6 +379,11 @@ compileOptions {
## 版本记录 ## 版本记录
#### v2.3.12023-3-4
* 更新CameraX至v1.2.1
* 更新Gradle至v7.5
* 优化细节
#### v2.3.02022-12-11 #### v2.3.02022-12-11
* 更新CameraX至v1.2.0 * 更新CameraX至v1.2.0
* 更新zxing至v3.5.1 * 更新zxing至v3.5.1

View File

@@ -26,8 +26,8 @@ android {
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
// kotlinOptions { // kotlinOptions {

Binary file not shown.

View File

@@ -11,8 +11,8 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 35, "versionCode": 36,
"versionName": "2.3.0", "versionName": "2.3.1",
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
], ],

View File

@@ -44,7 +44,7 @@
android:theme="@style/CaptureTheme"/> android:theme="@style/CaptureTheme"/>
<activity <activity
android:name=".CustomActivity" android:name=".CustomFullScanActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/CaptureTheme"/> android:theme="@style/CaptureTheme"/>

View File

@@ -22,7 +22,6 @@ import android.widget.Toast;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.king.zxing.CameraScan; import com.king.zxing.CameraScan;
import com.king.zxing.config.CameraConfig;
import com.king.zxing.CaptureActivity; import com.king.zxing.CaptureActivity;
import com.king.zxing.DecodeConfig; import com.king.zxing.DecodeConfig;
import com.king.zxing.DecodeFormatManager; import com.king.zxing.DecodeFormatManager;
@@ -121,8 +120,8 @@ public class CustomCaptureActivity extends CaptureActivity {
if(toast == null){ if(toast == null){
toast = Toast.makeText(this,text,Toast.LENGTH_SHORT); toast = Toast.makeText(this,text,Toast.LENGTH_SHORT);
}else{ }else{
toast.setDuration(Toast.LENGTH_SHORT);
toast.setText(text); toast.setText(text);
toast.setDuration(Toast.LENGTH_SHORT);
} }
toast.show(); toast.show();
} }

View File

@@ -8,9 +8,13 @@ import android.widget.Toast;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.king.zxing.CameraScan; import com.king.zxing.CameraScan;
import com.king.zxing.DecodeConfig;
import com.king.zxing.DecodeFormatManager;
import com.king.zxing.DefaultCameraScan; import com.king.zxing.DefaultCameraScan;
import com.king.zxing.ViewfinderView; import com.king.zxing.ViewfinderView;
import com.king.zxing.analyze.MultiFormatAnalyzer;
import com.king.zxing.app.util.StatusBarUtils; import com.king.zxing.app.util.StatusBarUtils;
import com.king.zxing.config.ResolutionCameraConfig;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
@@ -22,7 +26,7 @@ import androidx.fragment.app.Fragment;
* 自定义扫码切记自定义扫码需在{@link Activity}或者{@link Fragment}相对应的生命周期里面调用{@link #mCameraScan}对应的生命周期 * 自定义扫码切记自定义扫码需在{@link Activity}或者{@link Fragment}相对应的生命周期里面调用{@link #mCameraScan}对应的生命周期
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class CustomActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback { public class CustomFullScanActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback {
private boolean isContinuousScan; private boolean isContinuousScan;
@@ -59,9 +63,16 @@ public class CustomActivity extends AppCompatActivity implements CameraScan.OnSc
isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false); isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false);
//初始化解码配置
DecodeConfig decodeConfig = new DecodeConfig();
decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS)//如果只有识别二维码的需求这样设置效率会更高不设置默认为DecodeFormatManager.DEFAULT_HINTS
.setFullAreaScan(true);//设置是否全区域识别默认false
mCameraScan = new DefaultCameraScan(this,previewView); mCameraScan = new DefaultCameraScan(this,previewView);
mCameraScan.setOnScanResultCallback(this) mCameraScan.setOnScanResultCallback(this)
.setAnalyzer(new MultiFormatAnalyzer(decodeConfig))
.setVibrate(true) .setVibrate(true)
.setCameraConfig(new ResolutionCameraConfig(this, ResolutionCameraConfig.IMAGE_QUALITY_720P))
.startCamera(); .startCamera();
} }

View File

@@ -31,7 +31,6 @@ import com.king.zxing.util.CodeUtils;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.io.IOException;
import java.util.List; import java.util.List;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
@@ -43,9 +42,9 @@ import pub.devrel.easypermissions.EasyPermissions;
/** /**
* 扫码Demo示例说明 * 扫码Demo示例说明
* *
* 快速实现扫码有以下几种方式: * 快速实现扫码识别有以下几种方式:
* *
* 1、直接使用CaptureActivity或者CaptureFragment。(纯洁的扫码,无任何添加剂) * 1、直接使用CaptureActivity或者CaptureFragment。(默认的扫码实现)
* *
* 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。适用于大多场景并无需关心扫码相关逻辑自定义布局时需覆写getLayoutId方法 * 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。适用于大多场景并无需关心扫码相关逻辑自定义布局时需覆写getLayoutId方法
* *
@@ -108,13 +107,6 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
} }
private void parsePhoto(Intent data){ private void parsePhoto(Intent data){
// final String path = UriUtils.getImagePath(this,data);
// LogUtils.d("path:" + path);
// if(TextUtils.isEmpty(path)){
// return;
// }
try { try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),data.getData()); Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),data.getData());
//异步解析 //异步解析
@@ -243,7 +235,7 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
checkCameraPermissions(); checkCameraPermissions();
break; break;
case R.id.btn4: case R.id.btn4:
this.cls = CustomActivity.class; this.cls = CustomFullScanActivity.class;
this.title = ((Button)v).getText().toString(); this.title = ((Button)v).getText().toString();
checkCameraPermissions(); checkCameraPermissions();
break; break;

View File

@@ -11,6 +11,7 @@ import com.king.zxing.DecodeConfig;
import com.king.zxing.DecodeFormatManager; import com.king.zxing.DecodeFormatManager;
import com.king.zxing.analyze.MultiFormatAnalyzer; import com.king.zxing.analyze.MultiFormatAnalyzer;
import com.king.zxing.app.util.StatusBarUtils; import com.king.zxing.app.util.StatusBarUtils;
import com.king.zxing.config.AspectRatioCameraConfig;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
@@ -50,8 +51,8 @@ public class QRCodeActivity extends CaptureActivity {
//在启动预览之前,设置分析器,只识别二维码 //在启动预览之前,设置分析器,只识别二维码
getCameraScan() getCameraScan()
.setCameraConfig(new AspectRatioCameraConfig(this))//设置相机配置,使用 AspectRatioCameraConfig
.setVibrate(true)//设置是否震动默认为false .setVibrate(true)//设置是否震动默认为false
.setNeedAutoZoom(true)//二维码太小时可自动缩放默认为false
.setAnalyzer(new MultiFormatAnalyzer(decodeConfig));//设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现 .setAnalyzer(new MultiFormatAnalyzer(decodeConfig));//设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/flash_on"/>
<item android:state_selected="false" android:drawable="@drawable/flash_off"/>
</selector>

View File

@@ -87,7 +87,7 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="自定义码" android:text="自定义全屏扫二维码"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn3" app:layout_constraintTop_toBottomOf="@+id/btn3"

View File

@@ -13,24 +13,16 @@
android:id="@+id/viewfinderView" android:id="@+id/viewfinderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:labelTextWidth="180dp" app:viewfinderStyle="popular"/>
app:labelText="@string/tips_scan_code"
app:labelTextSize="@dimen/size_14sp"
app:laserColor="@color/colorAccent"
app:frameColor="@color/colorPrimary"
app:cornerColor="@color/colorPrimary"
app:labelTextLocation="bottom"
app:laserStyle="grid"
app:gridHeight="0dp"/>
<ImageView <ImageView
android:id="@+id/ivFlash" android:id="@+id/ivFlash"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/flash_selected_selector" android:src="@drawable/zxl_flashlight_selector"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="160dp" /> android:layout_marginTop="170dp" />
<include layout="@layout/toolbar_capture"/> <include layout="@layout/toolbar_capture"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -24,11 +24,11 @@
android:id="@+id/ivFlashlight" android:id="@+id/ivFlashlight"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/flash_selected_selector" android:src="@drawable/zxl_flashlight_selector"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="160dp" /> android:layout_marginTop="170dp" />
<include layout="@layout/toolbar_capture"/> <include layout="@layout/toolbar_capture"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -24,11 +24,11 @@
android:id="@+id/ivFlashlight" android:id="@+id/ivFlashlight"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/flash_selected_selector" android:src="@drawable/zxl_flashlight_selector"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="160dp" /> android:layout_marginTop="170dp" />
<include layout="@layout/toolbar_capture"/> <include layout="@layout/toolbar_capture"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="status_bar_height">25dp</dimen>
</resources>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<dimen name="status_bar_height">0dp</dimen> <dimen name="status_bar_height">25dp</dimen>
<dimen name="title_height">50dp</dimen> <dimen name="title_height">50dp</dimen>

View File

@@ -6,7 +6,6 @@ buildscript {
repositories { repositories {
google() google()
maven { url 'https://maven.aliyun.com/repository/public/' }
mavenCentral() mavenCentral()
} }
@@ -24,9 +23,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
maven { url 'https://maven.aliyun.com/repository/public/' }
mavenCentral() mavenCentral()
// jcenter() // Warning: this repository is going to shut down soon
} }
} }

View File

@@ -1,5 +1,10 @@
## 版本记录 ## 版本记录
#### v2.3.12023-3-4
* 更新CameraX至v1.2.1
* 更新Gradle至v7.5
* 优化细节
#### v2.3.02022-12-11 #### v2.3.02022-12-11
* 更新CameraX至v1.2.0 * 更新CameraX至v1.2.0
* 更新zxing至v3.5.1 * 更新zxing至v3.5.1
@@ -20,6 +25,7 @@
#### v2.1.02021-6-30 (从v2.1.0开始不再发布至JCenter) #### v2.1.02021-6-30 (从v2.1.0开始不再发布至JCenter)
* 更新CameraX至v1.0.0 * 更新CameraX至v1.0.0
* 优化细节 * 优化细节
* 发布至MavenCentral
#### v2.0.32021-3-26 #### v2.0.32021-3-26
* 更新CameraX至v1.0.0-rc03 * 更新CameraX至v1.0.0-rc03

View File

@@ -14,8 +14,8 @@ org.gradle.jvmargs = -Xmx1536m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
VERSION_NAME=2.3.0 VERSION_NAME=2.3.1
VERSION_CODE=35 VERSION_CODE=36
GROUP=com.github.jenly1314 GROUP=com.github.jenly1314
POM_DESCRIPTION=ZXingLite for Android POM_DESCRIPTION=ZXingLite for Android
@@ -43,3 +43,5 @@ RELEASE_SIGNING_ENABLED=false

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip

View File

@@ -1,7 +1,7 @@
//App //App
def app_version = [:] def app_version = [:]
app_version.versionCode = 35 app_version.versionCode = 36
app_version.versionName = "2.3.0" app_version.versionName = "2.3.1"
ext.app_version = app_version ext.app_version = app_version
//build version //build version
@@ -29,14 +29,14 @@ versions.espresso = "3.2.0"
versions.bintray_release = "0.9.2" versions.bintray_release = "0.9.2"
versions.mavenPublish = '0.18.0' versions.mavenPublish = '0.18.0'
versions.gralde = "7.1.1" versions.gralde = "7.4.1"
versions.kotlin = "1.6.0" versions.kotlin = "1.6.0"
versions.coreKtx = "1.6.0" versions.coreKtx = "1.6.0"
//zxing //zxing
versions.zxing = "3.5.1" versions.zxing = "3.5.1"
versions.camerax = "1.2.0" versions.camerax = "1.2.1"
versions.easypermissions = "3.0.0" versions.easypermissions = "3.0.0"

View File

@@ -27,8 +27,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
} }

View File

@@ -16,18 +16,36 @@ import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraSelector;
/** /**
* 相机扫描基类定义;内置的默认实现见:{@link DefaultCameraScan}
* <p>
* 快速实现扫描识别主要有以下几种方式:
* <p>
* 1、通过继承 {@link CaptureActivity}或者{@link CaptureFragment}或其子类,可快速实现扫描识别。
* 适用于大多数场景自定义布局时需覆写getLayoutId方法
* <p>
* 2、在你项目的Activity或者Fragment中实例化一个{@link DefaultCameraScan}。(适用于想在扫码界面写交互逻辑,又因为项目
* 架构或其它原因,无法直接或间接继承{@link CaptureActivity}或{@link CaptureFragment}时使用)
* <p>
* 3、继承{@link CameraScan}自己实现一个,可参照默认实现类{@link DefaultCameraScan}其他步骤同方式2。高级用法谨慎使用
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public abstract class CameraScan implements ICamera,ICameraControl { public abstract class CameraScan implements ICamera, ICameraControl {
/**
* 扫描返回结果的key解析方式可参见{@link #parseScanResult(Intent)}
*/
public static String SCAN_RESULT = "SCAN_RESULT"; public static String SCAN_RESULT = "SCAN_RESULT";
/** A camera on the device facing the same direction as the device's screen. */ /**
* A camera on the device facing the same direction as the device's screen.
*/
public static int LENS_FACING_FRONT = CameraSelector.LENS_FACING_FRONT; public static int LENS_FACING_FRONT = CameraSelector.LENS_FACING_FRONT;
/** A camera on the device facing the opposite direction as the device's screen. */ /**
* A camera on the device facing the opposite direction as the device's screen.
*/
public static int LENS_FACING_BACK = CameraSelector.LENS_FACING_BACK; public static int LENS_FACING_BACK = CameraSelector.LENS_FACING_BACK;
/** /**
* 是否需要支持自动缩放 * 是否需要支持自动缩放
*/ */
@@ -40,15 +58,16 @@ public abstract class CameraScan implements ICamera,ICameraControl {
/** /**
* 是否需要支持触摸缩放 * 是否需要支持触摸缩放
*
* @return * @return
*/ */
protected boolean isNeedTouchZoom() { protected boolean isNeedTouchZoom() {
return isNeedTouchZoom; return isNeedTouchZoom;
} }
/** /**
* 设置是否需要支持触摸缩放 * 设置是否需要支持触摸缩放
*
* @param needTouchZoom * @param needTouchZoom
* @return * @return
*/ */
@@ -59,6 +78,7 @@ public abstract class CameraScan implements ICamera,ICameraControl {
/** /**
* 是否需要支持自动缩放 * 是否需要支持自动缩放
*
* @return * @return
*/ */
protected boolean isNeedAutoZoom() { protected boolean isNeedAutoZoom() {
@@ -67,6 +87,7 @@ public abstract class CameraScan implements ICamera,ICameraControl {
/** /**
* 设置是否需要支持自动缩放 * 设置是否需要支持自动缩放
*
* @param needAutoZoom * @param needAutoZoom
* @return * @return
*/ */
@@ -77,19 +98,21 @@ public abstract class CameraScan implements ICamera,ICameraControl {
/** /**
* 设置相机配置,请在{@link #startCamera()}之前调用 * 设置相机配置,请在{@link #startCamera()}之前调用
*
* @param cameraConfig * @param cameraConfig
*/ */
public abstract CameraScan setCameraConfig(CameraConfig cameraConfig); public abstract CameraScan setCameraConfig(CameraConfig cameraConfig);
/** /**
* 设置是否分析图像,通过此方法可以动态控制是否分析图像,常用于中断扫码识别。如:连扫时,扫到结果,然后停止分析图像 * 设置是否分析图像,通过此方法可以动态控制是否分析图像,常用于中断扫码识别。如:连扫时,扫到结果,然后停止分析图像
* * <p>
* 1. 因为分析图像默认为true如果想支持连扫在{@link OnScanResultCallback#onScanResultCallback(Result)}返回true拦截即可。 * 1. 因为分析图像默认为true如果想支持连扫在{@link OnScanResultCallback#onScanResultCallback(Result)}返回true拦截即可。
* 当连扫的处理逻辑比较复杂时请在处理逻辑前通过调用setAnalyzeImage(false)来停止分析图像, * 当连扫的处理逻辑比较复杂时请在处理逻辑前通过调用setAnalyzeImage(false)来停止分析图像,
* 等逻辑处理完后再调用getCameraScan().setAnalyzeImage(true)来继续分析图像。 * 等逻辑处理完后再调用getCameraScan().setAnalyzeImage(true)来继续分析图像。
* * <p>
* 2. 如果只是想拦截扫码结果回调自己处理逻辑,但并不想继续分析图像(即不想连扫),可通过 * 2. 如果只是想拦截扫码结果回调自己处理逻辑,但并不想继续分析图像(即不想连扫),可通过
* 调用getCameraScan().setAnalyzeImage(false)来停止分析图像。 * 调用getCameraScan().setAnalyzeImage(false)来停止分析图像。
*
* @param analyze * @param analyze
*/ */
public abstract CameraScan setAnalyzeImage(boolean analyze); public abstract CameraScan setAnalyzeImage(boolean analyze);
@@ -97,72 +120,77 @@ public abstract class CameraScan implements ICamera,ICameraControl {
/** /**
* 设置分析器,如果内置的一些分析器不满足您的需求,你也可以自定义{@link Analyzer} * 设置分析器,如果内置的一些分析器不满足您的需求,你也可以自定义{@link Analyzer}
* 自定义时,切记需在{@link #startCamera()}之前调用才有效。 * 自定义时,切记需在{@link #startCamera()}之前调用才有效。
* * <p>
* 内置了一些{@link Analyzer}的实现类如下: * 内置了一些{@link Analyzer}的实现类如下:
*
* @param analyzer
* @see {@link MultiFormatAnalyzer} * @see {@link MultiFormatAnalyzer}
* @see {@link AreaRectAnalyzer} * @see {@link AreaRectAnalyzer}
* @see {@link ImageAnalyzer} * @see {@link ImageAnalyzer}
*
* @see {@link BarcodeFormatAnalyzer} * @see {@link BarcodeFormatAnalyzer}
* @see {@link QRCodeReader} * @see {@link QRCodeReader}
*
* @param analyzer
*/ */
public abstract CameraScan setAnalyzer(Analyzer analyzer); public abstract CameraScan setAnalyzer(Analyzer analyzer);
/** /**
* 设置是否震动 * 设置是否震动
*
* @param vibrate * @param vibrate
*/ */
public abstract CameraScan setVibrate(boolean vibrate); public abstract CameraScan setVibrate(boolean vibrate);
/** /**
* 设置是否播放提示音 * 设置是否播放提示音
*
* @param playBeep * @param playBeep
*/ */
public abstract CameraScan setPlayBeep(boolean playBeep); public abstract CameraScan setPlayBeep(boolean playBeep);
/** /**
* 设置扫码结果回调 * 设置扫码结果回调
*
* @param callback * @param callback
*/ */
public abstract CameraScan setOnScanResultCallback(OnScanResultCallback callback); public abstract CameraScan setOnScanResultCallback(OnScanResultCallback callback);
/** /**
* 绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒 * 绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒
*
* @param v * @param v
*/ */
public abstract CameraScan bindFlashlightView(@Nullable View v); public abstract CameraScan bindFlashlightView(@Nullable View v);
/** /**
* 设置光线足够暗的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 * 设置光线足够暗的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效
*
* @param lightLux * @param lightLux
*/ */
public abstract CameraScan setDarkLightLux(float lightLux); public abstract CameraScan setDarkLightLux(float lightLux);
/** /**
* 设置光线足够明亮的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 * 设置光线足够明亮的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效
*
* @param lightLux * @param lightLux
*/ */
public abstract CameraScan setBrightLightLux(float lightLux); public abstract CameraScan setBrightLightLux(float lightLux);
public interface OnScanResultCallback{ public interface OnScanResultCallback {
/** /**
* 扫码结果回调 * 扫码结果回调
*
* @param result * @param result
* @return 返回false表示不拦截将关闭扫码界面并将结果返回给调用界面 * @return 返回false表示不拦截将关闭扫码界面并将结果返回给调用界面
* 返回true表示拦截需自己处理逻辑。当isAnalyze为true时默认会继续分析图像也就是连扫 * 返回true表示拦截需自己处理逻辑。当isAnalyze为true时默认会继续分析图像也就是连扫
* 如果只是想拦截扫码结果回调,并不想继续分析图像(不想连扫),请在拦截扫码逻辑处通过调 * 如果只是想拦截扫码结果回调,并不想继续分析图像(不想连扫),请在拦截扫码逻辑处通过调
* 用{@link CameraScan#setAnalyzeImage(boolean)} * 用{@link CameraScan#setAnalyzeImage(boolean)}
* 因为{@link CameraScan#setAnalyzeImage(boolean)}方法能动态控制是否继续分析图像。 * 因为{@link CameraScan#setAnalyzeImage(boolean)}方法能动态控制是否继续分析图像。
*
*/ */
boolean onScanResultCallback(Result result); boolean onScanResultCallback(Result result);
/** /**
* 扫码结果识别失败时触发此回调方法 * 扫码结果识别失败时触发此回调方法
*/ */
default void onScanResultFailure(){ default void onScanResultFailure() {
} }
@@ -170,12 +198,13 @@ public abstract class CameraScan implements ICamera,ICameraControl {
/** /**
* 解析扫码结果 * 解析扫码结果
*
* @param data * @param data
* @return * @return
*/ */
@Nullable @Nullable
public static String parseScanResult(Intent data){ public static String parseScanResult(Intent data) {
if(data != null){ if (data != null) {
return data.getStringExtra(SCAN_RESULT); return data.getStringExtra(SCAN_RESULT);
} }
return null; return null;

View File

@@ -29,9 +29,21 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.view.PreviewView; import androidx.camera.view.PreviewView;
/** /**
* 相机扫描基类;{@link CaptureActivity} 内部持有{@link CameraScan},便于快速实现扫描识别。
* <p>
* 快速实现扫描识别主要有以下几种方式:
* <p>
* 1、通过继承 {@link CaptureActivity}或者{@link CaptureFragment}或其子类,可快速实现扫描识别。
* 适用于大多数场景自定义布局时需覆写getLayoutId方法
* <p>
* 2、在你项目的Activity或者Fragment中实例化一个{@link DefaultCameraScan}。(适用于想在扫码界面写交互逻辑,又因为项目
* 架构或其它原因,无法直接或间接继承{@link CaptureActivity}或{@link CaptureFragment}时使用)
* <p>
* 3、继承{@link CameraScan}自己实现一个,可参照默认实现类{@link DefaultCameraScan}其他步骤同方式2。高级用法谨慎使用
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class CaptureActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback{ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback {
private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86; private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86;
@@ -44,7 +56,7 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if(isContentView()){ if (isContentView()) {
setContentView(getLayoutId()); setContentView(getLayoutId());
} }
initUI(); initUI();
@@ -53,16 +65,16 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
/** /**
* 初始化 * 初始化
*/ */
public void initUI(){ public void initUI() {
previewView = findViewById(getPreviewViewId()); previewView = findViewById(getPreviewViewId());
int viewfinderViewId = getViewfinderViewId(); int viewfinderViewId = getViewfinderViewId();
if(viewfinderViewId != 0){ if (viewfinderViewId != 0) {
viewfinderView = findViewById(viewfinderViewId); viewfinderView = findViewById(viewfinderViewId);
} }
int ivFlashlightId = getFlashlightId(); int ivFlashlightId = getFlashlightId();
if(ivFlashlightId != 0){ if (ivFlashlightId != 0) {
ivFlashlight = findViewById(ivFlashlightId); ivFlashlight = findViewById(ivFlashlightId);
if(ivFlashlight != null){ if (ivFlashlight != null) {
ivFlashlight.setOnClickListener(v -> onClickFlashlight()); ivFlashlight.setOnClickListener(v -> onClickFlashlight());
} }
} }
@@ -73,39 +85,37 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
/** /**
* 点击手电筒 * 点击手电筒
*/ */
protected void onClickFlashlight(){ protected void onClickFlashlight() {
toggleTorchState(); toggleTorchState();
} }
/** /**
* 初始化CameraScan * 初始化CameraScan
*/ */
public void initCameraScan(){ public void initCameraScan() {
mCameraScan = new DefaultCameraScan(this,previewView); mCameraScan = new DefaultCameraScan(this, previewView);
mCameraScan.setOnScanResultCallback(this); mCameraScan.setOnScanResultCallback(this);
} }
/** /**
* 启动相机预览 * 启动相机预览
*/ */
public void startCamera(){ public void startCamera() {
if(mCameraScan != null){ if (mCameraScan != null) {
if(PermissionUtils.checkPermission(this,Manifest.permission.CAMERA)){ if (PermissionUtils.checkPermission(this, Manifest.permission.CAMERA)) {
mCameraScan.startCamera(); mCameraScan.startCamera();
}else{ } else {
LogUtils.d("checkPermissionResult != PERMISSION_GRANTED"); LogUtils.d("checkPermissionResult != PERMISSION_GRANTED");
PermissionUtils.requestPermission(this,Manifest.permission.CAMERA,CAMERA_PERMISSION_REQUEST_CODE); PermissionUtils.requestPermission(this, Manifest.permission.CAMERA, CAMERA_PERMISSION_REQUEST_CODE);
} }
} }
} }
/** /**
* 释放相机 * 释放相机
*/ */
private void releaseCamera(){ private void releaseCamera() {
if(mCameraScan != null){ if (mCameraScan != null) {
mCameraScan.release(); mCameraScan.release();
} }
} }
@@ -113,11 +123,11 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
/** /**
* 切换闪光灯状态(开启/关闭) * 切换闪光灯状态(开启/关闭)
*/ */
protected void toggleTorchState(){ protected void toggleTorchState() {
if(mCameraScan != null){ if (mCameraScan != null) {
boolean isTorch = mCameraScan.isTorchEnabled(); boolean isTorch = mCameraScan.isTorchEnabled();
mCameraScan.enableTorch(!isTorch); mCameraScan.enableTorch(!isTorch);
if(ivFlashlight != null){ if (ivFlashlight != null) {
ivFlashlight.setSelected(!isTorch); ivFlashlight.setSelected(!isTorch);
} }
} }
@@ -126,20 +136,21 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == CAMERA_PERMISSION_REQUEST_CODE){ if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
requestCameraPermissionResult(permissions,grantResults); requestCameraPermissionResult(permissions, grantResults);
} }
} }
/** /**
* 请求Camera权限回调结果 * 请求Camera权限回调结果
*
* @param permissions * @param permissions
* @param grantResults * @param grantResults
*/ */
public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults){ public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults) {
if(PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA,permissions,grantResults)){ if (PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA, permissions, grantResults)) {
startCamera(); startCamera();
}else{ } else {
finish(); finish();
} }
} }
@@ -152,55 +163,62 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
/** /**
* 返回true时会自动初始化{@link #setContentView(int)}返回为false是需自己去初始化{@link #setContentView(int)} * 返回true时会自动初始化{@link #setContentView(int)}返回为false是需自己去初始化{@link #setContentView(int)}
*
* @return 默认返回true * @return 默认返回true
*/ */
public boolean isContentView(){ public boolean isContentView() {
return true; return true;
} }
/** /**
* 布局id * 布局ID通过覆写此方法可以自定义布局
*
* @return * @return
*/ */
public int getLayoutId(){ public int getLayoutId() {
return R.layout.zxl_capture; return R.layout.zxl_capture;
} }
/** /**
* {@link #viewfinderView} 的 ID * {@link #viewfinderView} 的 ID
*
* @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0 * @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0
*/ */
public int getViewfinderViewId(){ public int getViewfinderViewId() {
return R.id.viewfinderView; return R.id.viewfinderView;
} }
/** /**
* 预览界面{@link #previewView} 的ID * 预览界面{@link #previewView} 的ID
*
* @return * @return
*/ */
public int getPreviewViewId(){ public int getPreviewViewId() {
return R.id.previewView; return R.id.previewView;
} }
/** /**
* 获取 {@link #ivFlashlight} 的ID * 获取 {@link #ivFlashlight} 的ID
* @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0 *
* @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0
*/ */
public int getFlashlightId(){ public int getFlashlightId() {
return R.id.ivFlashlight; return R.id.ivFlashlight;
} }
/** /**
* Get {@link CameraScan} * Get {@link CameraScan}
*
* @return {@link #mCameraScan} * @return {@link #mCameraScan}
*/ */
public CameraScan getCameraScan(){ public CameraScan getCameraScan() {
return mCameraScan; return mCameraScan;
} }
/** /**
* 接收扫码结果回调 * 接收扫码结果回调
*
* @param result 扫码结果 * @param result 扫码结果
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截 * @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截
*/ */

View File

@@ -30,6 +30,18 @@ import androidx.camera.view.PreviewView;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
/** /**
* 相机扫描基类;{@link CaptureFragment} 内部持有{@link CameraScan},便于快速实现扫描识别。
* <p>
* 快速实现扫描识别主要有以下几种方式:
* <p>
* 1、通过继承 {@link CaptureActivity}或者{@link CaptureFragment}或其子类,可快速实现扫描识别。
* 适用于大多数场景自定义布局时需覆写getLayoutId方法
* <p>
* 2、在你项目的Activity或者Fragment中实例化一个{@link DefaultCameraScan}。(适用于想在扫码界面写交互逻辑,又因为项目
* 架构或其它原因,无法直接或间接继承{@link CaptureActivity}或{@link CaptureFragment}时使用)
* <p>
* 3、继承{@link CameraScan}自己实现一个,可参照默认实现类{@link DefaultCameraScan}其他步骤同方式2。高级用法谨慎使用
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class CaptureFragment extends Fragment implements CameraScan.OnScanResultCallback { public class CaptureFragment extends Fragment implements CameraScan.OnScanResultCallback {
@@ -53,11 +65,10 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
return fragment; return fragment;
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(isContentView()){ if (isContentView()) {
mRootView = createRootView(inflater,container); mRootView = createRootView(inflater, container);
} }
initUI(); initUI();
return mRootView; return mRootView;
@@ -66,16 +77,16 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 初始化 * 初始化
*/ */
public void initUI(){ public void initUI() {
previewView = mRootView.findViewById(getPreviewViewId()); previewView = mRootView.findViewById(getPreviewViewId());
int viewfinderViewId = getViewfinderViewId(); int viewfinderViewId = getViewfinderViewId();
if(viewfinderViewId != 0){ if (viewfinderViewId != 0) {
viewfinderView = mRootView.findViewById(viewfinderViewId); viewfinderView = mRootView.findViewById(viewfinderViewId);
} }
int ivFlashlightId = getFlashlightId(); int ivFlashlightId = getFlashlightId();
if(ivFlashlightId != 0){ if (ivFlashlightId != 0) {
ivFlashlight = mRootView.findViewById(ivFlashlightId); ivFlashlight = mRootView.findViewById(ivFlashlightId);
if(ivFlashlight != null){ if (ivFlashlight != null) {
ivFlashlight.setOnClickListener(v -> onClickFlashlight()); ivFlashlight.setOnClickListener(v -> onClickFlashlight());
} }
} }
@@ -86,28 +97,28 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 点击手电筒 * 点击手电筒
*/ */
protected void onClickFlashlight(){ protected void onClickFlashlight() {
toggleTorchState(); toggleTorchState();
} }
/** /**
* 初始化CameraScan * 初始化CameraScan
*/ */
public void initCameraScan(){ public void initCameraScan() {
mCameraScan = new DefaultCameraScan(this,previewView); mCameraScan = new DefaultCameraScan(this, previewView);
mCameraScan.setOnScanResultCallback(this); mCameraScan.setOnScanResultCallback(this);
} }
/** /**
* 启动相机预览 * 启动相机预览
*/ */
public void startCamera(){ public void startCamera() {
if(mCameraScan != null){ if (mCameraScan != null) {
if(PermissionUtils.checkPermission(getContext(), Manifest.permission.CAMERA)){ if (PermissionUtils.checkPermission(getContext(), Manifest.permission.CAMERA)) {
mCameraScan.startCamera(); mCameraScan.startCamera();
}else{ } else {
LogUtils.d("checkPermissionResult != PERMISSION_GRANTED"); LogUtils.d("checkPermissionResult != PERMISSION_GRANTED");
PermissionUtils.requestPermission(this,Manifest.permission.CAMERA,CAMERA_PERMISSION_REQUEST_CODE); PermissionUtils.requestPermission(this, Manifest.permission.CAMERA, CAMERA_PERMISSION_REQUEST_CODE);
} }
} }
} }
@@ -115,8 +126,8 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 释放相机 * 释放相机
*/ */
private void releaseCamera(){ private void releaseCamera() {
if(mCameraScan != null){ if (mCameraScan != null) {
mCameraScan.release(); mCameraScan.release();
} }
} }
@@ -124,11 +135,11 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 切换闪光灯状态(开启/关闭) * 切换闪光灯状态(开启/关闭)
*/ */
protected void toggleTorchState(){ protected void toggleTorchState() {
if(mCameraScan != null){ if (mCameraScan != null) {
boolean isTorch = mCameraScan.isTorchEnabled(); boolean isTorch = mCameraScan.isTorchEnabled();
mCameraScan.enableTorch(!isTorch); mCameraScan.enableTorch(!isTorch);
if(ivFlashlight != null){ if (ivFlashlight != null) {
ivFlashlight.setSelected(!isTorch); ivFlashlight.setSelected(!isTorch);
} }
} }
@@ -137,20 +148,21 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == CAMERA_PERMISSION_REQUEST_CODE){ if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
requestCameraPermissionResult(permissions,grantResults); requestCameraPermissionResult(permissions, grantResults);
} }
} }
/** /**
* 请求Camera权限回调结果 * 请求Camera权限回调结果
*
* @param permissions * @param permissions
* @param grantResults * @param grantResults
*/ */
public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults){ public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults) {
if(PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA,permissions,grantResults)){ if (PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA, permissions, grantResults)) {
startCamera(); startCamera();
}else{ } else {
getActivity().finish(); getActivity().finish();
} }
} }
@@ -163,66 +175,73 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 返回true时会自动初始化{@link #createRootView(LayoutInflater, ViewGroup)}返回为false是需自己去初始化{@link #createRootView(LayoutInflater, ViewGroup)} * 返回true时会自动初始化{@link #createRootView(LayoutInflater, ViewGroup)}返回为false是需自己去初始化{@link #createRootView(LayoutInflater, ViewGroup)}
*
* @return 默认返回true * @return 默认返回true
*/ */
public boolean isContentView(){ public boolean isContentView() {
return true; return true;
} }
/** /**
* 创建{@link #mRootView} * 创建{@link #mRootView}
*
* @param inflater * @param inflater
* @param container * @param container
* @return * @return
*/ */
@NonNull @NonNull
public View createRootView(LayoutInflater inflater, ViewGroup container){ public View createRootView(LayoutInflater inflater, ViewGroup container) {
return inflater.inflate(getLayoutId(),container,false); return inflater.inflate(getLayoutId(), container, false);
} }
/** /**
* 布局id * 布局ID通过覆写此方法可以自定义布局
*
* @return * @return
*/ */
public int getLayoutId(){ public int getLayoutId() {
return R.layout.zxl_capture; return R.layout.zxl_capture;
} }
/** /**
* {@link #viewfinderView} 的 ID * {@link #viewfinderView} 的 ID
*
* @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0 * @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0
*/ */
public int getViewfinderViewId(){ public int getViewfinderViewId() {
return R.id.viewfinderView; return R.id.viewfinderView;
} }
/** /**
* 预览界面{@link #previewView} 的ID * 预览界面{@link #previewView} 的ID
*
* @return * @return
*/ */
public int getPreviewViewId(){ public int getPreviewViewId() {
return R.id.previewView; return R.id.previewView;
} }
/** /**
* 获取 {@link #ivFlashlight} 的ID * 获取 {@link #ivFlashlight} 的ID
* @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0 *
* @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0
*/ */
public int getFlashlightId(){ public int getFlashlightId() {
return R.id.ivFlashlight; return R.id.ivFlashlight;
} }
/** /**
* Get {@link CameraScan} * Get {@link CameraScan}
*
* @return {@link #mCameraScan} * @return {@link #mCameraScan}
*/ */
public CameraScan getCameraScan(){ public CameraScan getCameraScan() {
return mCameraScan; return mCameraScan;
} }
/** /**
* 接收扫码结果回调 * 接收扫码结果回调
*
* @param result 扫码结果 * @param result 扫码结果
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截 * @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截
*/ */
@@ -237,5 +256,4 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
return mRootView; return mRootView;
} }
} }

View File

@@ -12,11 +12,12 @@ import java.util.Map;
import androidx.annotation.FloatRange; import androidx.annotation.FloatRange;
/** /**
* 解码配置:主要用于在扫码识别时,提供一些配置,便于扩展。通过配置可决定内置分析器的能力,从而间接的控制并简化扫码识别的流程 * 解码配置:主要用于在扫码识别时,提供一些配置,便于扩展。通过配置可决定内置分析器的能力,从而间接的控制并简化扫码识别的流程
* <p></> * <p></>
* 设置解码 {@link #setHints(Map)}内置的一些解码可参见如下: * 设置解码 {@link #setHints(Map)}内置的一些解码可参见如下:
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
* @see {@link DecodeFormatManager#DEFAULT_HINTS} * @see {@link DecodeFormatManager#DEFAULT_HINTS}
* @see {@link DecodeFormatManager#ALL_HINTS} * @see {@link DecodeFormatManager#ALL_HINTS}
* @see {@link DecodeFormatManager#CODE_128_HINTS} * @see {@link DecodeFormatManager#CODE_128_HINTS}
@@ -24,7 +25,7 @@ import androidx.annotation.FloatRange;
* @see {@link DecodeFormatManager#ONE_DIMENSIONAL_HINTS} * @see {@link DecodeFormatManager#ONE_DIMENSIONAL_HINTS}
* @see {@link DecodeFormatManager#TWO_DIMENSIONAL_HINTS} * @see {@link DecodeFormatManager#TWO_DIMENSIONAL_HINTS}
* @see {@link DecodeFormatManager#DEFAULT_HINTS} * @see {@link DecodeFormatManager#DEFAULT_HINTS}
* * <p>
* 如果不满足您也可以通过{@link DecodeFormatManager#createDecodeHints(BarcodeFormat...)}自己配置支持的格式 * 如果不满足您也可以通过{@link DecodeFormatManager#createDecodeHints(BarcodeFormat...)}自己配置支持的格式
* *
* <p></> * <p></>
@@ -32,18 +33,16 @@ import androidx.annotation.FloatRange;
* {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高 * {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高
* {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 * {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 * {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
* * <p>
* 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域 * 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* * <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* <p></> * <p></>
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class DecodeConfig { public class DecodeConfig {
private Map<DecodeHintType,Object> hints = DecodeFormatManager.DEFAULT_HINTS; private Map<DecodeHintType, Object> hints = DecodeFormatManager.DEFAULT_HINTS;
public static final float DEFAULT_AREA_RECT_RATIO = 0.8f; public static final float DEFAULT_AREA_RECT_RATIO = 0.8f;
@@ -92,7 +91,7 @@ public class DecodeConfig {
*/ */
private int areaRectHorizontalOffset; private int areaRectHorizontalOffset;
public DecodeConfig(){ public DecodeConfig() {
} }
@@ -102,9 +101,11 @@ public class DecodeConfig {
/** /**
* 设置解码 * 设置解码
* @param hints {@link DecodeFormatManager}
* *
* 内置的一些解码可参见如下: * @param hints {@link DecodeFormatManager}
* <p>
* 内置的一些解码可参见如下:
* @return
* @see {@link DecodeFormatManager#DEFAULT_HINTS} * @see {@link DecodeFormatManager#DEFAULT_HINTS}
* @see {@link DecodeFormatManager#ALL_HINTS} * @see {@link DecodeFormatManager#ALL_HINTS}
* @see {@link DecodeFormatManager#CODE_128_HINTS} * @see {@link DecodeFormatManager#CODE_128_HINTS}
@@ -112,10 +113,8 @@ public class DecodeConfig {
* @see {@link DecodeFormatManager#ONE_DIMENSIONAL_HINTS} * @see {@link DecodeFormatManager#ONE_DIMENSIONAL_HINTS}
* @see {@link DecodeFormatManager#TWO_DIMENSIONAL_HINTS} * @see {@link DecodeFormatManager#TWO_DIMENSIONAL_HINTS}
* @see {@link DecodeFormatManager#DEFAULT_HINTS} * @see {@link DecodeFormatManager#DEFAULT_HINTS}
* * <p>
* 如果不满足您也可以通过{@link DecodeFormatManager#createDecodeHints(BarcodeFormat...)}自己配置支持的格式 * 如果不满足您也可以通过{@link DecodeFormatManager#createDecodeHints(BarcodeFormat...)}自己配置支持的格式
*
* @return
*/ */
public DecodeConfig setHints(Map<DecodeHintType, Object> hints) { public DecodeConfig setHints(Map<DecodeHintType, Object> hints) {
this.hints = hints; this.hints = hints;
@@ -124,6 +123,7 @@ public class DecodeConfig {
/** /**
* 是否支持识别反色码,黑白颜色反转 * 是否支持识别反色码,黑白颜色反转
*
* @return * @return
*/ */
public boolean isSupportLuminanceInvert() { public boolean isSupportLuminanceInvert() {
@@ -132,6 +132,7 @@ public class DecodeConfig {
/** /**
* 设置是否支持识别反色码,黑白颜色反转 * 设置是否支持识别反色码,黑白颜色反转
*
* @param supportLuminanceInvert 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。 * @param supportLuminanceInvert 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。
* @return * @return
*/ */
@@ -142,6 +143,7 @@ public class DecodeConfig {
/** /**
* 是否支持扫垂直的条码 * 是否支持扫垂直的条码
*
* @return * @return
*/ */
public boolean isSupportVerticalCode() { public boolean isSupportVerticalCode() {
@@ -150,6 +152,7 @@ public class DecodeConfig {
/** /**
* 设置是否支持扫垂直的条码 * 设置是否支持扫垂直的条码
*
* @param supportVerticalCode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。 * @param supportVerticalCode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。
* @return * @return
*/ */
@@ -160,6 +163,7 @@ public class DecodeConfig {
/** /**
* 是否支持使用多解码 * 是否支持使用多解码
*
* @return * @return
*/ */
public boolean isMultiDecode() { public boolean isMultiDecode() {
@@ -168,9 +172,10 @@ public class DecodeConfig {
/** /**
* 是否支持使用多解码 * 是否支持使用多解码
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer} *
* @param multiDecode 默认为{@code true} * @param multiDecode 默认为{@code true}
* @return * @return
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
*/ */
public DecodeConfig setMultiDecode(boolean multiDecode) { public DecodeConfig setMultiDecode(boolean multiDecode) {
isMultiDecode = multiDecode; isMultiDecode = multiDecode;
@@ -178,7 +183,8 @@ public class DecodeConfig {
} }
/** /**
* 是否支持识别反色码(条码黑白颜色反转的码)使用多解码 * 是否支持识别反色码(条码黑白颜色反转的码)使用多解码
*
* @return * @return
*/ */
public boolean isSupportLuminanceInvertMultiDecode() { public boolean isSupportLuminanceInvertMultiDecode() {
@@ -187,9 +193,10 @@ public class DecodeConfig {
/** /**
* 设置是否支持识别反色码(条码黑白颜色反转的码)使用多解码 * 设置是否支持识别反色码(条码黑白颜色反转的码)使用多解码
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer} *
* @param supportLuminanceInvertMultiDecode 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。 * @param supportLuminanceInvertMultiDecode 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。
* @return * @return
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
*/ */
public DecodeConfig setSupportLuminanceInvertMultiDecode(boolean supportLuminanceInvertMultiDecode) { public DecodeConfig setSupportLuminanceInvertMultiDecode(boolean supportLuminanceInvertMultiDecode) {
isSupportLuminanceInvertMultiDecode = supportLuminanceInvertMultiDecode; isSupportLuminanceInvertMultiDecode = supportLuminanceInvertMultiDecode;
@@ -198,6 +205,7 @@ public class DecodeConfig {
/** /**
* 是否支持垂直的条码,使用多解码 * 是否支持垂直的条码,使用多解码
*
* @return * @return
*/ */
public boolean isSupportVerticalCodeMultiDecode() { public boolean isSupportVerticalCodeMultiDecode() {
@@ -205,8 +213,8 @@ public class DecodeConfig {
} }
/** /**
* 设置是否支持垂直的条码,使用多解码 * 设置是否支持垂直的条码,使用多解码;解码时,对应的二值化的实现: {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer} *
* @param supportVerticalCodeMultiDecode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。 * @param supportVerticalCodeMultiDecode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。
* @return * @return
*/ */
@@ -217,6 +225,7 @@ public class DecodeConfig {
/** /**
* 需要分析识别区域 * 需要分析识别区域
*
* @return * @return
*/ */
public Rect getAnalyzeAreaRect() { public Rect getAnalyzeAreaRect() {
@@ -225,18 +234,16 @@ public class DecodeConfig {
/** /**
* 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 * 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* @param analyzeAreaRect
*
* 识别区域可设置的方式有如下几种:
* {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高
* {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
*
* 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
*
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* *
* @param analyzeAreaRect 识别区域可设置的方式有如下几种:
* {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高
* {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
* <p>
* 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* @return * @return
*/ */
public DecodeConfig setAnalyzeAreaRect(Rect analyzeAreaRect) { public DecodeConfig setAnalyzeAreaRect(Rect analyzeAreaRect) {
@@ -246,6 +253,7 @@ public class DecodeConfig {
/** /**
* 是否支持全区域扫码识别 * 是否支持全区域扫码识别
*
* @return * @return
*/ */
public boolean isFullAreaScan() { public boolean isFullAreaScan() {
@@ -254,17 +262,18 @@ public class DecodeConfig {
/** /**
* 设置是否支持全区域扫码识别,优先级比识别区域高 * 设置是否支持全区域扫码识别,优先级比识别区域高
*
* @param fullAreaScan 默认为{@code true} * @param fullAreaScan 默认为{@code true}
* * <p>
* 识别区域可设置的方式有如下几种: * 识别区域可设置的方式有如下几种:
* {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高 * {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高
* {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效 * {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 * {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
* * <p>
* 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域 * 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* * <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* @return * @return
*/ */
public DecodeConfig setFullAreaScan(boolean fullAreaScan) { public DecodeConfig setFullAreaScan(boolean fullAreaScan) {
@@ -274,6 +283,7 @@ public class DecodeConfig {
/** /**
* 识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 * 识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
*
* @return * @return
*/ */
public float getAreaRectRatio() { public float getAreaRectRatio() {
@@ -282,27 +292,26 @@ public class DecodeConfig {
/** /**
* 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低 * 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
* @param areaRectRatio
*
* 识别区域可设置的方式有如下几种:
* {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高
* {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
*
* 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
*
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* *
* @param areaRectRatio 识别区域可设置的方式有如下几种:
* {@link #setFullAreaScan(boolean)} 设置是否支持全区域扫码识别,优先级比识别区域高
* {@link #setAnalyzeAreaRect(Rect)} 设置需要分析识别区域,优先级比识别区域比例高,当设置了指定的分析区域时,识别区域比例和识别区域偏移量相关参数都将无效
* {@link #setAreaRectRatio(float)} 设置识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别,优先级最低
* <p>
* 因为{@link androidx.camera.view.PreviewView}的预览区域是经过裁剪的所以这里的区域并不是用户所能预览到的区域而是指Camera预览的真实区域
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* @return * @return
*/ */
public DecodeConfig setAreaRectRatio(@FloatRange(from = 0.5,to = 1.0) float areaRectRatio) { public DecodeConfig setAreaRectRatio(@FloatRange(from = 0.5, to = 1.0) float areaRectRatio) {
this.areaRectRatio = areaRectRatio; this.areaRectRatio = areaRectRatio;
return this; return this;
} }
/** /**
* 识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移 * 识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移
*
* @return * @return
*/ */
public int getAreaRectVerticalOffset() { public int getAreaRectVerticalOffset() {
@@ -311,6 +320,7 @@ public class DecodeConfig {
/** /**
* 设置识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移 * 设置识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移
*
* @param areaRectVerticalOffset * @param areaRectVerticalOffset
* @return * @return
*/ */
@@ -321,6 +331,7 @@ public class DecodeConfig {
/** /**
* 识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移 * 识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移
*
* @return * @return
*/ */
public int getAreaRectHorizontalOffset() { public int getAreaRectHorizontalOffset() {
@@ -329,6 +340,7 @@ public class DecodeConfig {
/** /**
* 设置识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移 * 设置识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移
*
* @param areaRectHorizontalOffset * @param areaRectHorizontalOffset
* @return * @return
*/ */

View File

@@ -13,6 +13,10 @@ import java.util.Map;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
/** /**
* 解码格式管理器
* <p>
* 将常见的一些解码配置已根据条形码类型进行了几大划分,可根据需要找到符合的划分配置类型直接使用。
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public final class DecodeFormatManager { public final class DecodeFormatManager {
@@ -20,45 +24,46 @@ public final class DecodeFormatManager {
/** /**
* 所有的 * 所有的
*/ */
public static final Map<DecodeHintType,Object> ALL_HINTS = new EnumMap<>(DecodeHintType.class); public static final Map<DecodeHintType, Object> ALL_HINTS = new EnumMap<>(DecodeHintType.class);
/** /**
* CODE_128 (最常用的一维码) * CODE_128 (最常用的一维码)
*/ */
public static final Map<DecodeHintType,Object> CODE_128_HINTS = createDecodeHint(BarcodeFormat.CODE_128); public static final Map<DecodeHintType, Object> CODE_128_HINTS = createDecodeHint(BarcodeFormat.CODE_128);
/** /**
* QR_CODE (最常用的二维码) * QR_CODE (最常用的二维码)
*/ */
public static final Map<DecodeHintType,Object> QR_CODE_HINTS = createDecodeHint(BarcodeFormat.QR_CODE); public static final Map<DecodeHintType, Object> QR_CODE_HINTS = createDecodeHint(BarcodeFormat.QR_CODE);
/** /**
* 一维码 * 一维码
*/ */
public static final Map<DecodeHintType,Object> ONE_DIMENSIONAL_HINTS = new EnumMap<>(DecodeHintType.class); public static final Map<DecodeHintType, Object> ONE_DIMENSIONAL_HINTS = new EnumMap<>(DecodeHintType.class);
/** /**
* 二维码 * 二维码
*/ */
public static final Map<DecodeHintType,Object> TWO_DIMENSIONAL_HINTS = new EnumMap<>(DecodeHintType.class); public static final Map<DecodeHintType, Object> TWO_DIMENSIONAL_HINTS = new EnumMap<>(DecodeHintType.class);
/** /**
* 默认 * 默认
*/ */
public static final Map<DecodeHintType,Object> DEFAULT_HINTS = new EnumMap<>(DecodeHintType.class); public static final Map<DecodeHintType, Object> DEFAULT_HINTS = new EnumMap<>(DecodeHintType.class);
static { static {
//all hints //all hints
addDecodeHintTypes(ALL_HINTS,getAllFormats()); addDecodeHintTypes(ALL_HINTS, getAllFormats());
//one dimension //one dimension
addDecodeHintTypes(ONE_DIMENSIONAL_HINTS,getOneDimensionalFormats()); addDecodeHintTypes(ONE_DIMENSIONAL_HINTS, getOneDimensionalFormats());
//Two dimension //Two dimension
addDecodeHintTypes(TWO_DIMENSIONAL_HINTS,getTwoDimensionalFormats()); addDecodeHintTypes(TWO_DIMENSIONAL_HINTS, getTwoDimensionalFormats());
//default hints //default hints
addDecodeHintTypes(DEFAULT_HINTS,getDefaultFormats()); addDecodeHintTypes(DEFAULT_HINTS, getDefaultFormats());
} }
/** /**
* 所有支持的{@link BarcodeFormat} * 所有支持的{@link BarcodeFormat}
*
* @return * @return
*/ */
private static List<BarcodeFormat> getAllFormats(){ private static List<BarcodeFormat> getAllFormats() {
List<BarcodeFormat> list = new ArrayList<>(); List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.AZTEC); list.add(BarcodeFormat.AZTEC);
list.add(BarcodeFormat.CODABAR); list.add(BarcodeFormat.CODABAR);
@@ -83,21 +88,22 @@ public final class DecodeFormatManager {
/** /**
* 二维码 * 二维码
* 包括如下几种格式: * 包括如下几种格式:
* {@link BarcodeFormat#CODABAR} * {@link BarcodeFormat#CODABAR}
* {@link BarcodeFormat#CODE_39} * {@link BarcodeFormat#CODE_39}
* {@link BarcodeFormat#CODE_93} * {@link BarcodeFormat#CODE_93}
* {@link BarcodeFormat#CODE_128} * {@link BarcodeFormat#CODE_128}
* {@link BarcodeFormat#EAN_8} * {@link BarcodeFormat#EAN_8}
* {@link BarcodeFormat#EAN_13} * {@link BarcodeFormat#EAN_13}
* {@link BarcodeFormat#ITF} * {@link BarcodeFormat#ITF}
* {@link BarcodeFormat#RSS_14} * {@link BarcodeFormat#RSS_14}
* {@link BarcodeFormat#RSS_EXPANDED} * {@link BarcodeFormat#RSS_EXPANDED}
* {@link BarcodeFormat#UPC_A} * {@link BarcodeFormat#UPC_A}
* {@link BarcodeFormat#UPC_E} * {@link BarcodeFormat#UPC_E}
* {@link BarcodeFormat#UPC_EAN_EXTENSION} * {@link BarcodeFormat#UPC_EAN_EXTENSION}
*
* @return * @return
*/ */
private static List<BarcodeFormat> getOneDimensionalFormats(){ private static List<BarcodeFormat> getOneDimensionalFormats() {
List<BarcodeFormat> list = new ArrayList<>(); List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.CODABAR); list.add(BarcodeFormat.CODABAR);
list.add(BarcodeFormat.CODE_39); list.add(BarcodeFormat.CODE_39);
@@ -117,14 +123,15 @@ public final class DecodeFormatManager {
/** /**
* 二维码 * 二维码
* 包括如下几种格式: * 包括如下几种格式:
* {@link BarcodeFormat#AZTEC} * {@link BarcodeFormat#AZTEC}
* {@link BarcodeFormat#DATA_MATRIX} * {@link BarcodeFormat#DATA_MATRIX}
* {@link BarcodeFormat#MAXICODE} * {@link BarcodeFormat#MAXICODE}
* {@link BarcodeFormat#PDF_417} * {@link BarcodeFormat#PDF_417}
* {@link BarcodeFormat#QR_CODE} * {@link BarcodeFormat#QR_CODE}
*
* @return * @return
*/ */
private static List<BarcodeFormat> getTwoDimensionalFormats(){ private static List<BarcodeFormat> getTwoDimensionalFormats() {
List<BarcodeFormat> list = new ArrayList<>(); List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.AZTEC); list.add(BarcodeFormat.AZTEC);
list.add(BarcodeFormat.DATA_MATRIX); list.add(BarcodeFormat.DATA_MATRIX);
@@ -137,13 +144,14 @@ public final class DecodeFormatManager {
/** /**
* 默认支持的格式 * 默认支持的格式
* 包括如下几种格式: * 包括如下几种格式:
* {@link BarcodeFormat#QR_CODE} * {@link BarcodeFormat#QR_CODE}
* {@link BarcodeFormat#UPC_A} * {@link BarcodeFormat#UPC_A}
* {@link BarcodeFormat#EAN_13} * {@link BarcodeFormat#EAN_13}
* {@link BarcodeFormat#CODE_128} * {@link BarcodeFormat#CODE_128}
*
* @return * @return
*/ */
private static List<BarcodeFormat> getDefaultFormats(){ private static List<BarcodeFormat> getDefaultFormats() {
List<BarcodeFormat> list = new ArrayList<>(); List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.QR_CODE); list.add(BarcodeFormat.QR_CODE);
list.add(BarcodeFormat.UPC_A); list.add(BarcodeFormat.UPC_A);
@@ -152,38 +160,35 @@ public final class DecodeFormatManager {
return list; return list;
} }
private static <T> List<T> singletonList(T o){
return Collections.singletonList(o);
}
/** /**
* 支持解码的格式 * 支持解码的格式
*
* @param barcodeFormats {@link BarcodeFormat} * @param barcodeFormats {@link BarcodeFormat}
* @return * @return
*/ */
public static Map<DecodeHintType,Object> createDecodeHints(@NonNull BarcodeFormat... barcodeFormats){ public static Map<DecodeHintType, Object> createDecodeHints(@NonNull BarcodeFormat... barcodeFormats) {
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class); Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
addDecodeHintTypes(hints, Arrays.asList(barcodeFormats)); addDecodeHintTypes(hints, Arrays.asList(barcodeFormats));
return hints; return hints;
} }
/** /**
* 支持解码的格式 * 支持解码的格式
*
* @param barcodeFormat {@link BarcodeFormat} * @param barcodeFormat {@link BarcodeFormat}
* @return * @return
*/ */
public static Map<DecodeHintType,Object> createDecodeHint(@NonNull BarcodeFormat barcodeFormat){ public static Map<DecodeHintType, Object> createDecodeHint(@NonNull BarcodeFormat barcodeFormat) {
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class); Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
addDecodeHintTypes(hints,singletonList(barcodeFormat)); addDecodeHintTypes(hints, Collections.singletonList(barcodeFormat));
return hints; return hints;
} }
/** /**
*
* @param hints * @param hints
* @param formats * @param formats
*/ */
private static void addDecodeHintTypes(Map<DecodeHintType,Object> hints,List<BarcodeFormat> formats){ private static void addDecodeHintTypes(Map<DecodeHintType, Object> hints, List<BarcodeFormat> formats) {
// Image is known to be of one of a few possible formats. // Image is known to be of one of a few possible formats.
hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
// Spend more time to try to find a barcode; optimize for accuracy, not speed. // Spend more time to try to find a barcode; optimize for accuracy, not speed.

View File

@@ -1,5 +1,6 @@
package com.king.zxing; package com.king.zxing;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -27,6 +28,7 @@ import androidx.annotation.FloatRange;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.camera.core.Camera; import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraSelector;
import androidx.camera.core.FocusMeteringAction; import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.ImageAnalysis; import androidx.camera.core.ImageAnalysis;
@@ -43,6 +45,18 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
/** /**
* 相机扫描基类;{@link DefaultCameraScan} 为 {@link CameraScan} 的默认实现
* <p>
* 快速实现扫描识别主要有以下几种方式:
* <p>
* 1、通过继承 {@link CaptureActivity}或者{@link CaptureFragment}或其子类,可快速实现扫描识别。
* 适用于大多数场景自定义布局时需覆写getLayoutId方法
* <p>
* 2、在你项目的Activity或者Fragment中实例化一个{@link DefaultCameraScan}。(适用于想在扫码界面写交互逻辑,又因为项目
* 架构或其它原因,无法直接或间接继承{@link CaptureActivity}或{@link CaptureFragment}时使用)
* <p>
* 3、继承{@link CameraScan}自己实现一个,可参照默认实现类{@link DefaultCameraScan}其他步骤同方式2。高级用法谨慎使用
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class DefaultCameraScan extends CameraScan { public class DefaultCameraScan extends CameraScan {
@@ -61,6 +75,11 @@ public class DefaultCameraScan extends CameraScan {
*/ */
private static final int HOVER_TAP_SLOP = 20; private static final int HOVER_TAP_SLOP = 20;
/**
* 每次缩放改变的步长
*/
private static final float ZOOM_STEP_SIZE = 0.1F;
private FragmentActivity mFragmentActivity; private FragmentActivity mFragmentActivity;
private Context mContext; private Context mContext;
private LifecycleOwner mLifecycleOwner; private LifecycleOwner mLifecycleOwner;
@@ -92,15 +111,15 @@ public class DefaultCameraScan extends CameraScan {
private AmbientLightManager mAmbientLightManager; private AmbientLightManager mAmbientLightManager;
private int mOrientation; private int mOrientation;
private int mScreenWidth; private int mImageWidth;
private int mScreenHeight; private int mImageHeight;
private long mLastAutoZoomTime; private long mLastAutoZoomTime;
private long mLastHoveTapTime; private long mLastHoveTapTime;
private boolean isClickTap; private boolean isClickTap;
private float mDownX; private float mDownX;
private float mDownY; private float mDownY;
public DefaultCameraScan(@NonNull FragmentActivity activity,@NonNull PreviewView previewView){ public DefaultCameraScan(@NonNull FragmentActivity activity, @NonNull PreviewView previewView) {
this.mFragmentActivity = activity; this.mFragmentActivity = activity;
this.mLifecycleOwner = activity; this.mLifecycleOwner = activity;
this.mContext = activity; this.mContext = activity;
@@ -108,7 +127,7 @@ public class DefaultCameraScan extends CameraScan {
initData(); initData();
} }
public DefaultCameraScan(@NonNull Fragment fragment,@NonNull PreviewView previewView){ public DefaultCameraScan(@NonNull Fragment fragment, @NonNull PreviewView previewView) {
this.mFragmentActivity = fragment.getActivity(); this.mFragmentActivity = fragment.getActivity();
this.mLifecycleOwner = fragment; this.mLifecycleOwner = fragment;
this.mContext = fragment.getContext(); this.mContext = fragment.getContext();
@@ -116,25 +135,34 @@ public class DefaultCameraScan extends CameraScan {
initData(); initData();
} }
private ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener(){ /**
* 缩放手势检测
*/
private ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override @Override
public boolean onScale(ScaleGestureDetector detector) { public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor(); float scale = detector.getScaleFactor();
if(mCamera != null){ if (mCamera != null) {
float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio(); float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio();
// 根据缩放的手势和当前比例进行缩放
zoomTo(ratio * scale); zoomTo(ratio * scale);
return true;
} }
return true; return false;
} }
}; };
private void initData(){ /**
* 初始化
*/
@SuppressLint("ClickableViewAccessibility")
private void initData() {
mResultLiveData = new MutableLiveData<>(); mResultLiveData = new MutableLiveData<>();
mResultLiveData.observe(mLifecycleOwner, result -> { mResultLiveData.observe(mLifecycleOwner, result -> {
if(result != null){ if (result != null) {
handleAnalyzeResult(result); handleAnalyzeResult(result);
}else if(mOnScanResultCallback != null){ } else if (mOnScanResultCallback != null) {
mOnScanResultCallback.onScanResultFailure(); mOnScanResultCallback.onScanResultFailure();
} }
}); });
@@ -144,42 +172,38 @@ public class DefaultCameraScan extends CameraScan {
ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener); ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
mPreviewView.setOnTouchListener((v, event) -> { mPreviewView.setOnTouchListener((v, event) -> {
handlePreviewViewClickTap(event); handlePreviewViewClickTap(event);
if(isNeedTouchZoom()){ if (isNeedTouchZoom()) {
return scaleGestureDetector.onTouchEvent(event); return scaleGestureDetector.onTouchEvent(event);
} }
return false; return false;
}); });
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
mScreenWidth = displayMetrics.widthPixels;
mScreenHeight = displayMetrics.heightPixels;
LogUtils.d(String.format("displayMetrics:%dx%d",mScreenWidth,mScreenHeight));
mBeepManager = new BeepManager(mContext); mBeepManager = new BeepManager(mContext);
mAmbientLightManager = new AmbientLightManager(mContext); mAmbientLightManager = new AmbientLightManager(mContext);
if(mAmbientLightManager != null){ mAmbientLightManager.register();
mAmbientLightManager.register(); mAmbientLightManager.setOnLightSensorEventListener((dark, lightLux) -> {
mAmbientLightManager.setOnLightSensorEventListener((dark, lightLux) -> { if (flashlightView != null) {
if(flashlightView != null){ if (dark) {
if(dark){ if (flashlightView.getVisibility() != View.VISIBLE) {
if(flashlightView.getVisibility() != View.VISIBLE){ flashlightView.setVisibility(View.VISIBLE);
flashlightView.setVisibility(View.VISIBLE); flashlightView.setSelected(isTorchEnabled());
flashlightView.setSelected(isTorchEnabled());
}
}else if(flashlightView.getVisibility() == View.VISIBLE && !isTorchEnabled()){
flashlightView.setVisibility(View.INVISIBLE);
flashlightView.setSelected(false);
} }
} else if (flashlightView.getVisibility() == View.VISIBLE && !isTorchEnabled()) {
flashlightView.setVisibility(View.INVISIBLE);
flashlightView.setSelected(false);
} }
}); }
} });
} }
private void handlePreviewViewClickTap(MotionEvent event){ /**
if(event.getPointerCount() == 1){ * 处理预览视图点击事件;如果触发的点击事件被判定对焦操作,则开始自动对焦
switch (event.getAction()){ *
* @param event
*/
private void handlePreviewViewClickTap(MotionEvent event) {
if (event.getPointerCount() == 1) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
isClickTap = true; isClickTap = true;
mDownX = event.getX(); mDownX = event.getX();
@@ -187,35 +211,43 @@ public class DefaultCameraScan extends CameraScan {
mLastHoveTapTime = System.currentTimeMillis(); mLastHoveTapTime = System.currentTimeMillis();
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
isClickTap = MathUtils.distance(mDownX,mDownY,event.getX(),event.getY()) < HOVER_TAP_SLOP; isClickTap = MathUtils.distance(mDownX, mDownY, event.getX(), event.getY()) < HOVER_TAP_SLOP;
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
if(isClickTap && mLastHoveTapTime + HOVER_TAP_TIMEOUT > System.currentTimeMillis()){ if (isClickTap && mLastHoveTapTime + HOVER_TAP_TIMEOUT > System.currentTimeMillis()) {
startFocusAndMetering(event.getX(),event.getY()); // 开始对焦和测光
startFocusAndMetering(event.getX(), event.getY());
} }
break; break;
} }
} }
} }
private void startFocusAndMetering(float x, float y){ /**
if(mCamera != null){ * 开始对焦和测光
MeteringPoint point = mPreviewView.getMeteringPointFactory().createPoint(x,y); *
* @param x
* @param y
*/
private void startFocusAndMetering(float x, float y) {
if (mCamera != null) {
MeteringPoint point = mPreviewView.getMeteringPointFactory().createPoint(x, y);
FocusMeteringAction focusMeteringAction = new FocusMeteringAction.Builder(point).build(); FocusMeteringAction focusMeteringAction = new FocusMeteringAction.Builder(point).build();
if(mCamera.getCameraInfo().isFocusMeteringSupported(focusMeteringAction)){ if (mCamera.getCameraInfo().isFocusMeteringSupported(focusMeteringAction)) {
mCamera.getCameraControl().startFocusAndMetering(focusMeteringAction); mCamera.getCameraControl().startFocusAndMetering(focusMeteringAction);
LogUtils.d("startFocusAndMetering:" + x + "," + y); LogUtils.d("startFocusAndMetering:" + x + "," + y);
} }
} }
} }
/**
* 初始化配置
private void initConfig(){ */
if(mCameraConfig == null){ private void initConfig() {
if (mCameraConfig == null) {
mCameraConfig = new CameraConfig(); mCameraConfig = new CameraConfig();
} }
if(mAnalyzer == null){ if (mAnalyzer == null) {
mAnalyzer = new MultiFormatAnalyzer(); mAnalyzer = new MultiFormatAnalyzer();
} }
} }
@@ -223,19 +255,19 @@ public class DefaultCameraScan extends CameraScan {
@Override @Override
public CameraScan setCameraConfig(CameraConfig cameraConfig) { public CameraScan setCameraConfig(CameraConfig cameraConfig) {
if(cameraConfig != null){ if (cameraConfig != null) {
this.mCameraConfig = cameraConfig; this.mCameraConfig = cameraConfig;
} }
return this; return this;
} }
@Override @Override
public void startCamera(){ public void startCamera() {
initConfig(); initConfig();
mCameraProviderFuture = ProcessCameraProvider.getInstance(mContext); mCameraProviderFuture = ProcessCameraProvider.getInstance(mContext);
mCameraProviderFuture.addListener(() -> { mCameraProviderFuture.addListener(() -> {
try{ try {
Preview preview = mCameraConfig.options(new Preview.Builder()); Preview preview = mCameraConfig.options(new Preview.Builder());
//相机选择器 //相机选择器
@@ -248,49 +280,52 @@ public class DefaultCameraScan extends CameraScan {
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888) .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)); .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST));
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), image -> { imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), image -> {
if(isAnalyze && !isAnalyzeResult && mAnalyzer != null){ mImageWidth = image.getWidth();
Result result = mAnalyzer.analyze(image,mOrientation); mImageHeight = image.getHeight();
if (isAnalyze && !isAnalyzeResult && mAnalyzer != null) {
Result result = mAnalyzer.analyze(image, mOrientation);
mResultLiveData.postValue(result); mResultLiveData.postValue(result);
} }
image.close(); image.close();
}); });
if(mCamera != null){ if (mCamera != null) {
mCameraProviderFuture.get().unbindAll(); mCameraProviderFuture.get().unbindAll();
} }
//绑定到生命周期 // 绑定到生命周期
mCamera = mCameraProviderFuture.get().bindToLifecycle(mLifecycleOwner, cameraSelector, preview, imageAnalysis); mCamera = mCameraProviderFuture.get().bindToLifecycle(mLifecycleOwner, cameraSelector, preview, imageAnalysis);
}catch (Exception e){ } catch (Exception e) {
LogUtils.e(e); LogUtils.e(e);
} }
},ContextCompat.getMainExecutor(mContext)); }, ContextCompat.getMainExecutor(mContext));
} }
/** /**
* 处理分析结果 * 处理分析结果
*
* @param result * @param result
*/ */
private synchronized void handleAnalyzeResult(Result result){ private synchronized void handleAnalyzeResult(Result result) {
if(isAnalyzeResult || !isAnalyze){ if (isAnalyzeResult || !isAnalyze) {
return; return;
} }
isAnalyzeResult = true; isAnalyzeResult = true;
if(mBeepManager != null){ if (mBeepManager != null) {
mBeepManager.playBeepSoundAndVibrate(); mBeepManager.playBeepSoundAndVibrate();
} }
if(result.getBarcodeFormat() == BarcodeFormat.QR_CODE && isNeedAutoZoom() && mLastAutoZoomTime + 100 < System.currentTimeMillis()){ if (result.getBarcodeFormat() == BarcodeFormat.QR_CODE && isNeedAutoZoom() && mLastAutoZoomTime + 100 < System.currentTimeMillis()) {
ResultPoint[] points = result.getResultPoints(); ResultPoint[] points = result.getResultPoints();
if(points != null && points.length >= 2){ if (points != null && points.length >= 2) {
float distance1 = ResultPoint.distance(points[0],points[1]); float distance1 = ResultPoint.distance(points[0], points[1]);
float maxDistance = distance1; float maxDistance = distance1;
if(points.length >= 3){ if (points.length >= 3) {
float distance2 = ResultPoint.distance(points[1],points[2]); float distance2 = ResultPoint.distance(points[1], points[2]);
float distance3 = ResultPoint.distance(points[0],points[2]); float distance3 = ResultPoint.distance(points[0], points[2]);
maxDistance = Math.max(Math.max(distance1,distance2),distance3); maxDistance = Math.max(Math.max(distance1, distance2), distance3);
} }
if(handleAutoZoom((int)maxDistance,result)){ if (handleAutoZoom((int) maxDistance, result)) {
return; return;
} }
} }
@@ -299,9 +334,15 @@ public class DefaultCameraScan extends CameraScan {
scanResultCallback(result); scanResultCallback(result);
} }
private boolean handleAutoZoom(int distance,Result result){ /**
int size = Math.min(mScreenWidth,mScreenHeight); * 处理自动缩放
if(distance * 4 < size){ * @param distance
* @param result
* @return
*/
private boolean handleAutoZoom(int distance, Result result) {
int size = Math.min(mImageWidth, mImageHeight);
if (distance * 4 < size) {
mLastAutoZoomTime = System.currentTimeMillis(); mLastAutoZoomTime = System.currentTimeMillis();
zoomIn(); zoomIn();
scanResultCallback(result); scanResultCallback(result);
@@ -310,8 +351,12 @@ public class DefaultCameraScan extends CameraScan {
return false; return false;
} }
private void scanResultCallback(Result result){ /**
if(mOnScanResultCallback != null && mOnScanResultCallback.onScanResultCallback(result)){ * 扫描结果回调
* @param result
*/
private void scanResultCallback(Result result) {
if (mOnScanResultCallback != null && mOnScanResultCallback.onScanResultCallback(result)) {
/* /*
* 如果拦截了结果则重置分析结果状态并当isAnalyze为true时默认会继续分析图像也就是连扫 * 如果拦截了结果则重置分析结果状态并当isAnalyze为true时默认会继续分析图像也就是连扫
* 如果只是想拦截扫码结果回调,并不想继续分析图像(不想连扫),请在拦截扫码逻辑处通过调用 * 如果只是想拦截扫码结果回调,并不想继续分析图像(不想连扫),请在拦截扫码逻辑处通过调用
@@ -321,21 +366,20 @@ public class DefaultCameraScan extends CameraScan {
return; return;
} }
if(mFragmentActivity != null){ if (mFragmentActivity != null) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(SCAN_RESULT,result.getText()); intent.putExtra(SCAN_RESULT, result.getText());
mFragmentActivity.setResult(Activity.RESULT_OK,intent); mFragmentActivity.setResult(Activity.RESULT_OK, intent);
mFragmentActivity.finish(); mFragmentActivity.finish();
} }
} }
@Override @Override
public void stopCamera(){ public void stopCamera() {
if(mCameraProviderFuture != null){ if (mCameraProviderFuture != null) {
try { try {
mCameraProviderFuture.get().unbindAll(); mCameraProviderFuture.get().unbindAll();
}catch (Exception e){ } catch (Exception e) {
LogUtils.e(e); LogUtils.e(e);
} }
} }
@@ -353,45 +397,45 @@ public class DefaultCameraScan extends CameraScan {
return this; return this;
} }
@Override @Override
public void zoomIn(){ public void zoomIn() {
if(mCamera != null){ if (mCamera != null) {
float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio() + 0.1f; float ratio = getCameraInfo().getZoomState().getValue().getZoomRatio() + ZOOM_STEP_SIZE;
float maxRatio = mCamera.getCameraInfo().getZoomState().getValue().getMaxZoomRatio(); float maxRatio = getCameraInfo().getZoomState().getValue().getMaxZoomRatio();
if(ratio <= maxRatio){ if (ratio <= maxRatio) {
mCamera.getCameraControl().setZoomRatio(ratio); mCamera.getCameraControl().setZoomRatio(ratio);
} }
} }
} }
@Override @Override
public void zoomOut(){ public void zoomOut() {
if(mCamera != null){ if (mCamera != null) {
float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio() - 0.1f; float ratio = getCameraInfo().getZoomState().getValue().getZoomRatio() - ZOOM_STEP_SIZE;
float minRatio = mCamera.getCameraInfo().getZoomState().getValue().getMinZoomRatio(); float minRatio = getCameraInfo().getZoomState().getValue().getMinZoomRatio();
if(ratio >= minRatio){ if (ratio >= minRatio) {
mCamera.getCameraControl().setZoomRatio(ratio); mCamera.getCameraControl().setZoomRatio(ratio);
} }
} }
} }
@Override @Override
public void zoomTo(float ratio) { public void zoomTo(float ratio) {
if(mCamera != null){ if (mCamera != null) {
ZoomState zoomState = mCamera.getCameraInfo().getZoomState().getValue(); ZoomState zoomState = getCameraInfo().getZoomState().getValue();
float maxRatio = zoomState.getMaxZoomRatio(); float maxRatio = zoomState.getMaxZoomRatio();
float minRatio = zoomState.getMinZoomRatio(); float minRatio = zoomState.getMinZoomRatio();
float zoom = Math.max(Math.min(ratio,maxRatio),minRatio); float zoom = Math.max(Math.min(ratio, maxRatio), minRatio);
mCamera.getCameraControl().setZoomRatio(zoom); mCamera.getCameraControl().setZoomRatio(zoom);
} }
} }
@Override @Override
public void lineZoomIn() { public void lineZoomIn() {
if(mCamera != null){ if (mCamera != null) {
float zoom = mCamera.getCameraInfo().getZoomState().getValue().getLinearZoom() + 0.1f; float zoom = getCameraInfo().getZoomState().getValue().getLinearZoom() + ZOOM_STEP_SIZE;
if(zoom <= 1f){ if (zoom <= 1f) {
mCamera.getCameraControl().setLinearZoom(zoom); mCamera.getCameraControl().setLinearZoom(zoom);
} }
} }
@@ -399,31 +443,31 @@ public class DefaultCameraScan extends CameraScan {
@Override @Override
public void lineZoomOut() { public void lineZoomOut() {
if(mCamera != null){ if (mCamera != null) {
float zoom = mCamera.getCameraInfo().getZoomState().getValue().getLinearZoom() - 0.1f; float zoom = getCameraInfo().getZoomState().getValue().getLinearZoom() - ZOOM_STEP_SIZE;
if(zoom >= 0f){ if (zoom >= 0f) {
mCamera.getCameraControl().setLinearZoom(zoom); mCamera.getCameraControl().setLinearZoom(zoom);
} }
} }
} }
@Override @Override
public void lineZoomTo(@FloatRange(from = 0.0,to = 1.0) float linearZoom) { public void lineZoomTo(@FloatRange(from = 0.0, to = 1.0) float linearZoom) {
if(mCamera != null){ if (mCamera != null) {
mCamera.getCameraControl().setLinearZoom(linearZoom); mCamera.getCameraControl().setLinearZoom(linearZoom);
} }
} }
@Override @Override
public void enableTorch(boolean torch) { public void enableTorch(boolean torch) {
if(mCamera != null && hasFlashUnit()){ if (mCamera != null && hasFlashUnit()) {
mCamera.getCameraControl().enableTorch(torch); mCamera.getCameraControl().enableTorch(torch);
} }
} }
@Override @Override
public boolean isTorchEnabled() { public boolean isTorchEnabled() {
if(mCamera != null){ if (mCamera != null) {
return mCamera.getCameraInfo().getTorchState().getValue() == TorchState.ON; return mCamera.getCameraInfo().getTorchState().getValue() == TorchState.ON;
} }
return false; return false;
@@ -431,11 +475,12 @@ public class DefaultCameraScan extends CameraScan {
/** /**
* 是否支持闪光灯 * 是否支持闪光灯
*
* @return * @return
*/ */
@Override @Override
public boolean hasFlashUnit(){ public boolean hasFlashUnit() {
if(mCamera != null){ if (mCamera != null) {
return mCamera.getCameraInfo().hasFlashUnit(); return mCamera.getCameraInfo().hasFlashUnit();
} }
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
@@ -443,7 +488,7 @@ public class DefaultCameraScan extends CameraScan {
@Override @Override
public CameraScan setVibrate(boolean vibrate) { public CameraScan setVibrate(boolean vibrate) {
if(mBeepManager != null){ if (mBeepManager != null) {
mBeepManager.setVibrate(vibrate); mBeepManager.setVibrate(vibrate);
} }
return this; return this;
@@ -451,7 +496,7 @@ public class DefaultCameraScan extends CameraScan {
@Override @Override
public CameraScan setPlayBeep(boolean playBeep) { public CameraScan setPlayBeep(boolean playBeep) {
if(mBeepManager != null){ if (mBeepManager != null) {
mBeepManager.setPlayBeep(playBeep); mBeepManager.setPlayBeep(playBeep);
} }
return this; return this;
@@ -465,19 +510,27 @@ public class DefaultCameraScan extends CameraScan {
@Nullable @Nullable
@Override @Override
public Camera getCamera(){ public Camera getCamera() {
return mCamera; return mCamera;
} }
/**
* CameraInfo
*
* @return {@link CameraInfo}
*/
private CameraInfo getCameraInfo() {
return mCamera.getCameraInfo();
}
@Override @Override
public void release() { public void release() {
isAnalyze = false; isAnalyze = false;
flashlightView = null; flashlightView = null;
if(mAmbientLightManager != null){ if (mAmbientLightManager != null) {
mAmbientLightManager.unregister(); mAmbientLightManager.unregister();
} }
if(mBeepManager != null){ if (mBeepManager != null) {
mBeepManager.close(); mBeepManager.close();
} }
stopCamera(); stopCamera();
@@ -486,23 +539,23 @@ public class DefaultCameraScan extends CameraScan {
@Override @Override
public CameraScan bindFlashlightView(@Nullable View v) { public CameraScan bindFlashlightView(@Nullable View v) {
flashlightView = v; flashlightView = v;
if(mAmbientLightManager != null){ if (mAmbientLightManager != null) {
mAmbientLightManager.setLightSensorEnabled(v != null); mAmbientLightManager.setLightSensorEnabled(v != null);
} }
return this; return this;
} }
@Override @Override
public CameraScan setDarkLightLux(float lightLux){ public CameraScan setDarkLightLux(float lightLux) {
if(mAmbientLightManager != null){ if (mAmbientLightManager != null) {
mAmbientLightManager.setDarkLightLux(lightLux); mAmbientLightManager.setDarkLightLux(lightLux);
} }
return this; return this;
} }
@Override @Override
public CameraScan setBrightLightLux(float lightLux){ public CameraScan setBrightLightLux(float lightLux) {
if(mAmbientLightManager != null){ if (mAmbientLightManager != null) {
mAmbientLightManager.setBrightLightLux(lightLux); mAmbientLightManager.setBrightLightLux(lightLux);
} }
return this; return this;

View File

@@ -1,10 +1,11 @@
package com.king.zxing; package com.king.zxing;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.camera.core.Camera; import androidx.camera.core.Camera;
/** /**
* 相机定义
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public interface ICamera { public interface ICamera {
@@ -21,9 +22,11 @@ public interface ICamera {
/** /**
* 获取{@link Camera} * 获取{@link Camera}
*
* @return * @return
*/ */
@Nullable Camera getCamera(); @Nullable
Camera getCamera();
/** /**
* 释放 * 释放

View File

@@ -3,6 +3,8 @@ package com.king.zxing;
import androidx.annotation.FloatRange; import androidx.annotation.FloatRange;
/** /**
* 相机控制:主要包括调节焦距和闪光灯控制
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public interface ICameraControl { public interface ICameraControl {
@@ -19,6 +21,7 @@ public interface ICameraControl {
/** /**
* 缩放到指定比例 * 缩放到指定比例
*
* @param ratio * @param ratio
*/ */
void zoomTo(float ratio); void zoomTo(float ratio);
@@ -35,24 +38,28 @@ public interface ICameraControl {
/** /**
* 线性缩放到指定比例 * 线性缩放到指定比例
*
* @param linearZoom * @param linearZoom
*/ */
void lineZoomTo(@FloatRange(from = 0.0,to = 1.0) float linearZoom); void lineZoomTo(@FloatRange(from = 0.0, to = 1.0) float linearZoom);
/** /**
* 设置闪光灯(手电筒)是否开启 * 设置闪光灯(手电筒)是否开启
*
* @param torch * @param torch
*/ */
void enableTorch(boolean torch); void enableTorch(boolean torch);
/** /**
* 闪光灯(手电筒)是否开启 * 闪光灯(手电筒)是否开启
*
* @return * @return
*/ */
boolean isTorchEnabled(); boolean isTorchEnabled();
/** /**
* 是否支持闪光灯 * 是否支持闪光灯
*
* @return * @return
*/ */
boolean hasFlashUnit(); boolean hasFlashUnit();

View File

@@ -1,30 +1,20 @@
package com.king.zxing; package com.king.zxing;
/*
* Copyright (C) 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.LinearGradient; import android.graphics.LinearGradient;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.Shader; import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.text.Layout; import android.text.Layout;
import android.text.StaticLayout; import android.text.StaticLayout;
import android.text.TextPaint; import android.text.TextPaint;
@@ -32,28 +22,37 @@ import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes; import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; 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.
* *
* @author dswitkin@google.com (Daniel Switkin) * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class ViewfinderView extends View { public class ViewfinderView extends View {
private static final int CURRENT_POINT_OPACITY = 0xA0; /**
private static final int MAX_RESULT_POINTS = 20; * 默认范围比例,之所以默认为 1.2 是因为内切圆半径和外切圆半径之和的二分之一1 + √2) / 2 ≈ 1.2
private static final int POINT_SIZE = 30; */
private final float DEFAULT_RANGE_RATIO = 1.2F;
private final float MAX_ZOOM_RATIO = 1.2F;
/** /**
* 画笔 * 画笔
@@ -186,25 +185,78 @@ public class ViewfinderView extends View {
*/ */
private FrameGravity frameGravity; private FrameGravity frameGravity;
private Point point;
private int pointColor; private int pointColor;
private int pointStrokeColor; private int pointStrokeColor;
private Bitmap pointBitmap;
private boolean isShowPointAnim = true;
private float pointRadius; private float pointRadius;
private float pointStrokeRatio = 1.2f; private float pointStrokeRatio;
private float pointStrokeRadius;
/**
* 当前缩放比例
*/
private float currentZoomRatio = 1.0f;
/**
* 最后一次缩放比例(即上一次缩放比例)
*/
private float lastZoomRatio;
/**
* 缩放速度
*/
private float zoomSpeed = 0.02f;
public enum LaserStyle{ private int zoomCount;
NONE(0),LINE(1),GRID(2);
/**
* 结果点有效点击范围半径
*/
private float pointRangeRadius;
private Bitmap laserBitmap;
private int viewfinderStyle = ViewfinderStyle.CLASSIC;
private List<Point> pointList;
private boolean isShowPoints = false;
private OnItemClickListener onItemClickListener;
private GestureDetector gestureDetector;
/**
* 取景框样式
*/
@IntDef({ViewfinderStyle.CLASSIC, ViewfinderStyle.POPULAR})
@Retention(RetentionPolicy.SOURCE)
public @interface ViewfinderStyle {
/**
* 经典样式:经典的扫码风格(带扫码框)
*/
int CLASSIC = 0;
/**
* 流行样式:类似于新版的微信全屏扫码(不带扫码框)
*/
int POPULAR = 1;
}
/**
* 扫描线样式
*/
public enum LaserStyle {
NONE(0), LINE(1), GRID(2), IMAGE(3);
private int mValue; private int mValue;
LaserStyle(int value){
LaserStyle(int value) {
mValue = value; mValue = value;
} }
private static LaserStyle getFromInt(int value){ private static LaserStyle getFromInt(int value) {
for(LaserStyle style : LaserStyle.values()){ for (LaserStyle style : LaserStyle.values()) {
if(style.mValue == value){ if (style.mValue == value) {
return style; return style;
} }
} }
@@ -212,18 +264,21 @@ public class ViewfinderView extends View {
} }
} }
/**
* 文字位置
*/
public enum TextLocation { public enum TextLocation {
TOP(0),BOTTOM(1); TOP(0), BOTTOM(1);
private int mValue; private int mValue;
TextLocation(int value){ TextLocation(int value) {
mValue = value; mValue = value;
} }
private static TextLocation getFromInt(int value){ private static TextLocation getFromInt(int value) {
for(TextLocation location : TextLocation.values()){ for (TextLocation location : TextLocation.values()) {
if(location.mValue == value){ if (location.mValue == value) {
return location; return location;
} }
} }
@@ -231,7 +286,9 @@ public class ViewfinderView extends View {
} }
} }
/**
* 扫码框对齐方式
*/
public enum FrameGravity { public enum FrameGravity {
CENTER(0), LEFT(1), TOP(2), RIGHT(3), BOTTOM(4); CENTER(0), LEFT(1), TOP(2), RIGHT(3), BOTTOM(4);
@@ -252,65 +309,107 @@ public class ViewfinderView extends View {
} }
public ViewfinderView(Context context) { public ViewfinderView(Context context) {
this(context,null); this(context, null);
} }
public ViewfinderView(Context context, @Nullable AttributeSet attrs) { public ViewfinderView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0); this(context, attrs, 0);
} }
public ViewfinderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { public ViewfinderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
init(context,attrs); init(context, attrs);
} }
/**
* 初始化
*
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet attrs) { private void init(Context context, AttributeSet attrs) {
//初始化自定义属性信息 // 初始化自定义属性信息
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
maskColor = array.getColor(R.styleable.ViewfinderView_maskColor, ContextCompat.getColor(context,R.color.viewfinder_mask)); maskColor = array.getColor(R.styleable.ViewfinderView_maskColor, ContextCompat.getColor(context, R.color.viewfinder_mask));
frameColor = array.getColor(R.styleable.ViewfinderView_frameColor, ContextCompat.getColor(context,R.color.viewfinder_frame)); 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)); 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)); laserColor = array.getColor(R.styleable.ViewfinderView_laserColor, ContextCompat.getColor(context, R.color.viewfinder_laser));
labelText = array.getString(R.styleable.ViewfinderView_labelText); labelText = array.getString(R.styleable.ViewfinderView_labelText);
labelTextColor = array.getColor(R.styleable.ViewfinderView_labelTextColor, ContextCompat.getColor(context,R.color.viewfinder_text_color)); labelTextColor = array.getColor(R.styleable.ViewfinderView_labelTextColor, ContextCompat.getColor(context, R.color.viewfinder_text_color));
labelTextSize = array.getDimension(R.styleable.ViewfinderView_labelTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,14f,getResources().getDisplayMetrics())); labelTextSize = array.getDimension(R.styleable.ViewfinderView_labelTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14f, getResources().getDisplayMetrics()));
labelTextPadding = array.getDimension(R.styleable.ViewfinderView_labelTextPadding,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,24,getResources().getDisplayMetrics())); labelTextPadding = array.getDimension(R.styleable.ViewfinderView_labelTextPadding, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
labelTextWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_labelTextWidth,0); labelTextWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_labelTextWidth, 0);
labelTextLocation = TextLocation.getFromInt(array.getInt(R.styleable.ViewfinderView_labelTextLocation,0)); labelTextLocation = TextLocation.getFromInt(array.getInt(R.styleable.ViewfinderView_labelTextLocation, 0));
frameWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameWidth,0); frameWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameWidth, 0);
frameHeight = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameHeight,0); frameHeight = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameHeight, 0);
laserStyle = LaserStyle.getFromInt(array.getInt(R.styleable.ViewfinderView_laserStyle,LaserStyle.LINE.mValue)); laserStyle = LaserStyle.getFromInt(array.getInt(R.styleable.ViewfinderView_laserStyle, LaserStyle.LINE.mValue));
gridColumn = array.getInt(R.styleable.ViewfinderView_gridColumn,20); gridColumn = array.getInt(R.styleable.ViewfinderView_gridColumn, 20);
gridHeight = (int)array.getDimension(R.styleable.ViewfinderView_gridHeight,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,40,getResources().getDisplayMetrics())); gridHeight = (int) array.getDimension(R.styleable.ViewfinderView_gridHeight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics()));
cornerRectWidth = (int) array.getDimension(R.styleable.ViewfinderView_cornerRectWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()));
cornerRectHeight = (int) array.getDimension(R.styleable.ViewfinderView_cornerRectHeight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
scannerLineMoveDistance = (int) array.getDimension(R.styleable.ViewfinderView_scannerLineMoveDistance, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()));
scannerLineHeight = (int) array.getDimension(R.styleable.ViewfinderView_scannerLineHeight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));
frameLineWidth = (int) array.getDimension(R.styleable.ViewfinderView_frameLineWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()));
scannerAnimationDelay = array.getInteger(R.styleable.ViewfinderView_scannerAnimationDelay, 20);
frameRatio = array.getFloat(R.styleable.ViewfinderView_frameRatio, 0.625f);
framePaddingLeft = array.getDimension(R.styleable.ViewfinderView_framePaddingLeft, 0);
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, FrameGravity.CENTER.mValue));
pointColor = array.getColor(R.styleable.ViewfinderView_pointColor, ContextCompat.getColor(context, R.color.viewfinder_point));
pointStrokeColor = array.getColor(R.styleable.ViewfinderView_pointStrokeColor, Color.WHITE);
pointRadius = array.getDimension(R.styleable.ViewfinderView_pointRadius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics()));
pointStrokeRatio = array.getFloat(R.styleable.ViewfinderView_pointStrokeRatio, DEFAULT_RANGE_RATIO);
isShowPointAnim = array.getBoolean(R.styleable.ViewfinderView_showPointAnim, true);
Drawable pointDrawable = array.getDrawable(R.styleable.ViewfinderView_pointDrawable);
Drawable laserDrawable = array.getDrawable(R.styleable.ViewfinderView_laserDrawable);
viewfinderStyle = array.getInt(R.styleable.ViewfinderView_viewfinderStyle, ViewfinderStyle.CLASSIC);
cornerRectWidth = (int)array.getDimension(R.styleable.ViewfinderView_cornerRectWidth,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,4,getResources().getDisplayMetrics()));
cornerRectHeight = (int)array.getDimension(R.styleable.ViewfinderView_cornerRectHeight,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,16,getResources().getDisplayMetrics()));
scannerLineMoveDistance = (int)array.getDimension(R.styleable.ViewfinderView_scannerLineMoveDistance,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,2,getResources().getDisplayMetrics()));
scannerLineHeight = (int)array.getDimension(R.styleable.ViewfinderView_scannerLineHeight,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,5,getResources().getDisplayMetrics()));
frameLineWidth = (int)array.getDimension(R.styleable.ViewfinderView_frameLineWidth,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics()));
scannerAnimationDelay = array.getInteger(R.styleable.ViewfinderView_scannerAnimationDelay,20);
frameRatio = array.getFloat(R.styleable.ViewfinderView_frameRatio,0.625f);
framePaddingLeft = array.getDimension(R.styleable.ViewfinderView_framePaddingLeft,0);
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(); array.recycle();
pointColor = laserColor; if (pointDrawable != null) {
pointStrokeColor = Color.WHITE; pointBitmap = getBitmapFormDrawable(pointDrawable);
pointRangeRadius = (pointBitmap.getWidth() + pointBitmap.getHeight()) / 4 * DEFAULT_RANGE_RATIO;
} else {
pointStrokeRadius = pointRadius * pointStrokeRatio;
pointRangeRadius = pointStrokeRadius * DEFAULT_RANGE_RATIO;
}
if (laserDrawable != null) {
laserBitmap = getBitmapFormDrawable(laserDrawable);
}
pointRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,getResources().getDisplayMetrics());
paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (isShowPoints && checkSingleTap(e.getX(), e.getY())) {
return true;
}
return super.onSingleTapUp(e);
}
});
} }
private DisplayMetrics getDisplayMetrics(){ private Bitmap getBitmapFormDrawable(@NonNull Drawable drawable) {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
drawable.draw(canvas);
return bitmap;
}
private DisplayMetrics getDisplayMetrics() {
return getResources().getDisplayMetrics(); return getResources().getDisplayMetrics();
} }
@@ -322,48 +421,52 @@ public class ViewfinderView extends View {
this.labelTextColor = color; this.labelTextColor = color;
} }
public void setLabelTextColorResource(@ColorRes int id){ public void setLabelTextColorResource(@ColorRes int id) {
this.labelTextColor = ContextCompat.getColor(getContext(),id); this.labelTextColor = ContextCompat.getColor(getContext(), id);
} }
public void setLabelTextSize(float textSize) { public void setLabelTextSize(float textSize) {
this.labelTextSize = textSize; this.labelTextSize = textSize;
} }
public void setLaserStyle(LaserStyle laserStyle) {
this.laserStyle = laserStyle;
}
public void setPointImageResource(@DrawableRes int drawable) {
setPointBitmap(BitmapFactory.decodeResource(getResources(), drawable));
}
public void setPointBitmap(Bitmap bitmap) {
pointBitmap = bitmap;
pointRangeRadius = (pointBitmap.getWidth() + pointBitmap.getHeight()) / 4 * DEFAULT_RANGE_RATIO;
}
@Override @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom); super.onLayout(changed, left, top, right, bottom);
LogUtils.d("onLayout" + getWidth() + "," + getHeight());
initFrame(getWidth(),getHeight()); initFrame(getWidth(),getHeight());
} }
@Override private void initFrame(int width, int height) {
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
LogUtils.d("onSizeChanged" + w + "," + h);
} int size = (int) (Math.min(width, height) * frameRatio);
if (frameWidth <= 0 || frameWidth > width) {
private void initFrame(int width,int height){
int size = (int)(Math.min(width,height) * frameRatio);
if(frameWidth <= 0 || frameWidth > width){
frameWidth = size; frameWidth = size;
} }
if(frameHeight <= 0 || frameHeight > height){ if (frameHeight <= 0 || frameHeight > height) {
frameHeight = size; frameHeight = size;
} }
if(labelTextWidth <= 0){ if (labelTextWidth <= 0) {
labelTextWidth = width - getPaddingLeft() - getPaddingRight(); labelTextWidth = width - getPaddingLeft() - getPaddingRight();
} }
float leftOffsets = (width - frameWidth) / 2 + framePaddingLeft - framePaddingRight; float leftOffsets = (width - frameWidth) / 2 + framePaddingLeft - framePaddingRight;
float topOffsets = (height - frameHeight) / 2 + framePaddingTop - framePaddingBottom; float topOffsets = (height - frameHeight) / 2 + framePaddingTop - framePaddingBottom;
switch (frameGravity){ switch (frameGravity) {
case LEFT: case LEFT:
leftOffsets = framePaddingLeft; leftOffsets = framePaddingLeft;
break; break;
@@ -374,57 +477,74 @@ public class ViewfinderView extends View {
leftOffsets = width - frameWidth + framePaddingRight; leftOffsets = width - frameWidth + framePaddingRight;
break; break;
case BOTTOM: case BOTTOM:
topOffsets = height - frameHeight + framePaddingBottom; topOffsets = height - frameHeight + framePaddingBottom;
break; break;
} }
frame = new Rect((int)leftOffsets, (int)topOffsets, (int)leftOffsets + frameWidth, (int)topOffsets + frameHeight); frame = new Rect((int) leftOffsets, (int) topOffsets, (int) leftOffsets + frameWidth, (int) topOffsets + frameHeight);
} }
@Override @Override
public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) {
if (isShowPoints) {
// 显示结果点
drawMask(canvas, getWidth(), getHeight());
drawResultPoints(canvas, pointList);
if (isShowPointAnim && pointBitmap == null) {
// 显示动画并且结果点标记的图片为空时,支持缩放动画
calcZoomPointAnim();
}
return;
}
if (frame == null) { if (frame == null) {
return; return;
} }
if(scannerStart == 0 || scannerEnd == 0) { if (scannerStart == 0 || scannerEnd == 0) {
scannerStart = frame.top; scannerStart = frame.top;
scannerEnd = frame.bottom - scannerLineHeight; scannerEnd = frame.bottom - scannerLineHeight;
} }
int width = canvas.getWidth(); if (viewfinderStyle == ViewfinderStyle.CLASSIC) {// CLASSIC样式经典样式带扫码框
int height = canvas.getHeight(); // 绘制模糊区域
drawExterior(canvas, frame, getWidth(), getHeight());
// 绘制扫描动画
drawLaserScanner(canvas, frame);
// 绘制取景区域框
drawFrame(canvas, frame);
// 绘制取景区域边角
drawCorner(canvas, frame);
// 绘制提示信息
drawTextInfo(canvas, frame);
// 间隔更新取景区域
postInvalidateDelayed(scannerAnimationDelay, frame.left, frame.top, frame.right, frame.bottom);
} else if (viewfinderStyle == ViewfinderStyle.POPULAR) {// POPULAR样式类似于新版的微信全屏扫码不带扫码框
// 绘制扫描动画
drawLaserScanner(canvas, frame);
postInvalidateDelayed(scannerAnimationDelay);
}
// 绘制模糊区域
drawExterior(canvas,frame,width,height);
// 绘制扫描动画
drawLaserScanner(canvas,frame);
// 绘制取景区域框
drawFrame(canvas, frame);
// 绘制取景区域边角
drawCorner(canvas, frame);
//绘制提示信息
drawTextInfo(canvas, frame);
// 间隔更新取景区域
postInvalidateDelayed(scannerAnimationDelay, frame.left, frame.top, frame.right, frame.bottom);
} }
/** /**
* 绘制文本 * 绘制文本
*
* @param canvas * @param canvas
* @param frame * @param frame
*/ */
private void drawTextInfo(Canvas canvas, Rect frame) { private void drawTextInfo(Canvas canvas, Rect frame) {
if(!TextUtils.isEmpty(labelText)){ if (!TextUtils.isEmpty(labelText)) {
textPaint.setColor(labelTextColor); textPaint.setColor(labelTextColor);
textPaint.setTextSize(labelTextSize); textPaint.setTextSize(labelTextSize);
textPaint.setTextAlign(Paint.Align.CENTER); textPaint.setTextAlign(Paint.Align.CENTER);
StaticLayout staticLayout = new StaticLayout(labelText,textPaint,labelTextWidth, Layout.Alignment.ALIGN_NORMAL,1.2f,0.0f,true);
if(labelTextLocation == TextLocation.BOTTOM){ StaticLayout staticLayout = new StaticLayout(labelText, textPaint, labelTextWidth, Layout.Alignment.ALIGN_NORMAL, 1.2f, 0.0f, true);
canvas.translate(frame.left + frame.width() / 2,frame.bottom + labelTextPadding); if (labelTextLocation == TextLocation.BOTTOM) {
}else{ canvas.translate(frame.left + frame.width() / 2, frame.bottom + labelTextPadding);
canvas.translate(frame.left + frame.width() / 2,frame.top - labelTextPadding - staticLayout.getHeight()); } else {
canvas.translate(frame.left + frame.width() / 2, frame.top - labelTextPadding - staticLayout.getHeight());
} }
staticLayout.draw(canvas); staticLayout.draw(canvas);
} }
@@ -433,39 +553,65 @@ public class ViewfinderView extends View {
/** /**
* 绘制边角 * 绘制边角
*
* @param canvas * @param canvas
* @param frame * @param frame
*/ */
private void drawCorner(Canvas canvas, Rect frame) { private void drawCorner(Canvas canvas, Rect frame) {
paint.setColor(cornerColor); paint.setColor(cornerColor);
//左上 // 左上
canvas.drawRect(frame.left, frame.top, frame.left + cornerRectWidth, frame.top + cornerRectHeight, paint); canvas.drawRect(frame.left, frame.top, frame.left + cornerRectWidth, frame.top + cornerRectHeight, paint);
canvas.drawRect(frame.left, frame.top, frame.left + cornerRectHeight, frame.top + cornerRectWidth, paint); canvas.drawRect(frame.left, frame.top, frame.left + cornerRectHeight, frame.top + cornerRectWidth, paint);
//右上 // 右上
canvas.drawRect(frame.right - cornerRectWidth, frame.top, frame.right, frame.top + cornerRectHeight, paint); canvas.drawRect(frame.right - cornerRectWidth, frame.top, frame.right, frame.top + cornerRectHeight, paint);
canvas.drawRect(frame.right - cornerRectHeight, frame.top, frame.right, frame.top + cornerRectWidth, paint); canvas.drawRect(frame.right - cornerRectHeight, frame.top, frame.right, frame.top + cornerRectWidth, paint);
//左下 // 左下
canvas.drawRect(frame.left, frame.bottom - cornerRectWidth, frame.left + cornerRectHeight, frame.bottom, paint); canvas.drawRect(frame.left, frame.bottom - cornerRectWidth, frame.left + cornerRectHeight, frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - cornerRectHeight, frame.left + cornerRectWidth, frame.bottom, paint); canvas.drawRect(frame.left, frame.bottom - cornerRectHeight, frame.left + cornerRectWidth, frame.bottom, paint);
//右下 // 右下
canvas.drawRect(frame.right - cornerRectWidth, frame.bottom - cornerRectHeight, frame.right, frame.bottom, paint); canvas.drawRect(frame.right - cornerRectWidth, frame.bottom - cornerRectHeight, frame.right, frame.bottom, paint);
canvas.drawRect(frame.right - cornerRectHeight, frame.bottom - cornerRectWidth, frame.right, frame.bottom, paint); canvas.drawRect(frame.right - cornerRectHeight, frame.bottom - cornerRectWidth, frame.right, frame.bottom, paint);
} }
/**
* 绘制扫码动画
*
* @param canvas
* @param frame
*/
private void drawImageScanner(Canvas canvas, Rect frame) {
if (laserBitmap != null) {
paint.setColor(Color.WHITE);
canvas.drawBitmap(laserBitmap, frame.left, scannerStart, paint);
if (scannerStart < scannerEnd) {
scannerStart += scannerLineMoveDistance;
} else {
scannerStart = frame.top;
}
} else {
drawLineScanner(canvas, frame);
}
}
/** /**
* 绘制激光扫描线 * 绘制激光扫描线
*
* @param canvas * @param canvas
* @param frame * @param frame
*/ */
private void drawLaserScanner(Canvas canvas, Rect frame) { private void drawLaserScanner(Canvas canvas, Rect frame) {
if(laserStyle != null){ if (laserStyle != null) {
paint.setColor(laserColor); paint.setColor(laserColor);
switch (laserStyle){ switch (laserStyle) {
case LINE://线 case LINE:// 线
drawLineScanner(canvas,frame); drawLineScanner(canvas, frame);
break; break;
case GRID://网格 case GRID:// 网格
drawGridScanner(canvas,frame); drawGridScanner(canvas, frame);
break;
case IMAGE:// 图片
drawImageScanner(canvas, frame);
break; break;
} }
paint.setShader(null); paint.setShader(null);
@@ -474,11 +620,12 @@ public class ViewfinderView extends View {
/** /**
* 绘制线性式扫描 * 绘制线性式扫描
*
* @param canvas * @param canvas
* @param frame * @param frame
*/ */
private void drawLineScanner(Canvas canvas,Rect frame){ private void drawLineScanner(Canvas canvas, Rect frame) {
//线性渐变 // 线性渐变
LinearGradient linearGradient = new LinearGradient( LinearGradient linearGradient = new LinearGradient(
frame.left, scannerStart, frame.left, scannerStart,
frame.left, scannerStart + scannerLineHeight, frame.left, scannerStart + scannerLineHeight,
@@ -487,8 +634,8 @@ public class ViewfinderView extends View {
Shader.TileMode.MIRROR); Shader.TileMode.MIRROR);
paint.setShader(linearGradient); paint.setShader(linearGradient);
if(scannerStart <= scannerEnd) { if (scannerStart < scannerEnd) {
//椭圆 // 椭圆
RectF rectF = new RectF(frame.left + 2 * scannerLineHeight, scannerStart, frame.right - 2 * scannerLineHeight, scannerStart + scannerLineHeight); RectF rectF = new RectF(frame.left + 2 * scannerLineHeight, scannerStart, frame.right - 2 * scannerLineHeight, scannerStart + scannerLineHeight);
canvas.drawOval(rectF, paint); canvas.drawOval(rectF, paint);
scannerStart += scannerLineMoveDistance; scannerStart += scannerLineMoveDistance;
@@ -499,34 +646,35 @@ public class ViewfinderView extends View {
/** /**
* 绘制网格式扫描 * 绘制网格式扫描
*
* @param canvas * @param canvas
* @param frame * @param frame
*/ */
private void drawGridScanner(Canvas canvas,Rect frame){ private void drawGridScanner(Canvas canvas, Rect frame) {
int stroke = 2; int stroke = 2;
paint.setStrokeWidth(stroke); paint.setStrokeWidth(stroke);
//计算Y轴开始位置 // 计算Y轴开始位置
int startY = gridHeight > 0 && scannerStart - frame.top > gridHeight ? scannerStart - gridHeight : frame.top; int startY = gridHeight > 0 && scannerStart - frame.top > gridHeight ? scannerStart - gridHeight : frame.top;
LinearGradient linearGradient = new LinearGradient(frame.left + frame.width()/2, startY, frame.left + frame.width()/2, scannerStart, new int[]{shadeColor(laserColor), laserColor}, new float[]{0,1f}, LinearGradient.TileMode.CLAMP); LinearGradient linearGradient = new LinearGradient(frame.left + frame.width() / 2, startY, frame.left + frame.width() / 2, scannerStart, new int[]{shadeColor(laserColor), laserColor}, new float[]{0, 1f}, LinearGradient.TileMode.CLAMP);
//给画笔设置着色器 // 给画笔设置着色器
paint.setShader(linearGradient); paint.setShader(linearGradient);
float wUnit = frame.width() * 1.0f/ gridColumn; float wUnit = frame.width() * 1.0f / gridColumn;
float hUnit = wUnit; float hUnit = wUnit;
//遍历绘制网格纵线 // 遍历绘制网格纵线
for (int i = 1; i < gridColumn; i++) { for (int i = 1; i < gridColumn; i++) {
canvas.drawLine(frame.left + i * wUnit, startY,frame.left + i * wUnit, scannerStart,paint); canvas.drawLine(frame.left + i * wUnit, startY, frame.left + i * wUnit, scannerStart, paint);
} }
int height = gridHeight > 0 && scannerStart - frame.top > gridHeight ? gridHeight : scannerStart - frame.top; int height = gridHeight > 0 && scannerStart - frame.top > gridHeight ? gridHeight : scannerStart - frame.top;
//遍历绘制网格横线 // 遍历绘制网格横线
for (int i = 0; i <= height/hUnit; i++) { for (int i = 0; i <= height / hUnit; i++) {
canvas.drawLine(frame.left, scannerStart - i * hUnit,frame.right, scannerStart - i * hUnit,paint); canvas.drawLine(frame.left, scannerStart - i * hUnit, frame.right, scannerStart - i * hUnit, paint);
} }
if(scannerStart<scannerEnd){ if (scannerStart < scannerEnd) {
scannerStart += scannerLineMoveDistance; scannerStart += scannerLineMoveDistance;
} else { } else {
scannerStart = frame.top; scannerStart = frame.top;
@@ -536,17 +684,19 @@ public class ViewfinderView extends View {
/** /**
* 处理颜色模糊 * 处理颜色模糊
*
* @param color * @param color
* @return * @return
*/ */
public int shadeColor(int color) { public int shadeColor(int color) {
String hax = Integer.toHexString(color); String hax = Integer.toHexString(color);
String result = "01"+hax.substring(2); String result = "01" + hax.substring(2);
return Integer.valueOf(result, 16); return Integer.valueOf(result, 16);
} }
/** /**
* 绘制扫描区边框 * 绘制扫描区边框
*
* @param canvas * @param canvas
* @param frame * @param frame
*/ */
@@ -560,13 +710,14 @@ public class ViewfinderView extends View {
/** /**
* 绘制模糊区域 * 绘制模糊区域
*
* @param canvas * @param canvas
* @param frame * @param frame
* @param width * @param width
* @param height * @param height
*/ */
private void drawExterior(Canvas canvas, Rect frame, int width, int height) { private void drawExterior(Canvas canvas, Rect frame, int width, int height) {
if(maskColor != 0){ if (maskColor != 0) {
paint.setColor(maskColor); paint.setColor(maskColor);
canvas.drawRect(0, 0, width, frame.top, paint); canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom, paint); canvas.drawRect(0, frame.top, frame.left, frame.bottom, paint);
@@ -575,16 +726,173 @@ public class ViewfinderView extends View {
} }
} }
/**
* 绘制遮罩层
*
* @param canvas
* @param width
* @param height
*/
private void drawMask(Canvas canvas, int width, int height) {
if (maskColor != 0) {
paint.setColor(maskColor);
canvas.drawRect(0, 0, width, height, paint);
}
}
public void drawViewfinder() { /**
* 根据结果点集合绘制结果点
*
* @param canvas
* @param points
*/
private void drawResultPoints(Canvas canvas, List<Point> points) {
paint.setColor(Color.WHITE);
if (points != null) {
for (Point point : points) {
drawResultPoint(canvas, point, currentZoomRatio);
}
}
}
/**
* 计算点的缩放动画
*/
private void calcZoomPointAnim() {
if (currentZoomRatio <= 1F) {
lastZoomRatio = currentZoomRatio;
currentZoomRatio += zoomSpeed;
if (zoomCount < 2) {
// 记住缩放回合次数
zoomCount++;
} else {
zoomCount = 0;
}
} else if (currentZoomRatio >= MAX_ZOOM_RATIO) {
lastZoomRatio = currentZoomRatio;
currentZoomRatio -= zoomSpeed;
} else {
if (lastZoomRatio > currentZoomRatio) {
lastZoomRatio = currentZoomRatio;
currentZoomRatio -= zoomSpeed;
} else {
lastZoomRatio = currentZoomRatio;
currentZoomRatio += zoomSpeed;
}
}
// 每间隔3秒触发一套缩放动画一套动画缩放三个回合(即每次zoomCount累加到2后重置为0时)
postInvalidateDelayed(zoomCount == 0 && lastZoomRatio == 1f ? 3000 : scannerAnimationDelay * 2);
}
/**
* 绘制结果点
*
* @param canvas
* @param point
*/
private void drawResultPoint(Canvas canvas, Point point, float currentZoomRatio) {
if (pointBitmap != null) {
float left = point.x - pointBitmap.getWidth() / 2.0f;
float top = point.y - pointBitmap.getHeight() / 2.0f;
canvas.drawBitmap(pointBitmap, left, top, paint);
} else {
paint.setColor(pointStrokeColor);
canvas.drawCircle(point.x, point.y, pointStrokeRadius * currentZoomRatio, paint);
paint.setColor(pointColor);
canvas.drawCircle(point.x, point.y, pointRadius * currentZoomRatio, paint);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isShowPoints) {
gestureDetector.onTouchEvent(event);
}
return isShowPoints || super.onTouchEvent(event);
}
private boolean checkSingleTap(float x, float y) {
if (pointList != null) {
for (int i = 0; i < pointList.size(); i++) {
Point point = pointList.get(i);
float distance = getDistance(x, y, point.x, point.y);
if (distance <= pointRangeRadius) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(i);
}
return true;
}
}
}
return true;
}
/**
* 获取两点之间的距离
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
private float getDistance(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
/**
* 是否显示结果点
*
* @return
*/
public boolean isShowPoints() {
return isShowPoints;
}
/**
* 显示扫码动画
*/
public void showScanner() {
isShowPoints = false;
invalidate(); invalidate();
} }
public void setLaserStyle(LaserStyle laserStyle) { /**
this.laserStyle = laserStyle; * 显示结果点
*
* @param points
*/
public void showResultPoints(List<Point> points) {
pointList = points;
isShowPoints = true;
zoomCount = 0;
lastZoomRatio = 0;
currentZoomRatio = 1;
invalidate();
} }
/**
* 设置点击Item监听
*
* @param listener
*/
public void setOnItemClickListener(OnItemClickListener listener) {
onItemClickListener = listener;
}
/**
* Item点击监听
*/
public interface OnItemClickListener {
void onItemClick(int position);
}
} }

View File

@@ -1,24 +1,27 @@
package com.king.zxing.analyze; package com.king.zxing.analyze;
import android.content.res.Configuration;
import com.google.zxing.Result; import com.google.zxing.Result;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.camera.core.ImageProxy; import androidx.camera.core.ImageProxy;
import android.content.res.Configuration;
/** /**
* 分析器 * 分析器
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public interface Analyzer { public interface Analyzer {
/** /**
* Analyzes an image to produce a result. * Analyzes an image to produce a result.
* @param image The image to analyze *
* @param image The image to analyze
* @param orientation {@link Configuration#ORIENTATION_LANDSCAPE}, {@link Configuration#ORIENTATION_PORTRAIT}. * @param orientation {@link Configuration#ORIENTATION_LANDSCAPE}, {@link Configuration#ORIENTATION_PORTRAIT}.
* @return * @return
*/ */
@Nullable @Nullable
Result analyze(@NonNull ImageProxy image,int orientation); Result analyze(@NonNull ImageProxy image, int orientation);
} }

View File

@@ -13,26 +13,28 @@ import java.util.Map;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
/** /**
* 矩阵区域分析器:主要用于锁定具体的识别区域
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public abstract class AreaRectAnalyzer extends ImageAnalyzer { public abstract class AreaRectAnalyzer extends ImageAnalyzer {
DecodeConfig mDecodeConfig; DecodeConfig mDecodeConfig;
Map<DecodeHintType,?> mHints; Map<DecodeHintType, ?> mHints;
boolean isMultiDecode = true; boolean isMultiDecode = true;
private float mAreaRectRatio = DecodeConfig.DEFAULT_AREA_RECT_RATIO; private float mAreaRectRatio = DecodeConfig.DEFAULT_AREA_RECT_RATIO;
private int mAreaRectHorizontalOffset = 0; private int mAreaRectHorizontalOffset = 0;
private int mAreaRectVerticalOffset = 0; private int mAreaRectVerticalOffset = 0;
public AreaRectAnalyzer(@Nullable DecodeConfig config){ public AreaRectAnalyzer(@Nullable DecodeConfig config) {
this.mDecodeConfig = config; this.mDecodeConfig = config;
if(config != null){ if (config != null) {
mHints = config.getHints(); mHints = config.getHints();
isMultiDecode = config.isMultiDecode(); isMultiDecode = config.isMultiDecode();
mAreaRectRatio = config.getAreaRectRatio(); mAreaRectRatio = config.getAreaRectRatio();
mAreaRectHorizontalOffset = config.getAreaRectHorizontalOffset(); mAreaRectHorizontalOffset = config.getAreaRectHorizontalOffset();
mAreaRectVerticalOffset = config.getAreaRectVerticalOffset(); mAreaRectVerticalOffset = config.getAreaRectVerticalOffset();
}else{ } else {
mHints = DecodeFormatManager.DEFAULT_HINTS; mHints = DecodeFormatManager.DEFAULT_HINTS;
} }
@@ -41,28 +43,28 @@ public abstract class AreaRectAnalyzer extends ImageAnalyzer {
@Nullable @Nullable
@Override @Override
public Result analyze(byte[] data, int width, int height) { public Result analyze(byte[] data, int width, int height) {
if(mDecodeConfig != null){ if (mDecodeConfig != null) {
if(mDecodeConfig.isFullAreaScan()){ if (mDecodeConfig.isFullAreaScan()) {
//mDecodeConfig为空或者支持全区域扫码识别时直接使用全区域进行扫码识别 // mDecodeConfig为空或者支持全区域扫码识别时直接使用全区域进行扫码识别
return analyze(data,width,height,0,0,width,height); return analyze(data, width, height, 0, 0, width, height);
} }
Rect rect = mDecodeConfig.getAnalyzeAreaRect(); Rect rect = mDecodeConfig.getAnalyzeAreaRect();
if(rect != null){//如果分析区域不为空,则使用指定的区域进行扫码识别 if (rect != null) {// 如果分析区域不为空,则使用指定的区域进行扫码识别
return analyze(data,width,height,rect.left,rect.top,rect.width(),rect.height()); return analyze(data, width, height, rect.left, rect.top, rect.width(), rect.height());
} }
} }
//如果分析区域为空,则通过识别区域比例和相关的偏移量计算出最终的区域进行扫码识别 // 如果分析区域为空,则通过识别区域比例和相关的偏移量计算出最终的区域进行扫码识别
int size = (int)(Math.min(width,height) * mAreaRectRatio); int size = (int) (Math.min(width, height) * mAreaRectRatio);
int left = (width-size)/2 + mAreaRectHorizontalOffset; int left = (width - size) / 2 + mAreaRectHorizontalOffset;
int top = (height-size)/2 + mAreaRectVerticalOffset; int top = (height - size) / 2 + mAreaRectVerticalOffset;
return analyze(data,width,height,left,top,size,size); return analyze(data, width, height, left, top, size, size);
} }
@Nullable @Nullable
public abstract Result analyze(byte[] data, int dataWidth, int dataHeight,int left,int top,int width,int height); public abstract Result analyze(byte[] data, int dataWidth, int dataHeight, int left, int top, int width, int height);
} }

View File

@@ -11,23 +11,24 @@ import com.google.zxing.common.HybridBinarizer;
import com.king.zxing.DecodeConfig; import com.king.zxing.DecodeConfig;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.util.Map; import java.util.Map;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
/** /**
* 多格式分析器:主要用于分析识别条形码/二维码
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class MultiFormatAnalyzer extends AreaRectAnalyzer { public class MultiFormatAnalyzer extends AreaRectAnalyzer {
MultiFormatReader mReader; MultiFormatReader mReader;
public MultiFormatAnalyzer(){ public MultiFormatAnalyzer() {
this((DecodeConfig)null); this((DecodeConfig) null);
} }
public MultiFormatAnalyzer(@Nullable Map<DecodeHintType,Object> hints){ public MultiFormatAnalyzer(@Nullable Map<DecodeHintType, Object> hints) {
this(new DecodeConfig().setHints(hints)); this(new DecodeConfig().setHints(hints));
} }
@@ -36,62 +37,62 @@ public class MultiFormatAnalyzer extends AreaRectAnalyzer {
initReader(); initReader();
} }
private void initReader(){ private void initReader() {
mReader = new MultiFormatReader(); mReader = new MultiFormatReader();
} }
@Nullable @Nullable
@Override @Override
public Result analyze(byte[] data, int dataWidth, int dataHeight,int left,int top,int width,int height) { public Result analyze(byte[] data, int dataWidth, int dataHeight, int left, int top, int width, int height) {
Result rawResult = null; Result rawResult = null;
try { try {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
mReader.setHints(mHints); mReader.setHints(mHints);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data,dataWidth,dataHeight,left,top,width,height,false); PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data, dataWidth, dataHeight, left, top, width, height, false);
rawResult = decodeInternal(source,isMultiDecode); rawResult = decodeInternal(source, isMultiDecode);
if(rawResult == null && mDecodeConfig != null){ if (rawResult == null && mDecodeConfig != null) {
if(rawResult == null && mDecodeConfig.isSupportVerticalCode()){ if (rawResult == null && mDecodeConfig.isSupportVerticalCode()) {
byte[] rotatedData = new byte[data.length]; byte[] rotatedData = new byte[data.length];
for (int y = 0; y < dataHeight; y++) { 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]; rotatedData[x * dataHeight + dataHeight - y - 1] = data[x + y * dataWidth];
} }
} }
rawResult = decodeInternal(new PlanarYUVLuminanceSource(rotatedData,dataHeight,dataWidth,top,left,height,width,false),mDecodeConfig.isSupportVerticalCodeMultiDecode()); rawResult = decodeInternal(new PlanarYUVLuminanceSource(rotatedData, dataHeight, dataWidth, top, left, height, width, false), mDecodeConfig.isSupportVerticalCodeMultiDecode());
} }
if(rawResult == null && mDecodeConfig.isSupportLuminanceInvert()){ if (rawResult == null && mDecodeConfig.isSupportLuminanceInvert()) {
rawResult = decodeInternal(source.invert(),mDecodeConfig.isSupportLuminanceInvertMultiDecode()); rawResult = decodeInternal(source.invert(), mDecodeConfig.isSupportLuminanceInvertMultiDecode());
} }
} }
if(rawResult != null){ if (rawResult != null) {
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
LogUtils.d("Found barcode in " + (end - start) + " ms"); LogUtils.d("Found barcode in " + (end - start) + " ms");
} }
} catch (Exception e) { } catch (Exception e) {
}finally { } finally {
mReader.reset(); mReader.reset();
} }
return rawResult; return rawResult;
} }
private Result decodeInternal(LuminanceSource source,boolean isMultiDecode){ private Result decodeInternal(LuminanceSource source, boolean isMultiDecode) {
Result result = null; Result result = null;
try{ try {
try{ try {
//采用HybridBinarizer解析 // 采用HybridBinarizer解析
result = mReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source))); result = mReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
}catch (Exception e){ } catch (Exception e) {
} }
if(isMultiDecode && result == null){ if (isMultiDecode && result == null) {
//如果没有解析成功再采用GlobalHistogramBinarizer解析一次 // 如果没有解析成功再采用GlobalHistogramBinarizer解析一次
result = mReader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source))); result = mReader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
} }
}catch (Exception e){ } catch (Exception e) {
} }
return result; return result;

View File

@@ -3,7 +3,6 @@ package com.king.zxing.config;
import android.content.Context; import android.content.Context;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -13,7 +12,8 @@ import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview; import androidx.camera.core.Preview;
/** /**
* 相机配置:根据纵横比配置相机,使输出分析的图像尽可能的接近屏幕比例 * 相机配置:根据纵横比配置相机,使输出分析的图像尽可能的接近屏幕比例
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public final class AspectRatioCameraConfig extends CameraConfig { public final class AspectRatioCameraConfig extends CameraConfig {
@@ -22,22 +22,26 @@ public final class AspectRatioCameraConfig extends CameraConfig {
public AspectRatioCameraConfig(Context context) { public AspectRatioCameraConfig(Context context) {
super(); super();
initTargetAspectRatio(context);
}
/**
* 初始化 {@link #mAspectRatio}
*
* @param context
*/
private void initTargetAspectRatio(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int width = displayMetrics.widthPixels; int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels; int height = displayMetrics.heightPixels;
mAspectRatio = aspectRatio(width, height);
LogUtils.d("aspectRatio:" + mAspectRatio);
}
private int aspectRatio(float width, float height){
float ratio = Math.max(width, height) / Math.min(width, height); float ratio = Math.max(width, height) / Math.min(width, height);
if (Math.abs(ratio - 4.0f / 3.0f) < Math.abs(ratio - 16.0f / 9.0f)) { if (Math.abs(ratio - 4.0F / 3.0F) < Math.abs(ratio - 16.0F / 9.0F)) {
return AspectRatio.RATIO_4_3; mAspectRatio = AspectRatio.RATIO_4_3;
} else {
mAspectRatio = AspectRatio.RATIO_16_9;
} }
return AspectRatio.RATIO_16_9; LogUtils.d("aspectRatio:" + mAspectRatio);
} }
@NonNull @NonNull
@@ -59,3 +63,4 @@ public final class AspectRatioCameraConfig extends CameraConfig {
return super.options(builder); return super.options(builder);
} }
} }

View File

@@ -6,28 +6,75 @@ import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview; import androidx.camera.core.Preview;
/** /**
* 相机配置:主要用于提供相机预览时可自定义一些配置,便于扩展 * 相机配置:主要用于提供相机预览时可自定义一些配置,便于拓展;
* <p>
* 库中内置实现{@link CameraConfig}的有{@link AspectRatioCameraConfig}和{@link ResolutionCameraConfig}
* <p>
* 这里简单说下各自的特点:
* <p>
* {@link CameraConfig} - 默认的相机配置
* <p>
* {@link AspectRatioCameraConfig} - 根据纵横比配置相机,使输出分析的图像尽可能的接近屏幕的比例
* <p>
* {@link ResolutionCameraConfig} - 根据尺寸配置相机的目标图像大小,使输出分析的图像的分辨率尽可能的接近屏幕尺寸
* <p>
* 当使用默认的 {@link CameraConfig}在某些机型上体验欠佳时,你可以尝试使用{@link AspectRatioCameraConfig}或
* {@link ResolutionCameraConfig}会有意想不到奇效。
* <p>
* 你也可以自定义或覆写 {@link CameraConfig} 中的 {@link #options} 方法,根据需要定制配置。
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class CameraConfig { public class CameraConfig {
public CameraConfig(){ public CameraConfig() {
} }
/**
* 配置 {@link Preview.Builder};可参考:{@link AspectRatioCameraConfig} 或 {@link ResolutionCameraConfig}
* <p>
* 如配置目标旋转角度为90度{@code builder.setTargetRotation(Surface.ROTATION_90)}
* <p>
* 切记,外部请勿直接调用 {@link #options(Preview.Builder)}
*
* @param builder
* @return
*/
@NonNull @NonNull
public Preview options(@NonNull Preview.Builder builder){ public Preview options(@NonNull Preview.Builder builder) {
return builder.build(); return builder.build();
} }
/**
* 配置 {@link CameraSelector.Builder};可参考:{@link AspectRatioCameraConfig} 或 {@link ResolutionCameraConfig}
* <p>
* 如配置前置摄像头:{@code builder.requireLensFacing(CameraSelector.LENS_FACING_FRONT)}
* <p>
* 切记,外部请勿直接调用 {@link #options(CameraSelector.Builder)}
*
* @param builder
* @return
*/
@NonNull @NonNull
public CameraSelector options(@NonNull CameraSelector.Builder builder){ public CameraSelector options(@NonNull CameraSelector.Builder builder) {
return builder.build(); return builder.build();
} }
/**
* 配置 {@link ImageAnalysis.Builder};可参考:{@link AspectRatioCameraConfig} 或 {@link ResolutionCameraConfig}
* <p>
* 如配置目标旋转角度为90度{@code builder.setTargetRotation(Surface.ROTATION_90)}
* <p>
* 切记,外部请勿直接调用 {@link #options(ImageAnalysis.Builder)}
*
* @param builder
* @return
*/
@NonNull @NonNull
public ImageAnalysis options(@NonNull ImageAnalysis.Builder builder){ public ImageAnalysis options(@NonNull ImageAnalysis.Builder builder) {
return builder.build(); return builder.build();
} }
} }

View File

@@ -4,53 +4,89 @@ import android.content.Context;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Size; import android.util.Size;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.util.Locale;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis; import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview; import androidx.camera.core.Preview;
/** /**
* 相机配置:根据尺寸配置相机的目标图像,使输出分析的图像尽可能的接近屏幕尺寸 * 相机配置:根据尺寸配置相机的目标图像大小,使输出分析的图像的分辨率尽可能的接近屏幕尺寸
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class ResolutionCameraConfig extends CameraConfig { public class ResolutionCameraConfig extends CameraConfig {
/**
* 1080P
*/
public static final int IMAGE_QUALITY_1080P = 1080;
/**
* 720P
*/
public static final int IMAGE_QUALITY_720P = 720;
private Size mTargetSize; private Size mTargetSize;
/**
* 构造
*
* @param context 上下文
*/
public ResolutionCameraConfig(Context context) { public ResolutionCameraConfig(Context context) {
super(); this(context, IMAGE_QUALITY_1080P);
}
/**
* 构造
*
* @param context 上下文
* @param imageQuality 图像质量;此参数只是期望的图像质量,最终以实际计算结果为准
*/
public ResolutionCameraConfig(Context context, int imageQuality) {
super();
initTargetResolutionSize(context, imageQuality);
}
/**
* 初始化 {@link #mTargetSize}
*
* @param context 上下文
* @param imageQuality 图像质量;此参数只是期望的图像质量,最终以实际计算结果为准
*/
private void initTargetResolutionSize(Context context, int imageQuality) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int width = displayMetrics.widthPixels; int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels; int height = displayMetrics.heightPixels;
LogUtils.d(String.format("displayMetrics:%d x %d",width,height)); LogUtils.d(String.format(Locale.getDefault(), "displayMetrics:%d x %d", width, height));
//因为为了保持流畅性和性能,限制在1080p在此前提下尽可能的找到屏幕接近的分辨率 // 因为为了保持流畅性和性能,尽可能的限制在imageQuality默认1080p,在此前提下尽可能的找到屏幕接近的分辨率
if(width < height){ if (width < height) {
int size = Math.min(width, 1080); int size = Math.min(width, imageQuality);
float ratio = width / (float)height; float ratio = width / (float) height;
if(ratio > 0.7){//一般应用于平板 if (ratio > 0.7F) {
mTargetSize = new Size(size, (int)(size / 3.0f * 4.0f)); // 一般应用于平板
}else{ mTargetSize = new Size(size, (int) (size / 3.0F * 4.0F));
mTargetSize = new Size(size, (int)(size / 9.0f * 16.0f)); } else {
mTargetSize = new Size(size, (int) (size / 9.0F * 16.0F));
} }
}else{ } else {
int size = Math.min(height, 1080); int size = Math.min(height, imageQuality);
float ratio = height / (float)width; float ratio = height / (float) width;
if(ratio > 0.7){//一般应用于平板 if (ratio > 0.7F) {
mTargetSize = new Size((int)(size / 3.0f * 4.0f), size); // 一般应用于平板
}else{ mTargetSize = new Size((int) (size / 3.0F * 4.0F), size);
mTargetSize = new Size((int)(size / 9.0f * 16.0), size); } else {
mTargetSize = new Size((int) (size / 9.0F * 16.0F), size);
} }
} }
LogUtils.d("targetSize:" + mTargetSize); LogUtils.d("targetSize:" + mTargetSize);
} }
@NonNull @NonNull
@Override @Override
public Preview options(@NonNull Preview.Builder builder) { public Preview options(@NonNull Preview.Builder builder) {

View File

@@ -1,22 +1,5 @@
package com.king.zxing.manager; package com.king.zxing.manager;
/*
* Copyright (C) 2012 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.Context; import android.content.Context;
import android.hardware.Sensor; import android.hardware.Sensor;
import android.hardware.SensorEvent; import android.hardware.SensorEvent;
@@ -30,8 +13,8 @@ public class AmbientLightManager implements SensorEventListener {
private static final int INTERVAL_TIME = 200; private static final int INTERVAL_TIME = 200;
protected static final float DARK_LUX = 45.0f; protected static final float DARK_LUX = 45.0F;
protected static final float BRIGHT_LUX = 100.0f; protected static final float BRIGHT_LUX = 100.0F;
/** /**
* 光线太暗时默认照度45 lux * 光线太暗时默认照度45 lux
@@ -72,9 +55,10 @@ public class AmbientLightManager implements SensorEventListener {
@Override @Override
public void onSensorChanged(SensorEvent sensorEvent) { public void onSensorChanged(SensorEvent sensorEvent) {
if(isLightSensorEnabled){ if (isLightSensorEnabled) {
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
if(currentTime - lastTime < INTERVAL_TIME){//降低频率 if (currentTime - lastTime < INTERVAL_TIME) {
// 降低频率
return; return;
} }
lastTime = currentTime; lastTime = currentTime;
@@ -83,9 +67,9 @@ public class AmbientLightManager implements SensorEventListener {
float lightLux = sensorEvent.values[0]; float lightLux = sensorEvent.values[0];
mOnLightSensorEventListener.onSensorChanged(lightLux); mOnLightSensorEventListener.onSensorChanged(lightLux);
if (lightLux <= darkLightLux) { if (lightLux <= darkLightLux) {
mOnLightSensorEventListener.onSensorChanged(true,lightLux); mOnLightSensorEventListener.onSensorChanged(true, lightLux);
} else if (lightLux >= brightLightLux) { } else if (lightLux >= brightLightLux) {
mOnLightSensorEventListener.onSensorChanged(false,lightLux); mOnLightSensorEventListener.onSensorChanged(false, lightLux);
} }
} }
} }
@@ -93,17 +77,19 @@ public class AmbientLightManager implements SensorEventListener {
/** /**
* 设置光线足够暗的阈值单位lux * 设置光线足够暗的阈值单位lux
*
* @param lightLux * @param lightLux
*/ */
public void setDarkLightLux(float lightLux){ public void setDarkLightLux(float lightLux) {
this.darkLightLux = lightLux; this.darkLightLux = lightLux;
} }
/** /**
* 设置光线足够明亮的阈值单位lux * 设置光线足够明亮的阈值单位lux
*
* @param lightLux * @param lightLux
*/ */
public void setBrightLightLux(float lightLux){ public void setBrightLightLux(float lightLux) {
this.brightLightLux = lightLux; this.brightLightLux = lightLux;
} }
@@ -118,6 +104,7 @@ public class AmbientLightManager implements SensorEventListener {
/** /**
* 设置是否启用光线亮度传感器 * 设置是否启用光线亮度传感器
*
* @param lightSensorEnabled * @param lightSensorEnabled
*/ */
public void setLightSensorEnabled(boolean lightSensorEnabled) { public void setLightSensorEnabled(boolean lightSensorEnabled) {
@@ -126,26 +113,27 @@ public class AmbientLightManager implements SensorEventListener {
/** /**
* 设置光线亮度传感器监听器,只有在 {@link #isLightSensorEnabled} 为{@code true} 才有效 * 设置光线亮度传感器监听器,只有在 {@link #isLightSensorEnabled} 为{@code true} 才有效
*
* @param listener * @param listener
*/ */
public void setOnLightSensorEventListener(OnLightSensorEventListener listener){ public void setOnLightSensorEventListener(OnLightSensorEventListener listener) {
mOnLightSensorEventListener = listener; mOnLightSensorEventListener = listener;
} }
public interface OnLightSensorEventListener{ public interface OnLightSensorEventListener {
/** /**
*
* @param lightLux 当前检测到的光线照度值 * @param lightLux 当前检测到的光线照度值
*/ */
default void onSensorChanged(float lightLux){ default void onSensorChanged(float lightLux) {
} }
/** /**
* 传感器改变事件 * 传感器改变事件
* @param dark 是否太暗了,当检测到的光线照度值小于{@link #darkLightLux}时,为{@code true} *
* @param dark 是否太暗了,当检测到的光线照度值小于{@link #darkLightLux}时,为{@code true}
* @param lightLux 当前检测到的光线照度值 * @param lightLux 当前检测到的光线照度值
*/ */
void onSensorChanged(boolean dark,float lightLux); void onSensorChanged(boolean dark, float lightLux);
} }
} }

View File

@@ -1,24 +1,7 @@
package com.king.zxing.manager; package com.king.zxing.manager;
/*
* Copyright (C) 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.Context; import android.content.Context;
import android.content.res.AssetFileDescriptor; import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.os.Build; import android.os.Build;
import android.os.VibrationEffect; import android.os.VibrationEffect;
@@ -28,7 +11,6 @@ import com.king.zxing.R;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.io.Closeable; import java.io.Closeable;
import java.lang.annotation.ElementType;
/** /**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
@@ -49,11 +31,11 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
updatePrefs(); updatePrefs();
} }
public void setVibrate(boolean vibrate){ public void setVibrate(boolean vibrate) {
this.vibrate = vibrate; this.vibrate = vibrate;
} }
public void setPlayBeep(boolean playBeep){ public void setPlayBeep(boolean playBeep) {
this.playBeep = playBeep; this.playBeep = playBeep;
} }
@@ -61,8 +43,8 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
if (mediaPlayer == null) { if (mediaPlayer == null) {
mediaPlayer = buildMediaPlayer(context); mediaPlayer = buildMediaPlayer(context);
} }
if(vibrator == null){ if (vibrator == null) {
vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
} }
} }
@@ -85,7 +67,6 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
AssetFileDescriptor file = context.getResources().openRawResourceFd(R.raw.zxl_beep); AssetFileDescriptor file = context.getResources().openRawResourceFd(R.raw.zxl_beep);
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
mediaPlayer.setOnErrorListener(this); mediaPlayer.setOnErrorListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(false); mediaPlayer.setLooping(false);
mediaPlayer.prepare(); mediaPlayer.prepare();
return mediaPlayer; return mediaPlayer;
@@ -105,12 +86,12 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
@Override @Override
public synchronized void close() { public synchronized void close() {
try{ try {
if (mediaPlayer != null) { if (mediaPlayer != null) {
mediaPlayer.release(); mediaPlayer.release();
mediaPlayer = null; mediaPlayer = null;
} }
}catch (Exception e){ } catch (Exception e) {
LogUtils.e(e); LogUtils.e(e);
} }
} }

View File

@@ -48,8 +48,9 @@ import com.king.zxing.DecodeFormatManager;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* 二维码/条形码工具类:主要包括二维码/条形码的解析与生成
*
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public final class CodeUtils { public final class CodeUtils {
@@ -57,108 +58,115 @@ public final class CodeUtils {
public static final int DEFAULT_REQ_WIDTH = 480; public static final int DEFAULT_REQ_WIDTH = 480;
public static final int DEFAULT_REQ_HEIGHT = 640; public static final int DEFAULT_REQ_HEIGHT = 640;
private CodeUtils(){ private CodeUtils() {
throw new AssertionError(); throw new AssertionError();
} }
/** /**
* 生成二维码 * 生成二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix) { public static Bitmap createQRCode(String content, int heightPix) {
return createQRCode(content,heightPix,null); return createQRCode(content, heightPix, null);
} }
/** /**
* 生成二维码 * 生成二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @param codeColor 二维码的颜色 * @param codeColor 二维码的颜色
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix,int codeColor) { public static Bitmap createQRCode(String content, int heightPix, int codeColor) {
return createQRCode(content,heightPix,null,codeColor); return createQRCode(content, heightPix, null, codeColor);
} }
/** /**
* 生成我二维码 * 生成我二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @param logo logo大小默认占二维码的20% * @param logo logo大小默认占二维码的20%
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo) { public static Bitmap createQRCode(String content, int heightPix, Bitmap logo) {
return createQRCode(content,heightPix,logo,Color.BLACK); return createQRCode(content, heightPix, logo, Color.BLACK);
} }
/** /**
* 生成我二维码 * 生成我二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @param logo logo大小默认占二维码的20% * @param logo logo大小默认占二维码的20%
* @param codeColor 二维码的颜色 * @param codeColor 二维码的颜色
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,int codeColor) { public static Bitmap createQRCode(String content, int heightPix, Bitmap logo, int codeColor) {
return createQRCode(content,heightPix,logo,0.2f,codeColor); return createQRCode(content, heightPix, logo, 0.2f, codeColor);
} }
/** /**
* 生成二维码 * 生成二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @param logo 二维码中间的logo * @param logo 二维码中间的logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3 * @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio) { public static Bitmap createQRCode(String content, int heightPix, Bitmap logo, @FloatRange(from = 0.0f, to = 1.0f) float ratio) {
//配置参数 //配置参数
Map<EncodeHintType, Object> hints = new HashMap<>(); Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put( EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别 //容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//设置空白边距的宽度 //设置空白边距的宽度
hints.put(EncodeHintType.MARGIN, 1); //default is 4 hints.put(EncodeHintType.MARGIN, 1); //default is 4
return createQRCode(content,heightPix,logo,ratio,hints); return createQRCode(content, heightPix, logo, ratio, hints);
} }
/** /**
* 生成二维码 * 生成二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @param logo 二维码中间的logo * @param logo 二维码中间的logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3 * @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @param codeColor 二维码的颜色 * @param codeColor 二维码的颜色
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,int codeColor) { public static Bitmap createQRCode(String content, int heightPix, Bitmap logo, @FloatRange(from = 0.0f, to = 1.0f) float ratio, int codeColor) {
//配置参数 //配置参数
Map<EncodeHintType, Object> hints = new HashMap<>(); Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put( EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别 //容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//设置空白边距的宽度 //设置空白边距的宽度
hints.put(EncodeHintType.MARGIN, 1); //default is 1 hints.put(EncodeHintType.MARGIN, 1); //default is 1
return createQRCode(content,heightPix,logo,ratio,hints,codeColor); return createQRCode(content, heightPix, logo, ratio, hints, codeColor);
} }
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map<EncodeHintType,?> hints) { public static Bitmap createQRCode(String content, int heightPix, Bitmap logo, @FloatRange(from = 0.0f, to = 1.0f) float ratio, Map<EncodeHintType, ?> hints) {
return createQRCode(content,heightPix,logo,ratio,hints,Color.BLACK); return createQRCode(content, heightPix, logo, ratio, hints, Color.BLACK);
} }
/** /**
* 生成二维码 * 生成二维码
* @param content 二维码的内容 *
* @param content 二维码的内容
* @param heightPix 二维码的高 * @param heightPix 二维码的高
* @param logo 二维码中间的logo * @param logo 二维码中间的logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3 * @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @param hints * @param hints
* @param codeColor 二维码的颜色 * @param codeColor 二维码的颜色
* @return * @return
*/ */
public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map<EncodeHintType,?> hints,int codeColor) { public static Bitmap createQRCode(String content, int heightPix, Bitmap logo, @FloatRange(from = 0.0f, to = 1.0f) float ratio, Map<EncodeHintType, ?> hints, int codeColor) {
try { try {
// 图像数据转换,使用了矩阵转换 // 图像数据转换,使用了矩阵转换
@@ -181,7 +189,7 @@ public final class CodeUtils {
bitmap.setPixels(pixels, 0, heightPix, 0, 0, heightPix, heightPix); bitmap.setPixels(pixels, 0, heightPix, 0, 0, heightPix, heightPix);
if (logo != null) { if (logo != null) {
bitmap = addLogo(bitmap, logo,ratio); bitmap = addLogo(bitmap, logo, ratio);
} }
return bitmap; return bitmap;
@@ -194,12 +202,13 @@ public final class CodeUtils {
/** /**
* 在二维码中间添加Logo图案 * 在二维码中间添加Logo图案
*
* @param src * @param src
* @param logo * @param logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3 * @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @return * @return
*/ */
private static Bitmap addLogo(Bitmap src, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f) float ratio) { private static Bitmap addLogo(Bitmap src, Bitmap logo, @FloatRange(from = 0.0f, to = 1.0f) float ratio) {
if (src == null) { if (src == null) {
return null; return null;
} }
@@ -243,12 +252,13 @@ public final class CodeUtils {
/** /**
* 解析二维码图片 * 解析二维码图片
*
* @param bitmapPath 需要解析的图片路径 * @param bitmapPath 需要解析的图片路径
* @return * @return
*/ */
public static String parseQRCode(String bitmapPath){ public static String parseQRCode(String bitmapPath) {
Result result = parseQRCodeResult(bitmapPath); Result result = parseQRCodeResult(bitmapPath);
if(result != null){ if (result != null) {
return result.getText(); return result.getText();
} }
return null; return null;
@@ -256,42 +266,46 @@ public final class CodeUtils {
/** /**
* 解析二维码图片 * 解析二维码图片
*
* @param bitmapPath 需要解析的图片路径 * @param bitmapPath 需要解析的图片路径
* @return * @return
*/ */
public static Result parseQRCodeResult(String bitmapPath){ public static Result parseQRCodeResult(String bitmapPath) {
return parseQRCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT); return parseQRCodeResult(bitmapPath, DEFAULT_REQ_WIDTH, DEFAULT_REQ_HEIGHT);
} }
/** /**
* 解析二维码图片 * 解析二维码图片
*
* @param bitmapPath 需要解析的图片路径 * @param bitmapPath 需要解析的图片路径
* @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理 * @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理
* @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理 * @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理
* @return * @return
*/ */
public static Result parseQRCodeResult(String bitmapPath,int reqWidth,int reqHeight){ public static Result parseQRCodeResult(String bitmapPath, int reqWidth, int reqHeight) {
return parseCodeResult(bitmapPath,reqWidth,reqHeight, DecodeFormatManager.QR_CODE_HINTS); return parseCodeResult(bitmapPath, reqWidth, reqHeight, DecodeFormatManager.QR_CODE_HINTS);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmapPath 需要解析的图片路径 * @param bitmapPath 需要解析的图片路径
* @return * @return
*/ */
public static String parseCode(String bitmapPath){ public static String parseCode(String bitmapPath) {
return parseCode(bitmapPath, DecodeFormatManager.ALL_HINTS); return parseCode(bitmapPath, DecodeFormatManager.ALL_HINTS);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmapPath 需要解析的图片路径 * @param bitmapPath 需要解析的图片路径
* @param hints 解析编码类型 * @param hints 解析编码类型
* @return * @return
*/ */
public static String parseCode(String bitmapPath, Map<DecodeHintType,Object> hints){ public static String parseCode(String bitmapPath, Map<DecodeHintType, Object> hints) {
Result result = parseCodeResult(bitmapPath,hints); Result result = parseCodeResult(bitmapPath, hints);
if(result != null){ if (result != null) {
return result.getText(); return result.getText();
} }
return null; return null;
@@ -299,31 +313,34 @@ public final class CodeUtils {
/** /**
* 解析二维码图片 * 解析二维码图片
*
* @param bitmap 解析的图片 * @param bitmap 解析的图片
* @return * @return
*/ */
public static String parseQRCode(Bitmap bitmap){ public static String parseQRCode(Bitmap bitmap) {
return parseCode(bitmap,DecodeFormatManager.QR_CODE_HINTS); return parseCode(bitmap, DecodeFormatManager.QR_CODE_HINTS);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmap 解析的图片 * @param bitmap 解析的图片
* @return * @return
*/ */
public static String parseCode(Bitmap bitmap){ public static String parseCode(Bitmap bitmap) {
return parseCode(bitmap,DecodeFormatManager.ALL_HINTS); return parseCode(bitmap, DecodeFormatManager.ALL_HINTS);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmap 解析的图片 * @param bitmap 解析的图片
* @param hints 解析编码类型 * @param hints 解析编码类型
* @return * @return
*/ */
public static String parseCode(Bitmap bitmap,Map<DecodeHintType,Object> hints){ public static String parseCode(Bitmap bitmap, Map<DecodeHintType, Object> hints) {
Result result = parseCodeResult(bitmap,hints); Result result = parseCodeResult(bitmap, hints);
if(result != null){ if (result != null) {
return result.getText(); return result.getText();
} }
return null; return null;
@@ -331,103 +348,108 @@ public final class CodeUtils {
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmapPath * @param bitmapPath
* @param hints 解析编码类型 * @param hints 解析编码类型
* @return * @return
*/ */
public static Result parseCodeResult(String bitmapPath, Map<DecodeHintType,Object> hints){ public static Result parseCodeResult(String bitmapPath, Map<DecodeHintType, Object> hints) {
return parseCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT,hints); return parseCodeResult(bitmapPath, DEFAULT_REQ_WIDTH, DEFAULT_REQ_HEIGHT, hints);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmapPath 需要解析的图片路径 * @param bitmapPath 需要解析的图片路径
* @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理 * @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理
* @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理 * @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时则不进行压缩处理
* @param hints 解析编码类型 * @param hints 解析编码类型
* @return * @return
*/ */
public static Result parseCodeResult(String bitmapPath,int reqWidth,int reqHeight, Map<DecodeHintType,Object> hints){ public static Result parseCodeResult(String bitmapPath, int reqWidth, int reqHeight, Map<DecodeHintType, Object> hints) {
return parseCodeResult(compressBitmap(bitmapPath,reqWidth,reqHeight),hints); return parseCodeResult(compressBitmap(bitmapPath, reqWidth, reqHeight), hints);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmap 解析的图片 * @param bitmap 解析的图片
* @return * @return
*/ */
public static Result parseCodeResult(Bitmap bitmap){ public static Result parseCodeResult(Bitmap bitmap) {
return parseCodeResult(getRGBLuminanceSource(bitmap),DecodeFormatManager.ALL_HINTS); return parseCodeResult(getRGBLuminanceSource(bitmap), DecodeFormatManager.ALL_HINTS);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param bitmap 解析的图片 * @param bitmap 解析的图片
* @param hints 解析编码类型 * @param hints 解析编码类型
* @return * @return
*/ */
public static Result parseCodeResult(Bitmap bitmap,Map<DecodeHintType,Object> hints){ public static Result parseCodeResult(Bitmap bitmap, Map<DecodeHintType, Object> hints) {
return parseCodeResult(getRGBLuminanceSource(bitmap),hints); return parseCodeResult(getRGBLuminanceSource(bitmap), hints);
} }
/** /**
* 解析一维码/二维码图片 * 解析一维码/二维码图片
*
* @param source * @param source
* @param hints * @param hints
* @return * @return
*/ */
public static Result parseCodeResult(LuminanceSource source, Map<DecodeHintType,Object> hints){ public static Result parseCodeResult(LuminanceSource source, Map<DecodeHintType, Object> hints) {
Result result = null; Result result = null;
MultiFormatReader reader = new MultiFormatReader(); MultiFormatReader reader = new MultiFormatReader();
try{ try {
reader.setHints(hints); reader.setHints(hints);
if (source != null) { if (source != null) {
result = decodeInternal(reader,source); result = decodeInternal(reader, source);
if(result == null){ if (result == null) {
result = decodeInternal(reader,source.invert()); result = decodeInternal(reader, source.invert());
} }
if(result == null && source.isRotateSupported()){ if (result == null && source.isRotateSupported()) {
result = decodeInternal(reader,source.rotateCounterClockwise()); result = decodeInternal(reader, source.rotateCounterClockwise());
} }
} }
}catch (Exception e){ } catch (Exception e) {
LogUtils.w(e.getMessage()); LogUtils.w(e.getMessage());
}finally { } finally {
reader.reset(); reader.reset();
} }
return result; return result;
} }
private static Result decodeInternal(MultiFormatReader reader,LuminanceSource source){ private static Result decodeInternal(MultiFormatReader reader, LuminanceSource source) {
Result result = null; Result result = null;
try{ try {
try{ try {
//采用HybridBinarizer解析 //采用HybridBinarizer解析
result = reader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source))); result = reader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
}catch (Exception e){ } catch (Exception e) {
} }
if(result == null){ if (result == null) {
//如果没有解析成功再采用GlobalHistogramBinarizer解析一次 //如果没有解析成功再采用GlobalHistogramBinarizer解析一次
result = reader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source))); result = reader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
} }
}catch (Exception e){ } catch (Exception e) {
} }
return result; return result;
} }
/** /**
* 压缩图片 * 压缩图片
*
* @param path * @param path
* @return * @return
*/ */
private static Bitmap compressBitmap(String path,int reqWidth,int reqHeight){ private static Bitmap compressBitmap(String path, int reqWidth, int reqHeight) {
if(reqWidth > 0 && reqHeight > 0){//都大于进行判断是否压缩 if (reqWidth > 0 && reqHeight > 0) {//都大于进行判断是否压缩
BitmapFactory.Options newOpts = new BitmapFactory.Options(); BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 开始读入图片此时把options.inJustDecodeBounds 设回true了 // 开始读入图片此时把options.inJustDecodeBounds 设回true了
@@ -444,7 +466,7 @@ public final class CodeUtils {
if (height > reqHeight) {// 如果高度高的话根据宽度固定大小缩放 if (height > reqHeight) {// 如果高度高的话根据宽度固定大小缩放
hSize = (int) (height / reqHeight); hSize = (int) (height / reqHeight);
} }
int size = Math.max(wSize,hSize); int size = Math.max(wSize, hSize);
if (size <= 0) if (size <= 0)
size = 1; size = 1;
newOpts.inSampleSize = size;// 设置缩放比例 newOpts.inSampleSize = size;// 设置缩放比例
@@ -461,10 +483,11 @@ public final class CodeUtils {
/** /**
* 获取RGBLuminanceSource * 获取RGBLuminanceSource
*
* @param bitmap * @param bitmap
* @return * @return
*/ */
private static RGBLuminanceSource getRGBLuminanceSource(@NonNull Bitmap bitmap){ private static RGBLuminanceSource getRGBLuminanceSource(@NonNull Bitmap bitmap) {
int width = bitmap.getWidth(); int width = bitmap.getWidth();
int height = bitmap.getHeight(); int height = bitmap.getHeight();
@@ -476,33 +499,36 @@ public final class CodeUtils {
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param desiredWidth * @param desiredWidth
* @param desiredHeight * @param desiredHeight
* @return * @return
*/ */
public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight) { public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight) {
return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null); return createBarCode(content, BarcodeFormat.CODE_128, desiredWidth, desiredHeight, null);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param format * @param format
* @param desiredWidth * @param desiredWidth
* @param desiredHeight * @param desiredHeight
* @return * @return
*/ */
public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight) { public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight) {
return createBarCode(content,format,desiredWidth,desiredHeight,null); return createBarCode(content, format, desiredWidth, desiredHeight, null);
} }
public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText) { public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText) {
return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null,isShowText,40,Color.BLACK); return createBarCode(content, BarcodeFormat.CODE_128, desiredWidth, desiredHeight, null, isShowText, 40, Color.BLACK);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param desiredWidth * @param desiredWidth
* @param desiredHeight * @param desiredHeight
@@ -510,12 +536,13 @@ public final class CodeUtils {
* @param codeColor * @param codeColor
* @return * @return
*/ */
public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText,@ColorInt int codeColor) { public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText, @ColorInt int codeColor) {
return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null,isShowText,40,codeColor); return createBarCode(content, BarcodeFormat.CODE_128, desiredWidth, desiredHeight, null, isShowText, 40, codeColor);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param format * @param format
* @param desiredWidth * @param desiredWidth
@@ -523,12 +550,13 @@ public final class CodeUtils {
* @param hints * @param hints
* @return * @return
*/ */
public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType,?> hints) { public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType, ?> hints) {
return createBarCode(content,format,desiredWidth,desiredHeight,hints,false,40,Color.BLACK); return createBarCode(content, format, desiredWidth, desiredHeight, hints, false, 40, Color.BLACK);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param format * @param format
* @param desiredWidth * @param desiredWidth
@@ -537,12 +565,13 @@ public final class CodeUtils {
* @param isShowText * @param isShowText
* @return * @return
*/ */
public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType,?> hints, boolean isShowText) { public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType, ?> hints, boolean isShowText) {
return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,Color.BLACK); return createBarCode(content, format, desiredWidth, desiredHeight, hints, isShowText, 40, Color.BLACK);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param format * @param format
* @param desiredWidth * @param desiredWidth
@@ -551,12 +580,13 @@ public final class CodeUtils {
* @param codeColor * @param codeColor
* @return * @return
*/ */
public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, boolean isShowText,@ColorInt int codeColor) { public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, boolean isShowText, @ColorInt int codeColor) {
return createBarCode(content,format,desiredWidth,desiredHeight,null,isShowText,40,codeColor); return createBarCode(content, format, desiredWidth, desiredHeight, null, isShowText, 40, codeColor);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param format * @param format
* @param desiredWidth * @param desiredWidth
@@ -565,12 +595,13 @@ public final class CodeUtils {
* @param isShowText * @param isShowText
* @return * @return
*/ */
public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType,?> hints, boolean isShowText,@ColorInt int codeColor) { public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType, ?> hints, boolean isShowText, @ColorInt int codeColor) {
return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,codeColor); return createBarCode(content, format, desiredWidth, desiredHeight, hints, isShowText, 40, codeColor);
} }
/** /**
* 生成条形码 * 生成条形码
*
* @param content * @param content
* @param format * @param format
* @param desiredWidth * @param desiredWidth
@@ -581,8 +612,8 @@ public final class CodeUtils {
* @param codeColor * @param codeColor
* @return * @return
*/ */
public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight,Map<EncodeHintType,?> hints,boolean isShowText,int textSize,@ColorInt int codeColor) { public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map<EncodeHintType, ?> hints, boolean isShowText, int textSize, @ColorInt int codeColor) {
if(TextUtils.isEmpty(content)){ if (TextUtils.isEmpty(content)) {
return null; return null;
} }
final int WHITE = Color.WHITE; final int WHITE = Color.WHITE;
@@ -606,8 +637,8 @@ public final class CodeUtils {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888); Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height); bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
if(isShowText){ if (isShowText) {
return addCode(bitmap,content,textSize,codeColor,textSize/2); return addCode(bitmap, content, textSize, codeColor, textSize / 2);
} }
return bitmap; return bitmap;
} catch (WriterException e) { } catch (WriterException e) {
@@ -618,6 +649,7 @@ public final class CodeUtils {
/** /**
* 条形码下面添加文本信息 * 条形码下面添加文本信息
*
* @param src * @param src
* @param code * @param code
* @param textSize * @param textSize
@@ -650,7 +682,7 @@ public final class CodeUtils {
paint.setTextSize(textSize); paint.setTextSize(textSize);
paint.setColor(textColor); paint.setColor(textColor);
paint.setTextAlign(Paint.Align.CENTER); paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(code,srcWidth/2,srcHeight + textSize /2 + offset,paint); canvas.drawText(code, srcWidth / 2, srcHeight + textSize / 2 + offset, paint);
canvas.save(); canvas.save();
canvas.restore(); canvas.restore();
} catch (Exception e) { } catch (Exception e) {
@@ -662,5 +694,4 @@ public final class CodeUtils {
} }
} }

View File

@@ -16,9 +16,10 @@
*/ */
package com.king.zxing.util; package com.king.zxing.util;
import android.util.Log; import android.util.Log;
import java.util.Locale;
/** /**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
@@ -28,10 +29,14 @@ public class LogUtils {
public static final String VERTICAL = "|"; public static final String VERTICAL = "|";
/** 是否显示Log日志 */ /**
* 是否显示Log日志
*/
private static boolean isShowLog = true; private static boolean isShowLog = true;
/** Log日志优先权 */ /**
* Log日志优先权
*/
private static int priority = 1; private static int priority = 1;
/** /**
@@ -71,7 +76,7 @@ public class LogUtils {
public static final String TAG_FORMAT = "%s.%s(%s:%d)"; public static final String TAG_FORMAT = "%s.%s(%s:%d)";
private LogUtils(){ private LogUtils() {
throw new AssertionError(); throw new AssertionError();
} }
@@ -97,24 +102,25 @@ public class LogUtils {
/** /**
* 根据堆栈生成TAG * 根据堆栈生成TAG
*
* @return TAG|className.methodName(fileName:lineNumber) * @return TAG|className.methodName(fileName:lineNumber)
*/ */
private static String generateTag(StackTraceElement caller) { private static String generateTag(StackTraceElement caller) {
String tag = TAG_FORMAT; String tag = TAG_FORMAT;
String callerClazzName = caller.getClassName(); String callerClazzName = caller.getClassName();
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1); callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
tag = String.format(tag,callerClazzName, caller.getMethodName(),caller.getFileName(),caller.getLineNumber()); tag = String.format(Locale.getDefault(), tag, callerClazzName, caller.getMethodName(), caller.getFileName(), caller.getLineNumber());
return new StringBuilder().append(TAG).append(VERTICAL).append(tag).toString(); return new StringBuilder().append(TAG).append(VERTICAL).append(tag).toString();
} }
/** /**
* 获取堆栈 * 获取堆栈
* @param n *
* n=0 VMStack * @param n n=0 VMStack
* n=1 Thread * n=1 Thread
* n=3 CurrentStack * n=3 CurrentStack
* n=4 CallerStack * n=4 CallerStack
* ... * ...
* @return * @return
*/ */
public static StackTraceElement getStackTraceElement(int n) { public static StackTraceElement getStackTraceElement(int n) {
@@ -123,18 +129,18 @@ public class LogUtils {
/** /**
* 获取调用方的堆栈TAG * 获取调用方的堆栈TAG
*
* @return * @return
*/ */
private static String getCallerStackLogTag(){ private static String getCallerStackLogTag() {
return generateTag(getStackTraceElement(5)); return generateTag(getStackTraceElement(5));
} }
/** /**
*
* @param t * @param t
* @return * @return
*/ */
private static String getStackTraceString(Throwable t){ private static String getStackTraceString(Throwable t) {
return Log.getStackTraceString(t); return Log.getStackTraceString(t);
} }
@@ -142,6 +148,7 @@ public class LogUtils {
/** /**
* Log.v * Log.v
*
* @param msg * @param msg
*/ */
public static void v(String msg) { public static void v(String msg) {
@@ -155,7 +162,7 @@ public class LogUtils {
Log.v(getCallerStackLogTag(), getStackTraceString(t)); Log.v(getCallerStackLogTag(), getStackTraceString(t));
} }
public static void v(String msg,Throwable t) { public static void v(String msg, Throwable t) {
if (isShowLog && priority <= VERBOSE) if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg), t); Log.v(getCallerStackLogTag(), String.valueOf(msg), t);
} }
@@ -164,6 +171,7 @@ public class LogUtils {
/** /**
* Log.d * Log.d
*
* @param msg * @param msg
*/ */
public static void d(String msg) { public static void d(String msg) {
@@ -176,7 +184,7 @@ public class LogUtils {
Log.d(getCallerStackLogTag(), getStackTraceString(t)); Log.d(getCallerStackLogTag(), getStackTraceString(t));
} }
public static void d(String msg,Throwable t) { public static void d(String msg, Throwable t) {
if (isShowLog && priority <= DEBUG) if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg), t); Log.d(getCallerStackLogTag(), String.valueOf(msg), t);
} }
@@ -185,6 +193,7 @@ public class LogUtils {
/** /**
* Log.i * Log.i
*
* @param msg * @param msg
*/ */
public static void i(String msg) { public static void i(String msg) {
@@ -197,7 +206,7 @@ public class LogUtils {
Log.i(getCallerStackLogTag(), getStackTraceString(t)); Log.i(getCallerStackLogTag(), getStackTraceString(t));
} }
public static void i(String msg,Throwable t) { public static void i(String msg, Throwable t) {
if (isShowLog && priority <= INFO) if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg), t); Log.i(getCallerStackLogTag(), String.valueOf(msg), t);
} }
@@ -206,6 +215,7 @@ public class LogUtils {
/** /**
* Log.w * Log.w
*
* @param msg * @param msg
*/ */
public static void w(String msg) { public static void w(String msg) {
@@ -218,7 +228,7 @@ public class LogUtils {
Log.w(getCallerStackLogTag(), getStackTraceString(t)); Log.w(getCallerStackLogTag(), getStackTraceString(t));
} }
public static void w(String msg,Throwable t) { public static void w(String msg, Throwable t) {
if (isShowLog && priority <= WARN) if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg), t); Log.w(getCallerStackLogTag(), String.valueOf(msg), t);
} }
@@ -227,6 +237,7 @@ public class LogUtils {
/** /**
* Log.e * Log.e
*
* @param msg * @param msg
*/ */
public static void e(String msg) { public static void e(String msg) {
@@ -239,7 +250,7 @@ public class LogUtils {
Log.e(getCallerStackLogTag(), getStackTraceString(t)); Log.e(getCallerStackLogTag(), getStackTraceString(t));
} }
public static void e(String msg,Throwable t) { public static void e(String msg, Throwable t) {
if (isShowLog && priority <= ERROR) if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg), t); Log.e(getCallerStackLogTag(), String.valueOf(msg), t);
} }
@@ -248,6 +259,7 @@ public class LogUtils {
/** /**
* Log.wtf * Log.wtf
*
* @param msg * @param msg
*/ */
public static void wtf(String msg) { public static void wtf(String msg) {
@@ -260,7 +272,7 @@ public class LogUtils {
Log.wtf(getCallerStackLogTag(), getStackTraceString(t)); Log.wtf(getCallerStackLogTag(), getStackTraceString(t));
} }
public static void wtf(String msg,Throwable t) { public static void wtf(String msg, Throwable t) {
if (isShowLog && priority <= ASSERT) if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg), t); Log.wtf(getCallerStackLogTag(), String.valueOf(msg), t);
} }
@@ -312,3 +324,4 @@ public class LogUtils {
} }
} }

View File

@@ -14,72 +14,78 @@ import androidx.fragment.app.Fragment;
*/ */
public class PermissionUtils { public class PermissionUtils {
private PermissionUtils(){ private PermissionUtils() {
throw new AssertionError(); throw new AssertionError();
} }
/** /**
* 检测是否授权 * 检测是否授权
*
* @param context * @param context
* @param permission * @param permission
* @return 返回{@code true} 表示已授权,{@code false}表示未授权 * @return 返回{@code true} 表示已授权,{@code false}表示未授权
*/ */
public static boolean checkPermission(@NonNull Context context, @NonNull String permission){ public static boolean checkPermission(@NonNull Context context, @NonNull String permission) {
return ActivityCompat.checkSelfPermission(context,permission) == PackageManager.PERMISSION_GRANTED; return ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
} }
/** /**
* 请求权限 * 请求权限
*
* @param activity * @param activity
* @param permission * @param permission
* @param requestCode * @param requestCode
*/ */
public static void requestPermission(@NonNull Activity activity,@NonNull String permission, @IntRange(from = 0) int requestCode){ public static void requestPermission(@NonNull Activity activity, @NonNull String permission, @IntRange(from = 0) int requestCode) {
requestPermissions(activity,new String[]{permission},requestCode); requestPermissions(activity, new String[]{permission}, requestCode);
} }
/** /**
* 请求权限 * 请求权限
*
* @param fragment * @param fragment
* @param permission * @param permission
* @param requestCode * @param requestCode
*/ */
public static void requestPermission(@NonNull Fragment fragment, @NonNull String permission, @IntRange(from = 0) int requestCode){ public static void requestPermission(@NonNull Fragment fragment, @NonNull String permission, @IntRange(from = 0) int requestCode) {
requestPermissions(fragment,new String[]{permission},requestCode); requestPermissions(fragment, new String[]{permission}, requestCode);
} }
/** /**
* 请求权限 * 请求权限
*
* @param activity * @param activity
* @param permissions * @param permissions
* @param requestCode * @param requestCode
*/ */
public static void requestPermissions(@NonNull Activity activity,@NonNull String[] permissions, @IntRange(from = 0) int requestCode){ public static void requestPermissions(@NonNull Activity activity, @NonNull String[] permissions, @IntRange(from = 0) int requestCode) {
ActivityCompat.requestPermissions(activity,permissions,requestCode); ActivityCompat.requestPermissions(activity, permissions, requestCode);
} }
/** /**
* 请求权限 * 请求权限
*
* @param fragment * @param fragment
* @param permissions * @param permissions
* @param requestCode * @param requestCode
*/ */
public static void requestPermissions(@NonNull Fragment fragment,@NonNull String[] permissions, @IntRange(from = 0) int requestCode){ public static void requestPermissions(@NonNull Fragment fragment, @NonNull String[] permissions, @IntRange(from = 0) int requestCode) {
fragment.requestPermissions(permissions,requestCode); fragment.requestPermissions(permissions, requestCode);
} }
/** /**
* 请求权限结果 * 请求权限结果
*
* @param requestPermission 请求的权限 * @param requestPermission 请求的权限
* @param permissions * @param permissions
* @param grantResults * @param grantResults
* @return 返回{@code true} 表示已授权,{@code false}表示未授权 * @return 返回{@code true} 表示已授权,{@code false}表示未授权
*/ */
public static boolean requestPermissionsResult(@NonNull String requestPermission, @NonNull String[] permissions, @NonNull int[] grantResults){ public static boolean requestPermissionsResult(@NonNull String requestPermission, @NonNull String[] permissions, @NonNull int[] grantResults) {
int length = permissions.length; int length = permissions.length;
for(int i = 0; i < length; i++){ for (int i = 0; i < length; i++) {
if(requestPermission.equals(permissions[i])){ if (requestPermission.equals(permissions[i])) {
if(grantResults[i] == PackageManager.PERMISSION_GRANTED){ if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
return true; return true;
} }
} }
@@ -89,17 +95,18 @@ public class PermissionUtils {
/** /**
* 请求权限结果 * 请求权限结果
*
* @param requestPermissions 请求的权限 * @param requestPermissions 请求的权限
* @param permissions * @param permissions
* @param grantResults * @param grantResults
* @return 返回{@code true} 表示全部已授权,{@code false}表示未全部授权 * @return 返回{@code true} 表示全部已授权,{@code false}表示未全部授权
*/ */
public static boolean requestPermissionsResult(@NonNull String[] requestPermissions, @NonNull String[] permissions, @NonNull int[] grantResults){ public static boolean requestPermissionsResult(@NonNull String[] requestPermissions, @NonNull String[] permissions, @NonNull int[] grantResults) {
int length = permissions.length; int length = permissions.length;
for(int i = 0; i < length; i++){ for (int i = 0; i < length; i++) {
for(int j = 0; j < requestPermissions.length; j++){ for (int j = 0; j < requestPermissions.length; j++) {
if(requestPermissions[j].equals(permissions[i])){ if (requestPermissions[j].equals(permissions[i])) {
if(grantResults[i] != PackageManager.PERMISSION_GRANTED){ if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
return false; return false;
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFF"
android:pathData="m755.2,209.9c-17.2,-17.4 -40.2,-26.9 -64.5,-26.9l-357.2,0c-50.3,0 -91.3,40.9 -91.3,91.3l0,105.1c0,77.3 36.3,149.7 97.7,196.1l0,300.3c0,50.3 40.9,91.3 91.3,91.3l161.5,0c50.3,0 91.3,-40.9 91.3,-91.3l0,-300.3c61.4,-46.4 97.7,-118.7 97.7,-196.1l0,-104.9c0.2,-24.4 -9.3,-47.3 -26.5,-64.6zM333.5,230.8l357.1,0c11.6,0 22.5,4.5 30.7,12.8c8.2,8.2 12.7,19.1 12.6,30.8l0,42.9l-443.9,0l0,-43c0,-24 19.5,-43.5 43.5,-43.5zM646.7,543.3c-6.6,4.4 -10.5,11.9 -10.5,19.8l0,312.6c0,24 -19.5,43.5 -43.5,43.5l-161.4,0c-24,0 -43.5,-19.5 -43.5,-43.5l0,-312.6c0,-7.9 -3.9,-15.4 -10.5,-19.8c-54,-36.5 -86.3,-96.8 -87.1,-161.7l443.7,0c-0.8,64.9 -33.2,125.2 -87.2,161.7z" />
<path
android:fillColor="#FFFFFF"
android:pathData="m543,690.4l-62,0c-0.7,0 -1.2,-0.5 -1.2,-1.2l0,-169.4c0,-0.7 0.5,-1.2 1.2,-1.2l62,0c0.7,0 1.2,0.5 1.2,1.2l0,169.4c0,0.7 -0.5,1.2 -1.2,1.2z" />
</vector>

View File

@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFF"
android:pathData="m755.2,209.9c-17.2,-17.4 -40.2,-26.9 -64.5,-26.9l-357.2,0c-50.3,0 -91.3,40.9 -91.3,91.3l0,105.1c0,77.3 36.3,149.7 97.7,196.1l0,300.3c0,50.3 40.9,91.3 91.3,91.3l161.5,0c50.3,0 91.3,-40.9 91.3,-91.3l0,-300.3c61.4,-46.4 97.7,-118.7 97.7,-196.1l0,-104.9c0.2,-24.4 -9.3,-47.3 -26.5,-64.6zM333.5,230.8l357.1,0c11.6,0 22.5,4.5 30.7,12.8c8.2,8.2 12.7,19.1 12.6,30.8l0,42.9l-443.9,0l0,-43c0,-24 19.5,-43.5 43.5,-43.5zM646.7,543.3c-6.6,4.4 -10.5,11.9 -10.5,19.8l0,312.6c0,24 -19.5,43.5 -43.5,43.5l-161.4,0c-24,0 -43.5,-19.5 -43.5,-43.5l0,-312.6c0,-7.9 -3.9,-15.4 -10.5,-19.8c-54,-36.5 -86.3,-96.8 -87.1,-161.7l443.7,0c-0.8,64.9 -33.2,125.2 -87.2,161.7z" />
<path
android:fillColor="#FFFFFF"
android:pathData="m543,690.4l-62,0c-0.7,0 -1.2,-0.5 -1.2,-1.2l0,-169.4c0,-0.7 0.5,-1.2 1.2,-1.2l62,0c0.7,0 1.2,0.5 1.2,1.2l0,169.4c0,0.7 -0.5,1.2 -1.2,1.2z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M682.5,77.5a53.5,26.5 116.5,1 0,45 28.4a53.5,26.5 116.5,1 0,-45 -28.4z"
android:strokeWidth="1"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M482,80a30,68 0,1 0,60 0a30,68 0,1 0,-60 0z"
android:strokeWidth="1"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M296.5,105.6a53.5,26.5 63.5,1 0,45 -28.4a53.5,26.5 63.5,1 0,-45 28.4z"
android:strokeWidth="1"
android:strokeColor="#FFFFFF" />
</vector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/zxl_flashlight_on"/> <item android:drawable="@drawable/zxl_flashlight_on" android:state_selected="true" />
<item android:drawable="@drawable/zxl_flashlight_off"/> <item android:drawable="@drawable/zxl_flashlight_off" />
</selector> </selector>

View File

@@ -6,16 +6,19 @@
<androidx.camera.view.PreviewView <androidx.camera.view.PreviewView
android:id="@+id/previewView" android:id="@+id/previewView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent" />
<com.king.zxing.ViewfinderView <com.king.zxing.ViewfinderView
android:id="@+id/viewfinderView" android:id="@+id/viewfinderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent" />
<ImageView <ImageView
android:id="@+id/ivFlashlight" android:id="@+id/ivFlashlight"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@drawable/zxl_flashlight_selector" android:layout_marginTop="@dimen/zxl_flashlight_margin_top"
android:layout_marginTop="@dimen/zxl_flashlight_margin_top" /> android:contentDescription="@null"
android:src="@drawable/zxl_flashlight_selector" />
</FrameLayout> </FrameLayout>

View File

@@ -40,6 +40,17 @@
<enum name="right" value="3"/> <enum name="right" value="3"/>
<enum name="bottom" value="4"/> <enum name="bottom" value="4"/>
</attr> </attr>
<attr name="pointColor" format="color"/>
<attr name="pointStrokeColor" format="color"/>
<attr name="pointRadius" format="dimension"/>
<attr name="pointStrokeRatio" format="float"/>
<attr name="pointDrawable" format="reference"/>
<attr name="showPointAnim" format="boolean"/>
<attr name="laserDrawable" format="reference"/>
<attr name="viewfinderStyle" format="enum">
<enum name="classic" value="0"/>
<enum name="popular" value="1"/>
</attr>
</declare-styleable> </declare-styleable>

View File

@@ -5,6 +5,7 @@
<color name="viewfinder_frame">#7F1FB3E2</color> <color name="viewfinder_frame">#7F1FB3E2</color>
<color name="viewfinder_corner">#FF1FB3E2</color> <color name="viewfinder_corner">#FF1FB3E2</color>
<color name="viewfinder_laser">#FF1FB3E2</color> <color name="viewfinder_laser">#FF1FB3E2</color>
<color name="viewfinder_point">#FF1FB3E2</color>
<color name="viewfinder_text_color">#FFC0C0C0</color> <color name="viewfinder_text_color">#FFC0C0C0</color>
<color name="zxl_capture_status_bar_color">#00000000</color> <color name="zxl_capture_status_bar_color">#00000000</color>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<dimen name="zxl_flashlight_margin_top">80dp</dimen> <dimen name="zxl_flashlight_margin_top">90dp</dimen>
</resources> </resources>