Android Studio 2023集成ZXing 3.5.3全流程实战指南在移动应用开发中二维码扫描功能已成为许多应用的标配功能。作为Android开发者如何高效集成成熟的扫码库ZXing并解决实际开发中的各种问题是每个开发者都需要掌握的技能。本文将带你从零开始在Android Studio 2023中完整集成ZXing 3.5.3版本并解决Gradle冲突、竖屏适配等常见问题。1. 环境准备与基础配置1.1 创建新项目首先在Android Studio中创建一个新项目这里有几个关键点需要注意包名建议设置为com.google.zxing.client.android这样可以避免后续手动修改大量代码最低API级别建议设置为24Android 7.0语言选择JavaZXing核心库基于Java// build.gradle(Module:app)中的基本配置 android { compileSdk 33 defaultConfig { applicationId com.google.zxing.client.android minSdk 24 targetSdk 33 // 其他配置... } // 其他配置... }1.2 配置Gradle依赖ZXing的核心库已经发布在Maven中央仓库我们可以直接通过Gradle依赖引入dependencies { implementation com.google.zxing:core:3.5.3 // 其他依赖... }提示如果遇到依赖下载慢的问题可以配置阿里云镜像仓库加速下载// settings.gradle pluginManagement { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/public } gradlePluginPortal() } }2. 核心功能集成2.1 相机权限配置二维码扫描需要相机权限需要在AndroidManifest.xml中添加uses-permission android:nameandroid.permission.CAMERA / uses-feature android:nameandroid.hardware.camera / uses-feature android:nameandroid.hardware.camera.autofocus android:requiredfalse /同时需要处理Android 6.0以上的运行时权限申请// 在Activity中检查权限 private boolean checkCameraPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ! PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST); return false; } return true; } Override public void onRequestPermissionsResult(int requestCode, NonNull String[] permissions, NonNull int[] grantResults) { if (requestCode CAMERA_PERMISSION_REQUEST) { if (grantResults.length 0 grantResults[0] PackageManager.PERMISSION_GRANTED) { // 权限已授予初始化扫描 initScanner(); } } }2.2 扫码界面布局ZXing提供了一个完整的扫码界面我们可以直接使用!-- capture.xml -- FrameLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent SurfaceView android:idid/preview_view android:layout_widthmatch_parent android:layout_heightmatch_parent/ com.google.zxing.client.android.ViewfinderView android:idid/viewfinder_view android:layout_widthmatch_parent android:layout_heightmatch_parent/ TextView android:idid/status_view android:layout_widthwrap_content android:layout_heightwrap_content android:layout_gravitybottom|center_horizontal android:textstring/msg_default_status/ /FrameLayout3. 核心扫描功能实现3.1 初始化CameraManagerCameraManager是ZXing中管理相机操作的核心类public class ScannerActivity extends AppCompatActivity implements SurfaceHolder.Callback { private CameraManager cameraManager; private SurfaceView surfaceView; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.capture); surfaceView findViewById(R.id.preview_view); SurfaceHolder holder surfaceView.getHolder(); holder.addCallback(this); cameraManager new CameraManager(getApplication()); } Override public void surfaceCreated(SurfaceHolder holder) { try { cameraManager.openDriver(holder); // 开始预览 cameraManager.startPreview(); } catch (IOException e) { Log.e(TAG, Camera init error: e.getMessage()); } } // 其他回调方法... }3.2 解码处理ZXing使用DecodeHandler来处理相机捕获的图像数据public class DecodeHandler extends Handler { private final CaptureActivity activity; private final MultiFormatReader multiFormatReader; public DecodeHandler(CaptureActivity activity, MapDecodeHintType,Object hints) { this.activity activity; multiFormatReader new MultiFormatReader(); multiFormatReader.setHints(hints); } Override public void handleMessage(Message message) { if (message.what R.id.decode) { decode((byte[]) message.obj, message.arg1, message.arg2); } } private void decode(byte[] data, int width, int height) { // 处理竖屏情况 if (isScreenPortrait()) { byte[] rotatedData new byte[data.length]; for (int y 0; y height; y) { for (int x 0; x width; x) rotatedData[x * height height - y - 1] data[x y * width]; } int tmp width; width height; height tmp; data rotatedData; } PlanarYUVLuminanceSource source activity.getCameraManager() .buildLuminanceSource(data, width, height); BinaryBitmap bitmap new BinaryBitmap(new HybridBinarizer(source)); try { Result rawResult multiFormatReader.decodeWithState(bitmap); // 处理解码结果 Message message Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult); message.sendToTarget(); } catch (ReaderException re) { // 解码失败 } finally { multiFormatReader.reset(); } } private boolean isScreenPortrait() { WindowManager manager (WindowManager) activity .getSystemService(Context.WINDOW_SERVICE); Display display manager.getDefaultDisplay(); Point screenResolution new Point(); display.getSize(screenResolution); return screenResolution.x screenResolution.y; } }4. 竖屏适配解决方案ZXing默认是为横屏设计的需要进行以下修改才能支持竖屏4.1 AndroidManifest配置activity android:name.CaptureActivity android:screenOrientationportrait android:configChangesorientation|keyboardHidden|screenSize !-- 其他配置 -- /activity4.2 CameraManager修改修改getFramingRectInPreview()方法以适应竖屏public synchronized Rect getFramingRectInPreview() { if (framingRectInPreview null) { Rect framingRect getFramingRect(); if (framingRect null) { return null; } Rect rect new Rect(framingRect); Point cameraResolution configManager.getCameraResolution(); Point screenResolution configManager.getScreenResolution(); if (cameraResolution null || screenResolution null) { return null; } boolean isScreenPortrait screenResolution.x screenResolution.y; boolean isCameraSizePortrait cameraResolution.x cameraResolution.y; if (isScreenPortrait isCameraSizePortrait) { // 横屏模式 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; } else { // 竖屏模式 rect.left rect.left * cameraResolution.y / screenResolution.x; rect.right rect.right * cameraResolution.y / screenResolution.x; rect.top rect.top * cameraResolution.x / screenResolution.y; rect.bottom rect.bottom * cameraResolution.x / screenResolution.y; } framingRectInPreview rect; } return framingRectInPreview; }4.3 解码方向调整在DecodeHandler中增加竖屏数据处理private void decode(byte[] data, int width, int height) { if (isScreenPortrait()) { byte[] rotatedData new byte[data.length]; for (int y 0; y height; y) { for (int x 0; x width; x) rotatedData[x * height height - y - 1] data[x y * width]; } int tmp width; width height; height tmp; data rotatedData; } // 后续解码逻辑... }5. 常见问题解决方案5.1 Gradle版本冲突如果遇到类似错误Unable to find method java.lang.String org.gradle.api.artifacts.component.BuildIdentifier.getBuildPath()解决方案确保使用Gradle 8.0或更高版本修改gradle-wrapper.propertiesdistributionUrlhttps\://services.gradle.org/distributions/gradle-8.0-all.zip5.2 相机初始化失败相机初始化常见问题及解决方案问题现象可能原因解决方案相机黑屏相机权限未授予检查权限并动态申请预览变形预览尺寸不匹配调整CameraConfiguration中的预览尺寸对焦失败设备不支持自动对焦检查getSupportedFocusModes()闪光灯无效闪光灯模式设置错误检查setFlashMode()参数5.3 扫码区域调整默认扫码区域可能不适合所有设备可以通过以下方式调整// 在CameraConfigurationManager中修改 public void setDesiredCameraParameters(Camera camera) { Camera.Parameters parameters camera.getParameters(); // 设置扫码区域 if (parameters.getMaxNumFocusAreas() 0) { ListCamera.Area focusAreas new ArrayList(); Rect framingRect cameraManager.getFramingRect(); Rect framingRectInPreview cameraManager.getFramingRectInPreview(); focusAreas.add(new Camera.Area(framingRectInPreview, 1000)); parameters.setFocusAreas(focusAreas); } // 其他参数设置... camera.setParameters(parameters); }6. 高级功能扩展6.1 自定义扫码界面如果想自定义扫码界面可以继承ViewfinderViewpublic class CustomViewfinderView extends ViewfinderView { private final Paint laserPaint; public CustomViewfinderView(Context context, AttributeSet attrs) { super(context, attrs); laserPaint new Paint(); laserPaint.setColor(Color.GREEN); laserPaint.setStrokeWidth(5f); } Override public void onDraw(Canvas canvas) { super.onDraw(canvas); // 自定义绘制逻辑 Rect frame getFramingRect(); if (frame ! null) { // 绘制扫描线动画 canvas.drawLine(frame.left, frame.top 10, frame.right, frame.top 10, laserPaint); } } }6.2 多码识别ZXing支持同时识别多种格式的码MapDecodeHintType,Object hints new EnumMap(DecodeHintType.class); CollectionBarcodeFormat formats Arrays.asList( BarcodeFormat.QR_CODE, BarcodeFormat.CODE_128, BarcodeFormat.EAN_13); hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); MultiFormatReader reader new MultiFormatReader(); reader.setHints(hints);6.3 生成二维码ZXing也可以用于生成二维码public Bitmap generateQRCode(String content, int width, int height) { try { MapEncodeHintType, Object hints new HashMap(); hints.put(EncodeHintType.CHARACTER_SET, UTF-8); hints.put(EncodeHintType.MARGIN, 1); BitMatrix matrix new QRCodeWriter().encode( content, BarcodeFormat.QR_CODE, width, height, hints); Bitmap bitmap Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); for (int x 0; x width; x) { for (int y 0; y height; y) { bitmap.setPixel(x, y, matrix.get(x, y) ? Color.BLACK : Color.WHITE); } } return bitmap; } catch (WriterException e) { e.printStackTrace(); return null; } }7. 性能优化建议相机参数优化选择合适的预览尺寸根据设备能力设置对焦模式合理设置曝光补偿解码性能优化限制解码区域大小适当降低解码频率使用TRY_HARDER模式仅在需要时启用内存管理及时释放相机资源避免在解码过程中创建大量临时对象使用对象池复用ByteArray等资源// 示例优化后的相机初始化 public void openDriver(SurfaceHolder holder) throws IOException { if (camera null) { camera Camera.open(); if (camera null) { throw new IOException(); } camera.setPreviewDisplay(holder); Camera.Parameters parameters camera.getParameters(); // 设置最佳预览尺寸 Camera.Size optimalSize getOptimalPreviewSize( parameters.getSupportedPreviewSizes(), screenWidth, screenHeight); parameters.setPreviewSize(optimalSize.width, optimalSize.height); // 设置最佳对焦模式 if (parameters.getSupportedFocusModes().contains( Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } camera.setParameters(parameters); } }通过以上步骤你应该已经成功在Android Studio 2023中集成了ZXing 3.5.3并解决了常见的兼容性和适配问题。实际开发中建议根据具体需求对扫码界面和功能进行进一步定制以提供最佳的用户体验。