前言
最近接到个需求,内容是利用客户端去扫描书本上的ISBN码,并将ISBN传给服务器。
需求不难,稍微调研了一下发现大名鼎鼎的Google ZXing库就可以实现扫描ISBN的功能,无知的我一直以为ZXing是专门扫描二维码的三方库。
上一次接触ZXing还是当初刚刚做Android的时候,想想我还是挺前沿的,但是没想到这么多年过去了,ZXing的集成依然是件麻烦事,一时也是有点唏嘘,所以写这篇文章来记录一下如何集成ZXing到项目中以及会遇到什么问题。
集成
先讲一下如何集成,基本上网络上这种文章也是一搜一大把的,但是看归看,实践起来还是不一样的,往往是你遇到的情况比他们更复杂。
1)首先去github上的ZXing仓库https://github.com/zxing/zxing去把整个项目download下来并解压出来,可以看到其中有个android文件夹
2)打开自己的项目,这里就默认大家都用Android Studio了,如果有土豪用IntelliJ IDEA应该也差不多,然后File>New>Import Module,选择ZXing项目中的android进行导入
3)导入之后在Module的build.gradle中将apply plugin: 'com.android.application'修改为apply plugin: 'com.android.library',因为你需要把它当作一个库而不是一个独立的项目
4)接着是添加ZXing的依赖,否则项目中依然会有很多文件找不到。看到有的文章提到可以这么依赖:
在Module的build.gradle添加
dependencies {
implementation 'com.google.zxing:zxing-parent:3.4.0'
implementation 'com.google.zxing:zxing.appspot.com:3.4.0'
implementation 'com.google.zxing:zxingorg:3.4.0'
}
这种方式如果只是运行demo,在我这边是没有问题的,但是打包的时候发现出现了一个异常提示并且打包失败了:
No variants of com.google.zxing:zxing.appspot.com:3.4.0 match the consumer attributes
搜索了一番也是没有头绪,姑且认为是自己配置的问题吧,于是我又看到有人使用的是直接依赖ZXing的jar包的方式,于是就去下载jar包,这里贴一下下载的地址,我下载的是3.4.0的版本:
https://mvnrepository.com/artifact/com.google.zxing/core
可以自己选择版本进行下载,下载之后在对应的Module之下和src同级的位置新建一个libs文件夹,然后把jar包放进去,右键jar包Add as library,这个时候我就发现可以正常打包了。
贴一下我的dependencies写法
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation files('libs/core-3.4.0.jar')
}
遇到的问题
添加完依赖,sync之后会发现一些错误提示:
1)The minSdk version should not be declared in the android manifest file. You can move the version from the manifest to the defaultConfig in the build.gradle file. Remove minSdkVersion and sync project
到Module的AndroidManifest.xml中删除<uses-sdk>中的minSdkVersion即可,当然targetSdkVersion也可以一并删除了,<manifest>之下的versionName和versionCode也会被主项目覆盖而失效,所以也可以删了
2)
Library projects cannot set applicationId. applicationId is set to 'com.google.zxing.client.android' in default config.
删除Module下的build.gradle中的applicationId "com.google.zxing.client.android"然后sync
3)编译之后又发现了一堆错误,不怕,一个一个解决
TIM截图20200702092656.png
首先是找不到CameraConfigurationUtils,这个简单,复制ZXing仓库中android-core下的CameraConfigurationUtils文件,复制到Module的camera包之下即可。
接着是这个错误,写成new ArrayList<HistoryItem>()即可。
TIM截图20200702093025.png
然后把Module中所有使用switch的地方改成if语句,这是因为Module的R文件中的常量缺少final,由于Module可以包含资源文件,编译会生成R文件,多个Module中可能出现id冲突的问题,为了解决这个问题谷歌将Module的R文件从静态常量变为非常量。然而switch语句的case中必须是常量,而此时的R.id.xxx为非常量,所以报错了。
TIM截图20200702093138.png
4)删除Module下的AndroidManifest.xml的<application>中的icon、logo和label,否则会和主项目有冲突
TIM截图20200702094614.png
5)删除Module下的AndroidManifest.xml的CaptureActivity下的<category>为LAUNCHER的<intent-filter>,否则主项目依赖这个Module之后会直接打开这个Module的启动Activity而不是主项目的启动Activity
TIM截图20200702093856.png
依赖Module
1)在主项目上按一下F4,通过图片的流程去依赖这个Module
TIM截图20200702164307.png
2)在主项目的AndroidManifest.xml中增加如下权限:
其中CAMERA为必须的权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.VIBRATE" />
3)怎么打开ZXing
打开之前确定是否申请到了相机权限,相机权限是属于危险权限,如果没有申请就需要向用户申请,否则无法打开ZXing,关于危险权限可以参考我的另一篇文章:
https://www.jianshu.com/p/77077c202c74
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(this, CaptureActivity.class);
startActivityForResult(intent, XXX);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, XXX);
}
优化点
基本上算是集成好了,不过还有一些需要优化的地方
1)首先是可以在桌面上看到App的名称变成了"条码扫描器",这个简单,去Module中把values-zh-rCN下app_name删了即可
2)其他的类似做国际化的文件夹,assets文件夹,如果没有特殊需求的话也可以删了,毕竟体积也不小
3)如何隐藏顶部的ActionBar及菜单按钮:
菜单按钮:
在CaptureActivity的onCreateOptionsMenu()中注释掉以下代码
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.capture, menu);
ActionBar:
去res>values>themes中修改主题,设置成任意NoActionBar的主题即可
<style name="CaptureTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
4)最关键的来了,怎么设置成竖屏模式,由于默认竖屏时不能扫描条形码,即使UI变成竖屏了也必须横屏才能扫描,这就十分不方便,所以有必要修改成竖屏时也可以扫描条形码的状态:
首先修改Module下的AndroidManifest.xml的CaptureActivity的android:screenOrientation为portrait,这样保证了UI是竖屏的。
在CameraConfigurationUtils的findBestPreviewSizeValue()中:
修改
// double screenAspectRatio = screenResolution.x / (double) screenResolution.y;
double screenAspectRatio;
if (screenResolution.x < screenResolution.y) { // 竖屏
screenAspectRatio = (double) screenResolution.y / (double) screenResolution.x;
} else {
screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
}
注释
// if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
// Point exactPoint = new Point(realWidth, realHeight);
// Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
// return exactPoint;
// }
在CameraManager的getFramingRectInPreview()中:
修改
// rect.left = rect.left * cameraResolution.x / screenResolution.x;
// rect.right = rect.right * cameraResolution.x / screenResolution.x;
// rect.top = rect.top * cameraResolution.y / screenResolution.y;
// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
if (screenResolution.x < screenResolution.y) {//竖屏
rect.left = framingRect.left * cameraResolution.y / screenResolution.x;
rect.right = framingRect.right * cameraResolution.y / screenResolution.x;
rect.top = framingRect.top * cameraResolution.x / screenResolution.y;
rect.bottom = framingRect.bottom * cameraResolution.x / screenResolution.y;
} else {//横屏
rect.left = framingRect.left * cameraResolution.x / screenResolution.x;
rect.right = framingRect.right * cameraResolution.x / screenResolution.x;
rect.top = framingRect.top * cameraResolution.y / screenResolution.y;
rect.bottom = framingRect.bottom * cameraResolution.y / screenResolution.y;
}
在CameraManager的buildLuminanceSourc()中:
修改
/* // Go ahead and assume it's YUV rather than die.
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
rect.width(), rect.height(), false);*/
PlanarYUVLuminanceSource source;
Point point = configManager.getScreenResolution();
if (point.x < point.y) {
byte[] rotatedData = new byte[data.length];
int newWidth = height;
int newHeight = width;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * newWidth + newWidth - 1 - y] = data[x + y * width];
}
source = new PlanarYUVLuminanceSource(rotatedData, newWidth, newHeight,
rect.left, rect.top, rect.width(), rect.height(), false);
} else {
source = new PlanarYUVLuminanceSource(data, width, height,
rect.left, rect.top, rect.width(), rect.height(), false);
}
return source;// TODO: 2020/7/2 适配竖屏
到此基本上能正常工作了,可以愉快地扫描ISBN了。
在此感谢
https://www.jianshu.com/p/60820c83fcb7
https://www.jianshu.com/p/badc97fde6cf
https://www.jianshu.com/p/4fccee28abb9
网友评论