更新CameraX至v1.2.2

This commit is contained in:
Jenly
2023-04-15 16:45:34 +08:00
parent 863f1ab1b4
commit 2359695964
33 changed files with 734 additions and 319 deletions

View File

@@ -26,7 +26,7 @@ ZXingLite for Android 是ZXing的精简极速版基于ZXing库优化扫码和
## ViewfinderView属性说明 ## ViewfinderView属性说明
| 属性 | 属性类型 | 默认值 | 属性说明 | | 属性 | 属性类型 | 默认值 | 属性说明 |
| :------|:----------| :------ | :------ | |:------------------------|:----------| :------ | :------ |
| maskColor | color |<font color=#000000>#60000000</font>| 扫描区外遮罩的颜色 | | maskColor | color |<font color=#000000>#60000000</font>| 扫描区外遮罩的颜色 |
| frameColor | color |<font color=#1FB3E2>#7F1FB3E2</font>| 扫描区边框的颜色 | | frameColor | color |<font color=#1FB3E2>#7F1FB3E2</font>| 扫描区边框的颜色 |
| cornerColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区边角的颜色 | | cornerColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区边角的颜色 |
@@ -61,6 +61,7 @@ ZXingLite for Android 是ZXing的精简极速版基于ZXing库优化扫码和
| pointDrawable | reference | | 结果点自定义图片 | | pointDrawable | reference | | 结果点自定义图片 |
| showPointAnim | boolean | true | 是否显示结果点的动画 | | showPointAnim | boolean | true | 是否显示结果点的动画 |
| laserDrawable | reference | | 扫描激光自定义图片 | | laserDrawable | reference | | 扫描激光自定义图片 |
| laserDrawableRatio | float | 0.625f | 激光扫描图片与屏幕占比 |
| viewfinderStyle | enum | classic | 取景框样式支持classic经典样式带扫码框那种、popular流行样式不带扫码框 | | viewfinderStyle | enum | classic | 取景框样式支持classic经典样式带扫码框那种、popular流行样式不带扫码框 |
@@ -83,7 +84,7 @@ allprojects {
```gradle ```gradle
// AndroidX 版本 // AndroidX 版本
implementation 'com.github.jenly1314:zxing-lite:2.3.1' implementation 'com.github.jenly1314:zxing-lite:2.4.0'
``` ```
@@ -175,7 +176,7 @@ implementation 'com.king.zxing:zxing-lite:1.1.9'
// 获取CameraScan扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。 // 获取CameraScan扫码相关的配置设置。CameraScan里面包含部分支持链式调用的方法即调用返回是CameraScan本身的一些配置建议在startCamera之前调用。
getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false
.setVibrate(true)//设置是否震动默认为false .setVibrate(true)//设置是否震动默认为false
.setCameraConfig(new CameraConfig())//设置相机配置信息CameraConfig可覆写options方法自定义配置 .setCameraConfig(new ResolutionCameraConfig(this))//设置相机配置信息CameraConfig可覆写options方法自定义配置
.setNeedAutoZoom(false)//二维码太小时可自动缩放默认为false .setNeedAutoZoom(false)//二维码太小时可自动缩放默认为false
.setNeedTouchZoom(true)//支持多指触摸捏合缩放默认为true .setNeedTouchZoom(true)//支持多指触摸捏合缩放默认为true
.setDarkLightLux(45f)//设置光线足够暗的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 .setDarkLightLux(45f)//设置光线足够暗的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效
@@ -404,6 +405,12 @@ dependencies {
## 版本记录 ## 版本记录
#### v2.4.02023-4-15
* 优化CameraScan的缺省配置CameraConfig相关配置
* 优化ViewfinderView自定义属性新增laserDrawableRatio
* 优化ImageAnalyzer中YUV数据的处理
* 更新CameraX至v1.2.2
#### v2.3.12023-3-4 #### v2.3.12023-3-4
* 更新CameraX至v1.2.1 * 更新CameraX至v1.2.1
* 更新Gradle至v7.5 * 更新Gradle至v7.5

View File

@@ -1,18 +1,22 @@
apply plugin: 'com.android.application' plugins {
//apply plugin: 'kotlin-android' id 'com.android.application'
//apply plugin: 'kotlin-android-extensions' id 'org.jetbrains.kotlin.android'
}
android { android {
compileSdkVersion build_versions.compileSdk namespace 'com.king.zxing.app'
buildToolsVersion build_versions.buildTools compileSdk build_versions.compileSdk
defaultConfig { defaultConfig {
applicationId "com.king.zxing.app" applicationId "com.king.zxing.app"
minSdkVersion build_versions.minSdk minSdk build_versions.minSdk
targetSdkVersion build_versions.targetSdk targetSdk build_versions.targetSdk
versionCode app_version.versionCode versionCode app_version.versionCode
versionName app_version.versionName versionName app_version.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
@@ -24,22 +28,16 @@ android {
abortOnError false abortOnError false
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11
} }
// kotlinOptions {
// jvmTarget = '1.8'
// }
} }
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation deps.test.junit testImplementation deps.test.junit
androidTestImplementation deps.test.runner androidTestImplementation deps.test.android_ext_junit
androidTestImplementation deps.test.espresso androidTestImplementation deps.test.espresso
implementation deps.androidx.design implementation deps.androidx.design

Binary file not shown.

View File

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

View File

@@ -77,7 +77,7 @@ public class CustomCaptureActivity extends CaptureActivity {
getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false getCameraScan().setPlayBeep(true)//设置是否播放音效默认为false
.setVibrate(true)//设置是否震动默认为false .setVibrate(true)//设置是否震动默认为false
// .setCameraConfig(new CameraConfig())//设置相机配置信息CameraConfig可覆写options方法自定义配置 // .setCameraConfig(new CameraConfig())//设置相机配置信息CameraConfig可覆写options方法自定义配置
.setCameraConfig(new ResolutionCameraConfig(this))//设置CameraConfig可以根据自己的需求去自定义配置 // .setCameraConfig(new ResolutionCameraConfig(this))//设置CameraConfig可以根据自己的需求去自定义配置
.setNeedAutoZoom(false)//二维码太小时可自动缩放默认为false .setNeedAutoZoom(false)//二维码太小时可自动缩放默认为false
.setNeedTouchZoom(true)//支持多指触摸捏合缩放默认为true .setNeedTouchZoom(true)//支持多指触摸捏合缩放默认为true
.setDarkLightLux(45f)//设置光线足够暗的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 .setDarkLightLux(45f)//设置光线足够暗的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效
@@ -117,12 +117,10 @@ public class CustomCaptureActivity extends CaptureActivity {
} }
private void showToast(String text){ private void showToast(String text){
if(toast == null){ if(toast != null){
toast = Toast.makeText(this,text,Toast.LENGTH_SHORT); toast.cancel();
}else{
toast.setText(text);
toast.setDuration(Toast.LENGTH_SHORT);
} }
toast = Toast.makeText(this,text,Toast.LENGTH_SHORT);
toast.show(); toast.show();
} }

View File

@@ -32,6 +32,8 @@ import com.king.zxing.util.LogUtils;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
@@ -72,6 +74,8 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
private Toast toast; private Toast toast;
private ExecutorService executor = Executors.newSingleThreadExecutor();
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -97,12 +101,10 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
} }
private void showToast(String text){ private void showToast(String text){
if(toast == null){ if(toast != null){
toast = Toast.makeText(this,text,Toast.LENGTH_SHORT); toast.cancel();
}else{
toast.setDuration(Toast.LENGTH_SHORT);
toast.setText(text);
} }
toast = Toast.makeText(this,text,Toast.LENGTH_SHORT);
toast.show(); toast.show();
} }
@@ -165,7 +167,7 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
} }
private void asyncThread(Runnable runnable){ private void asyncThread(Runnable runnable){
new Thread(runnable).start(); executor.execute(runnable);
} }
/** /**

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -13,7 +13,10 @@
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:viewfinderStyle="popular"/> app:viewfinderStyle="popular"
app:laserStyle="image"
app:laserDrawableRatio="0.8"
app:laserDrawable="@drawable/ic_laser_line"/>
<ImageView <ImageView
android:id="@+id/ivFlash" android:id="@+id/ivFlash"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -1,53 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
apply from: 'versions.gradle' apply from: 'versions.gradle'
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
repositories { plugins {
google() id 'com.android.application' version '7.4.2' apply false
mavenCentral() id 'com.android.library' version '7.4.2' apply false
} id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
// id 'org.jetbrains.dokka' version '1.7.0' apply false
id 'com.vanniktech.maven.publish' version '0.22.0' apply false
dependencies {
classpath "com.android.tools.build:gradle:$versions.gralde"
// classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
// classpath "com.novoda:bintray-release:$versions.bintray_release"
classpath "com.vanniktech:gradle-maven-publish-plugin:$versions.mavenPublish"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
allprojects {
plugins.withId("com.vanniktech.maven.publish") {
mavenPublish {
sonatypeHost = "S01"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
allprojects {
tasks.withType(Javadoc) {
options{
encoding "UTF-8"
charSet 'UTF-8'
links "http://docs.oracle.com/javase/8/docs/api"
}
options.addStringOption('Xdoclint:none', '-quiet')
failOnError false
}
} }

View File

@@ -1,5 +1,11 @@
## 版本记录 ## 版本记录
#### v2.4.02023-4-15
* 优化CameraScan的缺省配置CameraConfig相关配置
* 优化ViewfinderView自定义属性新增laserDrawableRatio
* 优化ImageAnalyzer中YUV数据的处理
* 更新CameraX至v1.2.2
#### v2.3.12023-3-4 #### v2.3.12023-3-4
* 更新CameraX至v1.2.1 * 更新CameraX至v1.2.1
* 更新Gradle至v7.5 * 更新Gradle至v7.5

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.1 VERSION_NAME=2.4.0
VERSION_CODE=36 VERSION_CODE=37
GROUP=com.github.jenly1314 GROUP=com.github.jenly1314
POM_DESCRIPTION=ZXingLite for Android POM_DESCRIPTION=ZXingLite for Android
@@ -35,6 +35,8 @@ POM_DEVELOPER_ID=jenly
POM_DEVELOPER_NAME=Jenly Yu POM_DEVELOPER_NAME=Jenly Yu
POM_DEVELOPER_URL=https://github.com/jenly1314/ POM_DEVELOPER_URL=https://github.com/jenly1314/
SONATYPE_HOST=S01
RELEASE_REPOSITORY_URL=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ RELEASE_REPOSITORY_URL=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
SNAPSHOT_REPOSITORY_URL=https://s01.oss.sonatype.org/content/repositories/snapshots/ SNAPSHOT_REPOSITORY_URL=https://s01.oss.sonatype.org/content/repositories/snapshots/

View File

@@ -1 +1,18 @@
include ':app', ':zxing-lite' pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "ZXingLite"
include ':app'
include ':zxing-lite'

View File

@@ -1,7 +1,7 @@
//App //App
def app_version = [:] def app_version = [:]
app_version.versionCode = 36 app_version.versionCode = 37
app_version.versionName = "2.3.1" app_version.versionName = "2.4.0"
ext.app_version = app_version ext.app_version = app_version
//build version //build version
@@ -22,16 +22,17 @@ versions.appcompat = "1.1.0"
versions.constraintLayout = "2.0.4" versions.constraintLayout = "2.0.4"
//test //test
versions.junit = "1.1.0" versions.junit = "4.13.2"
versions.androidExtJunit = "1.1.3"
versions.test = "1.2.0" versions.test = "1.2.0"
versions.runner = "1.2.0" versions.runner = "1.2.0"
versions.espresso = "3.2.0" versions.espresso = "3.4.0"
versions.bintray_release = "0.9.2" versions.bintray_release = "0.9.2"
versions.mavenPublish = '0.18.0' versions.mavenPublish = '0.22.0'
versions.gralde = "7.4.1" versions.gralde = "7.4.2"
versions.kotlin = "1.6.0" versions.kotlin = "1.8.0"
versions.coreKtx = "1.6.0" versions.coreKtx = "1.7.0"
//zxing //zxing
versions.zxing = "3.5.1" versions.zxing = "3.5.1"
@@ -53,7 +54,8 @@ deps.androidx = androidx
//test //test
def test = [:] def test = [:]
test.junit = "androidx.test.ext:junit:$versions.junit" test.junit = "junit:junit:$versions.junit"
test.android_ext_junit = "androidx.test.ext:junit:$versions.androidExtJunit"
test.test = "androidx.test:core:$versions.test" test.test = "androidx.test:core:$versions.test"
test.runner = "androidx.test:runner:$versions.runner" test.runner = "androidx.test:runner:$versions.runner"
test.espresso = "androidx.test.espresso:espresso-core:$versions.espresso" test.espresso = "androidx.test.espresso:espresso-core:$versions.espresso"

View File

@@ -1,52 +1,46 @@
apply plugin: 'com.android.library' plugins {
//apply from: 'bintray.gradle' id 'com.android.library'
apply plugin: "com.vanniktech.maven.publish" id 'com.vanniktech.maven.publish'
}
android { android {
compileSdkVersion build_versions.compileSdk namespace 'com.king.zxing'
buildToolsVersion build_versions.buildTools compileSdk build_versions.compileSdk
defaultConfig { defaultConfig {
minSdkVersion build_versions.minSdk minSdk build_versions.minSdk
targetSdkVersion build_versions.targetSdk targetSdk build_versions.targetSdk
versionCode app_version.versionCode
versionName app_version.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
} }
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
lintOptions {
abortOnError false
warning 'InvalidPackage'
}
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11
} }
lintOptions {
abortOnError false
}
} }
//task javadoc(type: Javadoc) {
// source = android.sourceSets.main.java.srcDirs
// classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
//}
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation deps.test.junit testImplementation deps.test.junit
androidTestImplementation deps.test.runner androidTestImplementation deps.test.android_ext_junit
androidTestImplementation deps.test.espresso androidTestImplementation deps.test.espresso
coreLibraryDesugaring deps.desugar_jdk coreLibraryDesugaring deps.desugar_jdk
compileOnly deps.androidx.appcompat implementation deps.androidx.appcompat
api deps.zxing api deps.zxing
api deps.camera_core api deps.camera_core
api deps.camera_camera2 api deps.camera_camera2

View File

View File

@@ -46,6 +46,15 @@ public abstract class CameraScan implements ICamera, ICameraControl {
*/ */
public static int LENS_FACING_BACK = CameraSelector.LENS_FACING_BACK; public static int LENS_FACING_BACK = CameraSelector.LENS_FACING_BACK;
/**
* 纵横比4:3
*/
public static final float ASPECT_RATIO_4_3 = 4.0F / 3.0F;
/**
* 纵横比16:9
*/
public static final float ASPECT_RATIO_16_9 = 16.0F / 9.0F;
/** /**
* 是否需要支持自动缩放 * 是否需要支持自动缩放
*/ */
@@ -59,7 +68,7 @@ public abstract class CameraScan implements ICamera, ICameraControl {
/** /**
* 是否需要支持触摸缩放 * 是否需要支持触摸缩放
* *
* @return * @return 返回是否需要支持触摸缩放
*/ */
protected boolean isNeedTouchZoom() { protected boolean isNeedTouchZoom() {
return isNeedTouchZoom; return isNeedTouchZoom;
@@ -68,8 +77,8 @@ public abstract class CameraScan implements ICamera, ICameraControl {
/** /**
* 设置是否需要支持触摸缩放 * 设置是否需要支持触摸缩放
* *
* @param needTouchZoom * @param needTouchZoom 是否需要支持触摸缩放
* @return * @return {@link CameraScan}
*/ */
public CameraScan setNeedTouchZoom(boolean needTouchZoom) { public CameraScan setNeedTouchZoom(boolean needTouchZoom) {
isNeedTouchZoom = needTouchZoom; isNeedTouchZoom = needTouchZoom;
@@ -79,7 +88,7 @@ public abstract class CameraScan implements ICamera, ICameraControl {
/** /**
* 是否需要支持自动缩放 * 是否需要支持自动缩放
* *
* @return * @return 是否需要支持自动缩放
*/ */
protected boolean isNeedAutoZoom() { protected boolean isNeedAutoZoom() {
return isNeedAutoZoom; return isNeedAutoZoom;
@@ -88,8 +97,8 @@ public abstract class CameraScan implements ICamera, ICameraControl {
/** /**
* 设置是否需要支持自动缩放 * 设置是否需要支持自动缩放
* *
* @param needAutoZoom * @param needAutoZoom 是否需要支持自动缩放
* @return * @return {@link CameraScan}
*/ */
public CameraScan setNeedAutoZoom(boolean needAutoZoom) { public CameraScan setNeedAutoZoom(boolean needAutoZoom) {
isNeedAutoZoom = needAutoZoom; isNeedAutoZoom = needAutoZoom;
@@ -99,7 +108,8 @@ public abstract class CameraScan implements ICamera, ICameraControl {
/** /**
* 设置相机配置,请在{@link #startCamera()}之前调用 * 设置相机配置,请在{@link #startCamera()}之前调用
* *
* @param cameraConfig * @param cameraConfig 相机配置
* @return {@link CameraScan}
*/ */
public abstract CameraScan setCameraConfig(CameraConfig cameraConfig); public abstract CameraScan setCameraConfig(CameraConfig cameraConfig);
@@ -113,7 +123,8 @@ public abstract class CameraScan implements ICamera, ICameraControl {
* 2. 如果只是想拦截扫码结果回调自己处理逻辑,但并不想继续分析图像(即不想连扫),可通过 * 2. 如果只是想拦截扫码结果回调自己处理逻辑,但并不想继续分析图像(即不想连扫),可通过
* 调用getCameraScan().setAnalyzeImage(false)来停止分析图像。 * 调用getCameraScan().setAnalyzeImage(false)来停止分析图像。
* *
* @param analyze * @param analyze 是否分析图像
* @return {@link CameraScan}
*/ */
public abstract CameraScan setAnalyzeImage(boolean analyze); public abstract CameraScan setAnalyzeImage(boolean analyze);
@@ -123,7 +134,8 @@ public abstract class CameraScan implements ICamera, ICameraControl {
* <p> * <p>
* 内置了一些{@link Analyzer}的实现类如下: * 内置了一些{@link Analyzer}的实现类如下:
* *
* @param analyzer * @param analyzer 分析器
* @return {@link CameraScan}
* @see {@link MultiFormatAnalyzer} * @see {@link MultiFormatAnalyzer}
* @see {@link AreaRectAnalyzer} * @see {@link AreaRectAnalyzer}
* @see {@link ImageAnalyzer} * @see {@link ImageAnalyzer}
@@ -133,47 +145,56 @@ public abstract class CameraScan implements ICamera, ICameraControl {
public abstract CameraScan setAnalyzer(Analyzer analyzer); public abstract CameraScan setAnalyzer(Analyzer analyzer);
/** /**
* 设置是否 * 设置是否
* *
* @param vibrate * @param vibrate 是否振动
* @return {@link CameraScan}
*/ */
public abstract CameraScan setVibrate(boolean vibrate); public abstract CameraScan setVibrate(boolean vibrate);
/** /**
* 设置是否播放提示音 * 设置是否播放提示音
* *
* @param playBeep * @param playBeep 是否播放蜂鸣提示音
* @return {@link CameraScan}
*/ */
public abstract CameraScan setPlayBeep(boolean playBeep); public abstract CameraScan setPlayBeep(boolean playBeep);
/** /**
* 设置扫码结果回调 * 设置扫码结果回调
* *
* @param callback * @param callback 扫码结果回调
* @return {@link CameraScan}
*/ */
public abstract CameraScan setOnScanResultCallback(OnScanResultCallback callback); public abstract CameraScan setOnScanResultCallback(OnScanResultCallback callback);
/** /**
* 绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒 * 绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒
* *
* @param v * @param v 手电筒视图
* @return {@link CameraScan}
*/ */
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 光线亮度阈值
* @return {@link CameraScan}
*/ */
public abstract CameraScan setDarkLightLux(float lightLux); public abstract CameraScan setDarkLightLux(float lightLux);
/** /**
* 设置光线足够明亮的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效 * 设置光线足够明亮的阈值单位lux需要通过{@link #bindFlashlightView(View)}绑定手电筒才有效
* *
* @param lightLux * @param lightLux 光线亮度阈值
* @return {@link CameraScan}
*/ */
public abstract CameraScan setBrightLightLux(float lightLux); public abstract CameraScan setBrightLightLux(float lightLux);
/**
* 扫描结果回调
*/
public interface OnScanResultCallback { public interface OnScanResultCallback {
/** /**
* 扫码结果回调 * 扫码结果回调
@@ -193,14 +214,13 @@ public abstract class CameraScan implements ICamera, ICameraControl {
default void onScanResultFailure() { default void onScanResultFailure() {
} }
} }
/** /**
* 解析扫结果 * 解析扫结果
* *
* @param data * @param data 需解析的意图数据
* @return * @return 返回解析结果
*/ */
@Nullable @Nullable
public static String parseScanResult(Intent data) { public static String parseScanResult(Intent data) {

View File

@@ -45,12 +45,25 @@ import androidx.camera.view.PreviewView;
*/ */
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;
/**
* 预览视图
*/
protected PreviewView previewView; protected PreviewView previewView;
/**
* 取景视图
*/
protected ViewfinderView viewfinderView; protected ViewfinderView viewfinderView;
/**
* 手电筒视图
*/
protected View ivFlashlight; protected View ivFlashlight;
/**
* CameraScan
*/
private CameraScan mCameraScan; private CameraScan mCameraScan;
@Override @Override
@@ -68,11 +81,11 @@ 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 && viewfinderViewId != View.NO_ID) {
viewfinderView = findViewById(viewfinderViewId); viewfinderView = findViewById(viewfinderViewId);
} }
int ivFlashlightId = getFlashlightId(); int ivFlashlightId = getFlashlightId();
if (ivFlashlightId != 0) { if (ivFlashlightId != 0 && ivFlashlightId != View.NO_ID) {
ivFlashlight = findViewById(ivFlashlightId); ivFlashlight = findViewById(ivFlashlightId);
if (ivFlashlight != null) { if (ivFlashlight != null) {
ivFlashlight.setOnClickListener(v -> onClickFlashlight()); ivFlashlight.setOnClickListener(v -> onClickFlashlight());
@@ -144,8 +157,8 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
/** /**
* 请求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)) {
@@ -173,7 +186,7 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
/** /**
* 布局ID通过覆写此方法可以自定义布局 * 布局ID通过覆写此方法可以自定义布局
* *
* @return * @return 布局ID
*/ */
public int getLayoutId() { public int getLayoutId() {
return R.layout.zxl_capture; return R.layout.zxl_capture;
@@ -188,11 +201,10 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
return R.id.viewfinderView; return R.id.viewfinderView;
} }
/** /**
* 预览界面{@link #previewView} 的ID * 预览界面{@link #previewView} 的ID可通过覆写此方法自定义ID
* *
* @return * @return 默认返回{@code R.id.previewView}
*/ */
public int getPreviewViewId() { public int getPreviewViewId() {
return R.id.previewView; return R.id.previewView;
@@ -208,7 +220,7 @@ public class CaptureActivity extends AppCompatActivity implements CameraScan.OnS
} }
/** /**
* Get {@link CameraScan} * 获取 {@link CameraScan}
* *
* @return {@link #mCameraScan} * @return {@link #mCameraScan}
*/ */

View File

@@ -26,6 +26,7 @@ import com.king.zxing.util.LogUtils;
import com.king.zxing.util.PermissionUtils; import com.king.zxing.util.PermissionUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.view.PreviewView; import androidx.camera.view.PreviewView;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@@ -46,14 +47,29 @@ import androidx.fragment.app.Fragment;
*/ */
public class CaptureFragment extends Fragment implements CameraScan.OnScanResultCallback { public class CaptureFragment extends Fragment implements CameraScan.OnScanResultCallback {
/**
* 相机权限请求代码
*/
private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86; private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86;
/**
* 根视图
*/
private View mRootView; private View mRootView;
/**
* 预览视图
*/
protected PreviewView previewView; protected PreviewView previewView;
/**
* 取景视图
*/
protected ViewfinderView viewfinderView; protected ViewfinderView viewfinderView;
/**
* 手电筒视图
*/
protected View ivFlashlight; protected View ivFlashlight;
/**
* CameraScan
*/
private CameraScan mCameraScan; private CameraScan mCameraScan;
public static CaptureFragment newInstance() { public static CaptureFragment newInstance() {
@@ -70,21 +86,26 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
if (isContentView()) { if (isContentView()) {
mRootView = createRootView(inflater, container); mRootView = createRootView(inflater, container);
} }
initUI();
return mRootView; return mRootView;
} }
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initUI();
}
/** /**
* 初始化 * 初始化
*/ */
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 && viewfinderViewId != View.NO_ID) {
viewfinderView = mRootView.findViewById(viewfinderViewId); viewfinderView = mRootView.findViewById(viewfinderViewId);
} }
int ivFlashlightId = getFlashlightId(); int ivFlashlightId = getFlashlightId();
if (ivFlashlightId != 0) { if (ivFlashlightId != 0 && ivFlashlightId != View.NO_ID) {
ivFlashlight = mRootView.findViewById(ivFlashlightId); ivFlashlight = mRootView.findViewById(ivFlashlightId);
if (ivFlashlight != null) { if (ivFlashlight != null) {
ivFlashlight.setOnClickListener(v -> onClickFlashlight()); ivFlashlight.setOnClickListener(v -> onClickFlashlight());
@@ -156,8 +177,8 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 请求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)) {
@@ -168,9 +189,9 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
} }
@Override @Override
public void onDestroy() { public void onDestroyView() {
releaseCamera(); releaseCamera();
super.onDestroy(); super.onDestroyView();
} }
/** /**
@@ -197,7 +218,7 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
/** /**
* 布局ID通过覆写此方法可以自定义布局 * 布局ID通过覆写此方法可以自定义布局
* *
* @return * @return 布局ID
*/ */
public int getLayoutId() { public int getLayoutId() {
return R.layout.zxl_capture; return R.layout.zxl_capture;
@@ -213,9 +234,9 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
} }
/** /**
* 预览界面{@link #previewView} 的ID * 预览界面{@link #previewView} 的ID可通过覆写此方法自定义ID
* *
* @return * @return 默认返回{@code R.id.previewView}
*/ */
public int getPreviewViewId() { public int getPreviewViewId() {
return R.id.previewView; return R.id.previewView;
@@ -231,7 +252,7 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
} }
/** /**
* Get {@link CameraScan} * 获取 {@link CameraScan}
* *
* @return {@link #mCameraScan} * @return {@link #mCameraScan}
*/ */
@@ -252,6 +273,11 @@ public class CaptureFragment extends Fragment implements CameraScan.OnScanResult
//-------------------------------------------- //--------------------------------------------
/**
* 获取根视图
*
* @return {@link #mRootView}
*/
public View getRootView() { public View getRootView() {
return mRootView; return mRootView;
} }

View File

@@ -95,6 +95,12 @@ public class DecodeConfig {
} }
/**
* 获取配置的解码支持类型 {@link DecodeHintType}
*
* @return
* @see {@link DecodeFormatManager}
*/
public Map<DecodeHintType, Object> getHints() { public Map<DecodeHintType, Object> getHints() {
return hints; return hints;
} }
@@ -105,7 +111,7 @@ public class DecodeConfig {
* @param hints {@link DecodeFormatManager} * @param hints {@link DecodeFormatManager}
* <p> * <p>
* 内置的一些解码可参见如下: * 内置的一些解码可参见如下:
* @return * @return {@link DecodeConfig}
* @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}
@@ -124,7 +130,7 @@ public class DecodeConfig {
/** /**
* 是否支持识别反色码,黑白颜色反转 * 是否支持识别反色码,黑白颜色反转
* *
* @return * @return 是否支持识别反色码
*/ */
public boolean isSupportLuminanceInvert() { public boolean isSupportLuminanceInvert() {
return isSupportLuminanceInvert; return isSupportLuminanceInvert;
@@ -134,7 +140,7 @@ public class DecodeConfig {
* 设置是否支持识别反色码,黑白颜色反转 * 设置是否支持识别反色码,黑白颜色反转
* *
* @param supportLuminanceInvert 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。 * @param supportLuminanceInvert 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setSupportLuminanceInvert(boolean supportLuminanceInvert) { public DecodeConfig setSupportLuminanceInvert(boolean supportLuminanceInvert) {
isSupportLuminanceInvert = supportLuminanceInvert; isSupportLuminanceInvert = supportLuminanceInvert;
@@ -144,7 +150,7 @@ public class DecodeConfig {
/** /**
* 是否支持扫垂直的条码 * 是否支持扫垂直的条码
* *
* @return * @return 是否支持扫垂直的条码
*/ */
public boolean isSupportVerticalCode() { public boolean isSupportVerticalCode() {
return isSupportVerticalCode; return isSupportVerticalCode;
@@ -154,7 +160,7 @@ public class DecodeConfig {
* 设置是否支持扫垂直的条码 * 设置是否支持扫垂直的条码
* *
* @param supportVerticalCode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。 * @param supportVerticalCode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setSupportVerticalCode(boolean supportVerticalCode) { public DecodeConfig setSupportVerticalCode(boolean supportVerticalCode) {
isSupportVerticalCode = supportVerticalCode; isSupportVerticalCode = supportVerticalCode;
@@ -164,7 +170,7 @@ public class DecodeConfig {
/** /**
* 是否支持使用多解码 * 是否支持使用多解码
* *
* @return * @return 是否支持使用多解码
*/ */
public boolean isMultiDecode() { public boolean isMultiDecode() {
return isMultiDecode; return isMultiDecode;
@@ -185,7 +191,7 @@ public class DecodeConfig {
/** /**
* 是否支持识别反色码(条码黑白颜色反转的码)使用多解码 * 是否支持识别反色码(条码黑白颜色反转的码)使用多解码
* *
* @return * @return 是否支持识别反色码
*/ */
public boolean isSupportLuminanceInvertMultiDecode() { public boolean isSupportLuminanceInvertMultiDecode() {
return isSupportLuminanceInvertMultiDecode; return isSupportLuminanceInvertMultiDecode;
@@ -195,7 +201,7 @@ public class DecodeConfig {
* 设置是否支持识别反色码(条码黑白颜色反转的码)使用多解码 * 设置是否支持识别反色码(条码黑白颜色反转的码)使用多解码
* *
* @param supportLuminanceInvertMultiDecode 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。 * @param supportLuminanceInvertMultiDecode 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。
* @return * @return {@link DecodeConfig}
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer} * @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
*/ */
public DecodeConfig setSupportLuminanceInvertMultiDecode(boolean supportLuminanceInvertMultiDecode) { public DecodeConfig setSupportLuminanceInvertMultiDecode(boolean supportLuminanceInvertMultiDecode) {
@@ -206,7 +212,7 @@ public class DecodeConfig {
/** /**
* 是否支持垂直的条码,使用多解码 * 是否支持垂直的条码,使用多解码
* *
* @return * @return 是否支持垂直的条码,使用多解码
*/ */
public boolean isSupportVerticalCodeMultiDecode() { public boolean isSupportVerticalCodeMultiDecode() {
return isSupportVerticalCodeMultiDecode; return isSupportVerticalCodeMultiDecode;
@@ -216,7 +222,7 @@ public class DecodeConfig {
* 设置是否支持垂直的条码,使用多解码;解码时,对应的二值化的实现: {@link HybridBinarizer} , {@link GlobalHistogramBinarizer} * 设置是否支持垂直的条码,使用多解码;解码时,对应的二值化的实现: {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
* *
* @param supportVerticalCodeMultiDecode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。 * @param supportVerticalCodeMultiDecode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setSupportVerticalCodeMultiDecode(boolean supportVerticalCodeMultiDecode) { public DecodeConfig setSupportVerticalCodeMultiDecode(boolean supportVerticalCodeMultiDecode) {
isSupportVerticalCodeMultiDecode = supportVerticalCodeMultiDecode; isSupportVerticalCodeMultiDecode = supportVerticalCodeMultiDecode;
@@ -226,7 +232,7 @@ public class DecodeConfig {
/** /**
* 需要分析识别区域 * 需要分析识别区域
* *
* @return * @return 分析识别区域
*/ */
public Rect getAnalyzeAreaRect() { public Rect getAnalyzeAreaRect() {
return analyzeAreaRect; return analyzeAreaRect;
@@ -244,7 +250,7 @@ public class DecodeConfig {
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* <p> * <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setAnalyzeAreaRect(Rect analyzeAreaRect) { public DecodeConfig setAnalyzeAreaRect(Rect analyzeAreaRect) {
this.analyzeAreaRect = analyzeAreaRect; this.analyzeAreaRect = analyzeAreaRect;
@@ -254,7 +260,7 @@ public class DecodeConfig {
/** /**
* 是否支持全区域扫码识别 * 是否支持全区域扫码识别
* *
* @return * @return 是否支持全区域扫码识别
*/ */
public boolean isFullAreaScan() { public boolean isFullAreaScan() {
return isFullAreaScan; return isFullAreaScan;
@@ -274,7 +280,7 @@ public class DecodeConfig {
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* <p> * <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setFullAreaScan(boolean fullAreaScan) { public DecodeConfig setFullAreaScan(boolean fullAreaScan) {
isFullAreaScan = fullAreaScan; isFullAreaScan = fullAreaScan;
@@ -284,7 +290,7 @@ public class DecodeConfig {
/** /**
* 识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 * 识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO},设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
* *
* @return * @return 识别区域比例,默认{@link #DEFAULT_AREA_RECT_RATIO}
*/ */
public float getAreaRectRatio() { public float getAreaRectRatio() {
return areaRectRatio; return areaRectRatio;
@@ -302,7 +308,7 @@ public class DecodeConfig {
* 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息 * 您还可以通过{@link CameraScan#setCameraConfig(CameraConfig)}去自定义配置{@link CameraConfig}的配置信息控制预览相关配置信息
* <p> * <p>
* 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)} * 即判定区域分析的优先级顺序为:{@link #setFullAreaScan(boolean)} -> {@link #setAnalyzeAreaRect(Rect)} -> {@link #setAreaRectRatio(float)}
* @return * @return {@link DecodeConfig}
*/ */
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;
@@ -312,7 +318,7 @@ public class DecodeConfig {
/** /**
* 识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移 * 识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移
* *
* @return * @return 识别区域垂直方向偏移量
*/ */
public int getAreaRectVerticalOffset() { public int getAreaRectVerticalOffset() {
return areaRectVerticalOffset; return areaRectVerticalOffset;
@@ -322,7 +328,7 @@ public class DecodeConfig {
* 设置识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移 * 设置识别区域垂直方向偏移量支持负数大于0时居中心向下偏移小于0时居中心向上偏移
* *
* @param areaRectVerticalOffset * @param areaRectVerticalOffset
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setAreaRectVerticalOffset(int areaRectVerticalOffset) { public DecodeConfig setAreaRectVerticalOffset(int areaRectVerticalOffset) {
this.areaRectVerticalOffset = areaRectVerticalOffset; this.areaRectVerticalOffset = areaRectVerticalOffset;
@@ -332,7 +338,7 @@ public class DecodeConfig {
/** /**
* 识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移 * 识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移
* *
* @return * @return 识别区域水平方向偏移量
*/ */
public int getAreaRectHorizontalOffset() { public int getAreaRectHorizontalOffset() {
return areaRectHorizontalOffset; return areaRectHorizontalOffset;
@@ -342,7 +348,7 @@ public class DecodeConfig {
* 设置识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移 * 设置识别区域水平方向偏移量支持负数大于0时居中心向右偏移小于0时居中心向左偏移
* *
* @param areaRectHorizontalOffset * @param areaRectHorizontalOffset
* @return * @return {@link DecodeConfig}
*/ */
public DecodeConfig setAreaRectHorizontalOffset(int areaRectHorizontalOffset) { public DecodeConfig setAreaRectHorizontalOffset(int areaRectHorizontalOffset) {
this.areaRectHorizontalOffset = areaRectHorizontalOffset; this.areaRectHorizontalOffset = areaRectHorizontalOffset;

View File

@@ -41,7 +41,6 @@ public final class DecodeFormatManager {
* 二维码 * 二维码
*/ */
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);
/** /**
* 默认 * 默认
*/ */
@@ -86,7 +85,7 @@ public final class DecodeFormatManager {
} }
/** /**
* 维码 * 维码
* 包括如下几种格式: * 包括如下几种格式:
* {@link BarcodeFormat#CODABAR} * {@link BarcodeFormat#CODABAR}
* {@link BarcodeFormat#CODE_39} * {@link BarcodeFormat#CODE_39}

View File

@@ -17,7 +17,9 @@ import com.google.zxing.ResultPoint;
import com.google.zxing.common.detector.MathUtils; import com.google.zxing.common.detector.MathUtils;
import com.king.zxing.analyze.Analyzer; import com.king.zxing.analyze.Analyzer;
import com.king.zxing.analyze.MultiFormatAnalyzer; import com.king.zxing.analyze.MultiFormatAnalyzer;
import com.king.zxing.config.AspectRatioCameraConfig;
import com.king.zxing.config.CameraConfig; import com.king.zxing.config.CameraConfig;
import com.king.zxing.config.ResolutionCameraConfig;
import com.king.zxing.manager.AmbientLightManager; import com.king.zxing.manager.AmbientLightManager;
import com.king.zxing.manager.BeepManager; import com.king.zxing.manager.BeepManager;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
@@ -67,14 +69,12 @@ public class DefaultCameraScan extends CameraScan {
* opposed to a hover movement gesture. * opposed to a hover movement gesture.
*/ */
private static final int HOVER_TAP_TIMEOUT = 150; private static final int HOVER_TAP_TIMEOUT = 150;
/** /**
* Defines the maximum distance in pixels that a touch pad touch can move * Defines the maximum distance in pixels that a touch pad touch can move
* before being released for it to be considered a tap (click) as opposed * before being released for it to be considered a tap (click) as opposed
* to a hover movement gesture. * to a hover movement gesture.
*/ */
private static final int HOVER_TAP_SLOP = 20; private static final int HOVER_TAP_SLOP = 20;
/** /**
* 每次缩放改变的步长 * 每次缩放改变的步长
*/ */
@@ -83,40 +83,73 @@ public class DefaultCameraScan extends CameraScan {
private FragmentActivity mFragmentActivity; private FragmentActivity mFragmentActivity;
private Context mContext; private Context mContext;
private LifecycleOwner mLifecycleOwner; private LifecycleOwner mLifecycleOwner;
/**
* 预览视图
*/
private PreviewView mPreviewView; private PreviewView mPreviewView;
private ListenableFuture<ProcessCameraProvider> mCameraProviderFuture; private ListenableFuture<ProcessCameraProvider> mCameraProviderFuture;
/**
* 相机
*/
private Camera mCamera; private Camera mCamera;
/**
* 相机配置
*/
private CameraConfig mCameraConfig; private CameraConfig mCameraConfig;
/**
* 分析器
*/
private Analyzer mAnalyzer; private Analyzer mAnalyzer;
/** /**
* 是否分析 * 是否分析
*/ */
private volatile boolean isAnalyze = true; private volatile boolean isAnalyze = true;
/** /**
* 是否已经分析出结果 * 是否已经分析出结果
*/ */
private volatile boolean isAnalyzeResult; private volatile boolean isAnalyzeResult;
/**
* 闪光灯(手电筒)视图
*/
private View flashlightView; private View flashlightView;
private MutableLiveData<Result> mResultLiveData; private MutableLiveData<Result> mResultLiveData;
/**
* 扫描结果回调
*/
private OnScanResultCallback mOnScanResultCallback; private OnScanResultCallback mOnScanResultCallback;
/**
* 蜂鸣音效管理器:主要用于播放蜂鸣提示音和振动效果
*/
private BeepManager mBeepManager; private BeepManager mBeepManager;
/**
* 环境光线管理器:主要通过传感器来监听光线的亮度变化
*/
private AmbientLightManager mAmbientLightManager; private AmbientLightManager mAmbientLightManager;
private int mOrientation; private int mOrientation;
private int mImageWidth; private int mImageWidth;
private int mImageHeight; private int mImageHeight;
/**
* 最后自动缩放时间
*/
private long mLastAutoZoomTime; private long mLastAutoZoomTime;
/**
* 最后点击时间,根据两次点击时间间隔用于区分单机和触摸缩放事件
*/
private long mLastHoveTapTime; private long mLastHoveTapTime;
/**
* 是否是点击事件
*/
private boolean isClickTap; private boolean isClickTap;
/**
* 按下时X坐标
*/
private float mDownX; private float mDownX;
/**
* 按下时Y坐标
*/
private float mDownY; private float mDownY;
public DefaultCameraScan(@NonNull FragmentActivity activity, @NonNull PreviewView previewView) { public DefaultCameraScan(@NonNull FragmentActivity activity, @NonNull PreviewView previewView) {
@@ -199,7 +232,7 @@ public class DefaultCameraScan extends CameraScan {
/** /**
* 处理预览视图点击事件;如果触发的点击事件被判定对焦操作,则开始自动对焦 * 处理预览视图点击事件;如果触发的点击事件被判定对焦操作,则开始自动对焦
* *
* @param event * @param event 事件
*/ */
private void handlePreviewViewClickTap(MotionEvent event) { private void handlePreviewViewClickTap(MotionEvent event) {
if (event.getPointerCount() == 1) { if (event.getPointerCount() == 1) {
@@ -226,8 +259,8 @@ public class DefaultCameraScan extends CameraScan {
/** /**
* 开始对焦和测光 * 开始对焦和测光
* *
* @param x * @param x X轴坐标
* @param y * @param y Y轴坐标
*/ */
private void startFocusAndMetering(float x, float y) { private void startFocusAndMetering(float x, float y) {
if (mCamera != null) { if (mCamera != null) {
@@ -240,19 +273,6 @@ public class DefaultCameraScan extends CameraScan {
} }
} }
/**
* 初始化配置
*/
private void initConfig() {
if (mCameraConfig == null) {
mCameraConfig = new CameraConfig();
}
if (mAnalyzer == null) {
mAnalyzer = new MultiFormatAnalyzer();
}
}
@Override @Override
public CameraScan setCameraConfig(CameraConfig cameraConfig) { public CameraScan setCameraConfig(CameraConfig cameraConfig) {
if (cameraConfig != null) { if (cameraConfig != null) {
@@ -261,12 +281,35 @@ public class DefaultCameraScan extends CameraScan {
return this; return this;
} }
/**
* 初始化相机配置
*/
/**
* 初始化相机配置
*/
private void initCameraConfig(Context context) {
if (mCameraConfig == null) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int size = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
// 根据分辨率初始化缺省配置CameraConfig在此前提下尽可能的找到比屏幕分辨率小一级的配置在适配、性能与体验之间得有所取舍找到平衡点。
if (size > ResolutionCameraConfig.IMAGE_QUALITY_1080P) {
mCameraConfig = new ResolutionCameraConfig(context);
} else if (size > ResolutionCameraConfig.IMAGE_QUALITY_720P) {
mCameraConfig = new ResolutionCameraConfig(context, ResolutionCameraConfig.IMAGE_QUALITY_720P);
} else {
mCameraConfig = new AspectRatioCameraConfig(context);
}
}
}
@Override @Override
public void startCamera() { public void startCamera() {
initConfig(); initCameraConfig(mContext);
if (mAnalyzer == null) {
mAnalyzer = new MultiFormatAnalyzer();
}
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());
@@ -336,6 +379,7 @@ public class DefaultCameraScan extends CameraScan {
/** /**
* 处理自动缩放 * 处理自动缩放
*
* @param distance * @param distance
* @param result * @param result
* @return * @return
@@ -353,7 +397,8 @@ public class DefaultCameraScan extends CameraScan {
/** /**
* 扫描结果回调 * 扫描结果回调
* @param result *
* @param result 扫描结果
*/ */
private void scanResultCallback(Result result) { private void scanResultCallback(Result result) {
if (mOnScanResultCallback != null && mOnScanResultCallback.onScanResultCallback(result)) { if (mOnScanResultCallback != null && mOnScanResultCallback.onScanResultCallback(result)) {
@@ -473,11 +518,6 @@ public class DefaultCameraScan extends CameraScan {
return false; return false;
} }
/**
* 是否支持闪光灯
*
* @return
*/
@Override @Override
public boolean hasFlashUnit() { public boolean hasFlashUnit() {
if (mCamera != null) { if (mCamera != null) {

View File

@@ -23,7 +23,7 @@ public interface ICamera {
/** /**
* 获取 {@link Camera} * 获取 {@link Camera}
* *
* @return * @return {@link Camera}
*/ */
@Nullable @Nullable
Camera getCamera(); Camera getCamera();

View File

@@ -22,7 +22,7 @@ public interface ICameraControl {
/** /**
* 缩放到指定比例 * 缩放到指定比例
* *
* @param ratio * @param ratio 缩放比例
*/ */
void zoomTo(float ratio); void zoomTo(float ratio);
@@ -39,28 +39,28 @@ public interface ICameraControl {
/** /**
* 线性缩放到指定比例 * 线性缩放到指定比例
* *
* @param linearZoom * @param linearZoom 线性缩放比例范围在0.0 ~ 1.0之间
*/ */
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

@@ -8,6 +8,7 @@ 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.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Point; import android.graphics.Point;
@@ -26,8 +27,6 @@ import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.king.zxing.util.LogUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
@@ -216,6 +215,10 @@ public class ViewfinderView extends View {
private Bitmap laserBitmap; private Bitmap laserBitmap;
private float laserBitmapRatio;
private float laserBitmapWidth;
private int viewfinderStyle = ViewfinderStyle.CLASSIC; private int viewfinderStyle = ViewfinderStyle.CLASSIC;
private List<Point> pointList; private List<Point> pointList;
@@ -248,7 +251,7 @@ public class ViewfinderView extends View {
*/ */
public enum LaserStyle { public enum LaserStyle {
NONE(0), LINE(1), GRID(2), IMAGE(3); NONE(0), LINE(1), GRID(2), IMAGE(3);
private int mValue; private final int mValue;
LaserStyle(int value) { LaserStyle(int value) {
mValue = value; mValue = value;
@@ -270,7 +273,7 @@ public class ViewfinderView extends View {
public enum TextLocation { public enum TextLocation {
TOP(0), BOTTOM(1); TOP(0), BOTTOM(1);
private int mValue; private final int mValue;
TextLocation(int value) { TextLocation(int value) {
mValue = value; mValue = value;
@@ -292,7 +295,7 @@ 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);
private int mValue; private final int mValue;
FrameGravity(int value) { FrameGravity(int value) {
mValue = value; mValue = value;
@@ -369,6 +372,7 @@ public class ViewfinderView extends View {
isShowPointAnim = array.getBoolean(R.styleable.ViewfinderView_showPointAnim, true); isShowPointAnim = array.getBoolean(R.styleable.ViewfinderView_showPointAnim, true);
Drawable pointDrawable = array.getDrawable(R.styleable.ViewfinderView_pointDrawable); Drawable pointDrawable = array.getDrawable(R.styleable.ViewfinderView_pointDrawable);
Drawable laserDrawable = array.getDrawable(R.styleable.ViewfinderView_laserDrawable); Drawable laserDrawable = array.getDrawable(R.styleable.ViewfinderView_laserDrawable);
laserBitmapRatio = array.getFloat(R.styleable.ViewfinderView_laserDrawableRatio, 0.625f);
viewfinderStyle = array.getInt(R.styleable.ViewfinderView_viewfinderStyle, ViewfinderStyle.CLASSIC); viewfinderStyle = array.getInt(R.styleable.ViewfinderView_viewfinderStyle, ViewfinderStyle.CLASSIC);
array.recycle(); array.recycle();
@@ -433,6 +437,24 @@ public class ViewfinderView extends View {
this.laserStyle = laserStyle; this.laserStyle = laserStyle;
} }
/**
* 设置激光扫描自定义图片
*
* @param drawableResId
*/
public void setLaserDrawable(@DrawableRes int drawableResId) {
setLaserBitmap(BitmapFactory.decodeResource(getResources(), drawableResId));
}
/**
* 设置激光扫描自定义图片
*
* @param laserBitmap
*/
public void setLaserBitmap(Bitmap laserBitmap) {
this.laserBitmap = laserBitmap;
scaleLaserBitmap();
}
public void setPointImageResource(@DrawableRes int drawable) { public void setPointImageResource(@DrawableRes int drawable) {
setPointBitmap(BitmapFactory.decodeResource(getResources(), drawable)); setPointBitmap(BitmapFactory.decodeResource(getResources(), drawable));
} }
@@ -448,10 +470,26 @@ public class ViewfinderView extends View {
initFrame(getWidth(),getHeight()); initFrame(getWidth(),getHeight());
} }
private void scaleLaserBitmap() {
if (laserBitmap != null && laserBitmapWidth > 0) {
float ratio = laserBitmapWidth / laserBitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(ratio, ratio);
int w = laserBitmap.getWidth();
int h = laserBitmap.getHeight();
laserBitmap = Bitmap.createBitmap(laserBitmap, 0, 0, w, h, matrix, true);
}
}
private void initFrame(int width, int height) { private void initFrame(int width, int height) {
int size = (int) (Math.min(width, height) * frameRatio); int size = (int) (Math.min(width, height) * frameRatio);
if (laserBitmapWidth <= 0) {
laserBitmapWidth = Math.min(width, height) * laserBitmapRatio;
scaleLaserBitmap();
}
if (frameWidth <= 0 || frameWidth > width) { if (frameWidth <= 0 || frameWidth > width) {
frameWidth = size; frameWidth = size;
} }
@@ -582,7 +620,7 @@ public class ViewfinderView extends View {
private void drawImageScanner(Canvas canvas, Rect frame) { private void drawImageScanner(Canvas canvas, Rect frame) {
if (laserBitmap != null) { if (laserBitmap != null) {
paint.setColor(Color.WHITE); paint.setColor(Color.WHITE);
canvas.drawBitmap(laserBitmap, frame.left, scannerStart, paint); canvas.drawBitmap(laserBitmap, (getWidth() - laserBitmap.getWidth()) / 2, scannerStart, paint);
if (scannerStart < scannerEnd) { if (scannerStart < scannerEnd) {
scannerStart += scannerLineMoveDistance; scannerStart += scannerLineMoveDistance;
} else { } else {
@@ -591,7 +629,6 @@ public class ViewfinderView extends View {
} else { } else {
drawLineScanner(canvas, frame); drawLineScanner(canvas, frame);
} }
} }
/** /**

View File

@@ -16,6 +16,8 @@ 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 BarcodeFormatAnalyzer extends AreaRectAnalyzer { public abstract class BarcodeFormatAnalyzer extends AreaRectAnalyzer {

View File

@@ -5,22 +5,23 @@ import android.content.res.Configuration;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.king.zxing.util.BitmapUtils;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.nio.ByteBuffer;
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;
/** /**
* 图像分析器 * 图像分析器
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public abstract class ImageAnalyzer implements Analyzer { public abstract class ImageAnalyzer implements Analyzer {
/** /**
* 分析图像数据 * 分析图像数据
*
* @param data * @param data
* @param width * @param width
* @param height * @param height
@@ -31,12 +32,10 @@ public abstract class ImageAnalyzer implements Analyzer {
@Override @Override
public Result analyze(@NonNull ImageProxy image, int orientation) { public Result analyze(@NonNull ImageProxy image, int orientation) {
if (image.getFormat() == ImageFormat.YUV_420_888) { if (image.getFormat() == ImageFormat.YUV_420_888) {
@SuppressLint("UnsafeExperimentalUsageError")
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
int width = image.getWidth(); int width = image.getWidth();
int height = image.getHeight(); int height = image.getHeight();
@SuppressLint("UnsafeOptInUsageError")
byte[] data = BitmapUtils.yuv420ThreePlanesToNV21(image.getImage().getPlanes(), width, height).array();
if (orientation == Configuration.ORIENTATION_PORTRAIT) { if (orientation == Configuration.ORIENTATION_PORTRAIT) {
byte[] rotatedData = new byte[data.length]; byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {

View File

@@ -11,6 +11,8 @@ import androidx.annotation.Nullable;
/** /**
* 二维码分析器
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class QRCodeAnalyzer extends BarcodeFormatAnalyzer { public class QRCodeAnalyzer extends BarcodeFormatAnalyzer {

View File

@@ -3,8 +3,11 @@ 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.CameraScan;
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.AspectRatio; import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraSelector;
@@ -18,6 +21,9 @@ import androidx.camera.core.Preview;
*/ */
public final class AspectRatioCameraConfig extends CameraConfig { public final class AspectRatioCameraConfig extends CameraConfig {
/**
* 纵横比
*/
private int mAspectRatio; private int mAspectRatio;
public AspectRatioCameraConfig(Context context) { public AspectRatioCameraConfig(Context context) {
@@ -34,9 +40,10 @@ public final class AspectRatioCameraConfig extends CameraConfig {
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(Locale.getDefault(), "displayMetrics: %dx%d", width, height));
float ratio = Math.max(width, height) / Math.min(width, height); float ratio = Math.max(width, height) / (float) Math.min(width, height);
if (Math.abs(ratio - 4.0F / 3.0F) < Math.abs(ratio - 16.0F / 9.0F)) { if (Math.abs(ratio - CameraScan.ASPECT_RATIO_4_3) < Math.abs(ratio - CameraScan.ASPECT_RATIO_16_9)) {
mAspectRatio = AspectRatio.RATIO_4_3; mAspectRatio = AspectRatio.RATIO_4_3;
} else { } else {
mAspectRatio = AspectRatio.RATIO_16_9; mAspectRatio = AspectRatio.RATIO_16_9;

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Size; import android.util.Size;
import com.king.zxing.CameraScan;
import com.king.zxing.util.LogUtils; import com.king.zxing.util.LogUtils;
import java.util.Locale; import java.util.Locale;
@@ -29,7 +30,9 @@ public class ResolutionCameraConfig extends CameraConfig {
*/ */
public static final int IMAGE_QUALITY_720P = 720; public static final int IMAGE_QUALITY_720P = 720;
/**
* 目标尺寸
*/
private Size mTargetSize; private Size mTargetSize;
/** /**
@@ -62,26 +65,24 @@ public class ResolutionCameraConfig extends CameraConfig {
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(Locale.getDefault(), "displayMetrics: %dx%d", width, height)); LogUtils.d(String.format(Locale.getDefault(), "displayMetrics: %dx%d", width, height));
// 因为为了保持流畅性和性能尽可能的限制在imageQuality默认1080p在此前提下尽可能的找到屏幕接近的分辨率 // 因为为了保持流畅性和性能尽可能的限制在imageQuality默认1080p在此前提下尽可能的找到屏幕接近的分辨率
if (width < height) { if (width < height) {
float ratio = height / (float) width;
int size = Math.min(width, imageQuality); int size = Math.min(width, imageQuality);
float ratio = width / (float) height; if (Math.abs(ratio - CameraScan.ASPECT_RATIO_4_3) < Math.abs(ratio - CameraScan.ASPECT_RATIO_16_9)) {
if (ratio > 0.7F) { mTargetSize = new Size(size, Math.round(size * CameraScan.ASPECT_RATIO_4_3));
// 一般应用于平板
mTargetSize = new Size(size, (int) (size / 3.0F * 4.0F));
} else { } else {
mTargetSize = new Size(size, (int) (size / 9.0F * 16.0F)); mTargetSize = new Size(size, Math.round(size * CameraScan.ASPECT_RATIO_16_9));
} }
} else { } else {
int size = Math.min(height, imageQuality); int size = Math.min(height, imageQuality);
float ratio = height / (float) width; float ratio = width / (float) height;
if (ratio > 0.7F) { if (Math.abs(ratio - CameraScan.ASPECT_RATIO_4_3) < Math.abs(ratio - CameraScan.ASPECT_RATIO_16_9)) {
// 一般应用于平板 mTargetSize = new Size(Math.round(size * CameraScan.ASPECT_RATIO_4_3), size);
mTargetSize = new Size((int) (size / 3.0F * 4.0F), size);
} else { } else {
mTargetSize = new Size((int) (size / 9.0F * 16.0F), size); mTargetSize = new Size(Math.round(size * CameraScan.ASPECT_RATIO_16_9), size);
} }
} }
LogUtils.d("targetSize: " + mTargetSize); LogUtils.d("targetSize: " + mTargetSize);

View File

@@ -7,6 +7,8 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager; import android.hardware.SensorManager;
/** /**
* 环境光线管理器:主要通过传感器来监听光线的亮度变化
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class AmbientLightManager implements SensorEventListener { public class AmbientLightManager implements SensorEventListener {

View File

@@ -13,6 +13,8 @@ import com.king.zxing.util.LogUtils;
import java.io.Closeable; import java.io.Closeable;
/** /**
* 蜂鸣音效管理器:主要用于播放蜂鸣提示音和振动效果
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable { public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {

View File

@@ -0,0 +1,273 @@
package com.king.zxing.util;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.media.ExifInterface;
import android.media.Image;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.ImageProxy;
/**
* Utils functions for bitmap conversions.
*
* @see <a href="https://github.com/googlesamples/mlkit/blob/master/android/vision-quickstart/app/src/main/java/com/google/mlkit/vision/demo/BitmapUtils.java">BitmapUtils</a>
*/
public class BitmapUtils {
private BitmapUtils() {
throw new AssertionError();
}
/**
* Converts NV21 format byte buffer to bitmap.
*/
@Nullable
public static Bitmap getBitmap(ByteBuffer data, int width, int height, int rotationDegrees) {
data.rewind();
byte[] imageInBuffer = new byte[data.limit()];
data.get(imageInBuffer, 0, imageInBuffer.length);
try {
YuvImage image = new YuvImage(
imageInBuffer, ImageFormat.NV21, width, height, null);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);
Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
stream.close();
return rotateBitmap(bmp, rotationDegrees, false, false);
} catch (Exception e) {
LogUtils.e("Error: " + e.getMessage());
}
return null;
}
/**
* Converts a YUV_420_888 image from CameraX API to a bitmap.
*/
@Nullable
public static Bitmap getBitmap(ImageProxy image) {
@SuppressLint("UnsafeOptInUsageError")
ByteBuffer nv21Buffer = yuv420ThreePlanesToNV21(image.getImage().getPlanes(), image.getWidth(), image.getHeight());
return getBitmap(nv21Buffer, image.getWidth(), image.getHeight(), image.getImageInfo().getRotationDegrees());
}
/**
* Rotates a bitmap if it is converted from a bytebuffer.
*/
private static Bitmap rotateBitmap(
Bitmap bitmap, int rotationDegrees, boolean flipX, boolean flipY) {
Matrix matrix = new Matrix();
// Rotate the image back to straight.
matrix.postRotate(rotationDegrees);
// Mirror the image along the X or Y axis.
matrix.postScale(flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f);
Bitmap rotatedBitmap =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// Recycle the old bitmap if it has changed.
if (rotatedBitmap != bitmap) {
bitmap.recycle();
}
return rotatedBitmap;
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Nullable
public static Bitmap getBitmapFromContentUri(ContentResolver contentResolver, Uri imageUri)
throws IOException {
Bitmap decodedBitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri);
if (decodedBitmap == null) {
return null;
}
int orientation = getExifOrientationTag(contentResolver, imageUri);
int rotationDegrees = 0;
boolean flipX = false;
boolean flipY = false;
// See e.g. https://magnushoff.com/articles/jpeg-orientation/ for a detailed explanation on each
// orientation.
switch (orientation) {
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
flipX = true;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotationDegrees = 90;
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
rotationDegrees = 90;
flipX = true;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotationDegrees = 180;
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
flipY = true;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotationDegrees = -90;
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
rotationDegrees = -90;
flipX = true;
break;
case ExifInterface.ORIENTATION_UNDEFINED:
case ExifInterface.ORIENTATION_NORMAL:
default:
// No transformations necessary in this case.
}
return rotateBitmap(decodedBitmap, rotationDegrees, flipX, flipY);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private static int getExifOrientationTag(ContentResolver resolver, Uri imageUri) {
// We only support parsing EXIF orientation tag from local file on the device.
// See also:
// https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html
if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())
&& !ContentResolver.SCHEME_FILE.equals(imageUri.getScheme())) {
return 0;
}
ExifInterface exif;
try (InputStream inputStream = resolver.openInputStream(imageUri)) {
if (inputStream == null) {
return 0;
}
exif = new ExifInterface(inputStream);
} catch (IOException e) {
LogUtils.e("failed to open file to read rotation meta data: " + imageUri, e);
return 0;
}
return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
}
/**
* Converts YUV_420_888 to NV21 bytebuffer.
*
* <p>The NV21 format consists of a single byte array containing the Y, U and V values. For an
* image of size S, the first S positions of the array contain all the Y values. The remaining
* positions contain interleaved V and U values. U and V are subsampled by a factor of 2 in both
* dimensions, so there are S/4 U values and S/4 V values. In summary, the NV21 array will contain
* S Y values followed by S/4 VU values: YYYYYYYYYYYYYY(...)YVUVUVUVU(...)VU
*
* <p>YUV_420_888 is a generic format that can describe any YUV image where U and V are subsampled
* by a factor of 2 in both dimensions. {@link Image#getPlanes} returns an array with the Y, U and
* V planes. The Y plane is guaranteed not to be interleaved, so we can just copy its values into
* the first part of the NV21 array. The U and V planes may already have the representation in the
* NV21 format. This happens if the planes share the same buffer, the V buffer is one position
* before the U buffer and the planes have a pixelStride of 2. If this is case, we can just copy
* them to the NV21 array.
*/
public static ByteBuffer yuv420ThreePlanesToNV21(Image.Plane[] yuv420888planes, int width, int height) {
int imageSize = width * height;
byte[] out = new byte[imageSize + 2 * (imageSize / 4)];
if (areUVPlanesNV21(yuv420888planes, width, height)) {
// Copy the Y values.
yuv420888planes[0].getBuffer().get(out, 0, imageSize);
ByteBuffer uBuffer = yuv420888planes[1].getBuffer();
ByteBuffer vBuffer = yuv420888planes[2].getBuffer();
// Get the first V value from the V buffer, since the U buffer does not contain it.
vBuffer.get(out, imageSize, 1);
// Copy the first U value and the remaining VU values from the U buffer.
uBuffer.get(out, imageSize + 1, 2 * imageSize / 4 - 1);
} else {
// Fallback to copying the UV values one by one, which is slower but also works.
// Unpack Y.
unpackPlane(yuv420888planes[0], width, height, out, 0, 1);
// Unpack U.
unpackPlane(yuv420888planes[1], width, height, out, imageSize + 1, 2);
// Unpack V.
unpackPlane(yuv420888planes[2], width, height, out, imageSize, 2);
}
return ByteBuffer.wrap(out);
}
/**
* Checks if the UV plane buffers of a YUV_420_888 image are in the NV21 format.
*/
private static boolean areUVPlanesNV21(Image.Plane[] planes, int width, int height) {
int imageSize = width * height;
ByteBuffer uBuffer = planes[1].getBuffer();
ByteBuffer vBuffer = planes[2].getBuffer();
// Backup buffer properties.
int vBufferPosition = vBuffer.position();
int uBufferLimit = uBuffer.limit();
// Advance the V buffer by 1 byte, since the U buffer will not contain the first V value.
vBuffer.position(vBufferPosition + 1);
// Chop off the last byte of the U buffer, since the V buffer will not contain the last U value.
uBuffer.limit(uBufferLimit - 1);
// Check that the buffers are equal and have the expected number of elements.
boolean areNV21 =
(vBuffer.remaining() == (2 * imageSize / 4 - 2)) && (vBuffer.compareTo(uBuffer) == 0);
// Restore buffers to their initial state.
vBuffer.position(vBufferPosition);
uBuffer.limit(uBufferLimit);
return areNV21;
}
/**
* Unpack an image plane into a byte array.
*
* <p>The input plane data will be copied in 'out', starting at 'offset' and every pixel will be
* spaced by 'pixelStride'. Note that there is no row padding on the output.
*/
private static void unpackPlane(
Image.Plane plane, int width, int height, byte[] out, int offset, int pixelStride) {
ByteBuffer buffer = plane.getBuffer();
buffer.rewind();
// Compute the size of the current plane.
// We assume that it has the aspect ratio as the original image.
int numRow = (buffer.limit() + plane.getRowStride() - 1) / plane.getRowStride();
if (numRow == 0) {
return;
}
int scaleFactor = height / numRow;
int numCol = width / scaleFactor;
// Extract the data in the output buffer.
int outputPos = offset;
int rowStart = 0;
for (int row = 0; row < numRow; row++) {
int inputPos = rowStart;
for (int col = 0; col < numCol; col++) {
out[outputPos] = buffer.get(inputPos);
outputPos += pixelStride;
inputPos += plane.getPixelStride();
}
rowStart += plane.getRowStride();
}
}
}

View File

@@ -48,6 +48,7 @@
<attr name="pointDrawable" format="reference"/> <attr name="pointDrawable" format="reference"/>
<attr name="showPointAnim" format="boolean"/> <attr name="showPointAnim" format="boolean"/>
<attr name="laserDrawable" format="reference"/> <attr name="laserDrawable" format="reference"/>
<attr name="laserDrawableRatio" format="float"/>
<attr name="viewfinderStyle" format="enum"> <attr name="viewfinderStyle" format="enum">
<enum name="classic" value="0"/> <enum name="classic" value="0"/>
<enum name="popular" value="1"/> <enum name="popular" value="1"/>