Android studio2023接入Zxing 3.5.3

1.zxing库下载

GitHub - zxing/zxing: ZXing ("Zebra Crossing") barcode scanning library for Java, AndroidZXing ("Zebra Crossing") barcode scanning library for Java, Android - zxing/zxingicon-default.png?t=N7T8https://github.com/zxing/zxing

2.选择版本

 3.我选择的是3.5.3

4.zxing的核心jar包下载

Central Repository: com/google/zxing/core (maven.org)icon-default.png?t=N7T8https://repo1.maven.org/maven2/com/google/zxing/core/选择同一个版本,我还是下载3.5.3

5.将项目导入 

6.然后新建文件夹

7.将核心包放进来

再导入

 8.android-core文件夹里面的CameraConfigurationUtils导入到项目里 否则报错

折腾好几天没成功,最后手动移植代码

1.新建一个空项目

新建工程 包名设置为com.google.zxing.client.android(与zxing android工程包名一样,省去一些手工导包修改过程操作) 

切记包名 com.google.zxing.client.android

 1.同步一下

2.再build下 确定有没有问题再继续

更换镜像地址

使用腾讯的地址

https\://mirrors.cloud.tencent.com/gradle/gradle-8.0-all.zip

##Sun Jun 02 13:39:24 CST 2024
#distributionBase=GRADLE_USER_HOME
#distributionPath=wrapper/dists
#distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
#zipStoreBase=GRADLE_USER_HOME
#zipStorePath=wrapper/dists

#Sat Jun 01 16:52:27 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
#distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.0-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

添加阿里依赖下载地址 

//pluginManagement {
//    repositories {
//        google()
//        mavenCentral()
//        gradlePluginPortal()
//    }
//}
//dependencyResolutionManagement {
//    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
//    repositories {
//        google()
//        mavenCentral()
//    }
//}
//
//rootProject.name = "zwing20240601"
//include ':app'
pluginManagement {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        google()
        mavenCentral()
    }
}

rootProject.name = "myzxing"
include ':app'

打开工程目录

移植代码android

官方代码路径

C:\Users\Administrator\Desktop\zxing-master\android\src\com\google\zxing\client\android

空项码代码路径

C:\Users\Administrator\AndroidStudioProjects\zwing20240601\app\src\main\java\com\google\zwing\client\android

android\src\com\google\zxing\client\android下全部复制进去

grade最低版本改为8.2

切记grade版本应为8.2

不能为8.0否则

Unable to find method ''java.lang.String org.gradle.api.artifacts.component.BuildIdentifier.getBuildPath()''
'java.lang.String org.gradle.api.artifacts.component.BuildIdentifier.getBuildPath()'

Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)

Re-download dependencies and sync project (requires network)
The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.

Stop Gradle build processes (requires restart)
Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.

In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes

解决报错问题

集成zwing库

引入zxing核心架包core 暂不引入 android-core
dependencies {

    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

    //集成zxing
    implementation 'com.google.zxing:core:3.5.3'
//    implementation 'com.google.zxing:android-core:3.5.3'

    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

全部源码

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.google.zxing.client.android'
    compileSdk 33

    defaultConfig {
        applicationId "com.google.zxing.client.android"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

    //集成zxing
    implementation 'com.google.zxing:core:3.5.3'

    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

///

/

以下为扩展功能

zxing竖屏修改

AndroidManifest.xmlCaptureActivity设置竖屏android:screenOrientation="portrait 

  <activity android:name=".CaptureActivity"
            android:screenOrientation="portrait"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/CaptureTheme"
            android:windowSoftInputMode="stateAlwaysHidden">

 CameraManager.java 中方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) {
                // Called early, before init even finished
                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;
    }

 DecodeHandler.java 中decode(byte[] data, int width, int height) 修改

 private void decode(byte[] data, int width, int height) {
        long start = System.nanoTime();
        Result rawResult = null;
        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);
        if (source != null) {
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            try {
                rawResult = multiFormatReader.decodeWithState(bitmap);
            } catch (ReaderException re) {
                // continue
            } 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;
    }

CameraManager.java 

扫描区域偏上getFramingRect()

framingRect = new Rect(leftOffset/2,leftOffset, leftOffset +leftOffset/2+ width, leftOffset + height/2);

Android自定义相机定点聚焦,扫码区自动对焦openDriver(SurfaceHolder holder)

    if (parameters !=null && parameters.getMaxNumFocusAreas() >0){
      Rect framingRectInPreview = getFramingRect();
      list<Camera.Area>focusArealist = new ArrayList<>();
      focusArealist.add(new Camera.Area(framingRectInPreview,weight:1000));
      parameters.setFocusAreas(focusArealist);//设置聚焦区域
      cameraObject.setParameters(parameters);
    }

1.setFocusAreas(List<Area> focusAreas)方法

    /**
     * Sets focus areas. See {@link #getFocusAreas()} for documentation.
     *
     * @param focusAreas the focus areas
     * @see #getFocusAreas()
     */
    public void setFocusAreas(List<Area> focusAreas) {
        set(KEY_FOCUS_AREAS, focusAreas);
    }

 2.getFocusAreas()方法

    /**
     * <p>Gets the current focus areas. Camera driver uses the areas to decide
     * focus.</p>
     *
     * <p>Before using this API or {@link #setFocusAreas(List)}, apps should
     * call {@link #getMaxNumFocusAreas()} to know the maximum number of
     * focus areas first. If the value is 0, focus area is not supported.</p>
     *
     * @return a list of current focus areas
     */
    public List<Area> getFocusAreas() {
        return splitArea(get(KEY_FOCUS_AREAS));
    }

3.getMaxNumFocusAreas()方法

    /**
     * Gets the maximum number of focus areas supported. This is the maximum
     * length of the list in {@link #setFocusAreas(List)} and
     * {@link #getFocusAreas()}.
     *
     * @return the maximum number of focus areas supported by the camera.
     * @see #getFocusAreas()
     */
    public int getMaxNumFocusAreas() {
        return getInt(KEY_MAX_NUM_FOCUS_AREAS, 0);
    }

 4.Camera.Area静态内部类

    /**
     * <p>The Area class is used for choosing specific metering and focus areas for
     * the camera to use when calculating auto-exposure, auto-white balance, and
     * auto-focus.</p>
     */
    public static class Area {}

 就是指测光区域和聚焦区域,用于相机计算自动曝光、自动白平衡、自动聚焦

    /**
     * Create an area with specified rectangle and weight.
     *
     * @param rect the bounds of the area.
     * @param weight the weight of the area.
     */
    public Area(Rect rect, int weight) {
        this.rect = rect;
        this.weight = weight;
    }

Rect就是一个矩形区域,weight是一个权重值,代表其重要程度。官方注释中对这两个参数的解释为

    /**
     * <p>Each Area consists of a rectangle specifying its bounds, and a weight
     * that determines its importance. The bounds are relative to the camera's
     * current field of view. The coordinates are mapped so that (-1000, -1000)
     * is always the top-left corner of the current field of view, and (1000,
     * 1000) is always the bottom-right corner of the current field of
     * view. Setting Areas with bounds outside that range is not allowed. Areas
     * with zero or negative width or height are not allowed.</p>
     *
     * <p>The weight must range from 1 to 1000, and represents a weight for
     * every pixel in the area. This means that a large metering area with
     * the same weight as a smaller area will have more effect in the
     * metering result.  Metering areas can overlap and the driver
     * will add the weights in the overlap region.</p>
     */

 以下代码来自网络收集方便使用

//Android自定义相机实现自动对焦和手动对焦
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;

import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.hp.classes.config.Constants;
import com.hp.classes.tools.CommonUtils;
import com.hp.classes.ui.BaseActivity;

@SuppressWarnings("deprecation")
public class PhotographActivity extends BaseActivity implements OnClickListener, SurfaceHolder.Callback {
  private SurfaceView surfaceView;
  private Camera camera;
  private Camera.Parameters parameters;
  private Button btn_goback, btn_takephoto;
  private SurfaceHolder surfaceHolder;
  
  @Override
  protected void onDestroy() {
    super.onDestroy();
    if(camera != null){
      camera.stopPreview();
      camera.release();
      camera = null;
    }
  }
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.classes_activity_takephoto);
    initView();
  }

  @Override
  protected void onResume() {
    super.onResume();
    initCamera();
  }
  private void initView(){
    btn_goback = (Button) findViewById(R.id.btn_goback);
    btn_goback.setOnClickListener(this);
    btn_takephoto = (Button) findViewById(R.id.btn_takephoto);
    btn_takephoto.setOnClickListener(this);

    surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
    surfaceView.setFocusable(true);
    surfaceView.setOnClickListener(this);
    surfaceView.setBackgroundColor(TRIM_MEMORY_BACKGROUND);
    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    surfaceHolder.setKeepScreenOn(true);
    surfaceHolder.setFixedSize(400, 300);
    surfaceHolder.addCallback(this);
  }
  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
    // TODO Auto-generated method stub
    camera.stopPreview();
    camera.release();
    camera = null;

  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    // TODO Auto-generated method stub
    try {
      camera.setPreviewDisplay(surfaceHolder);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // 实现自动对焦
    camera.autoFocus(new AutoFocusCallback() {
      @Override
      public void onAutoFocus(boolean success, Camera camera) {
        if (success) {
          camera.cancelAutoFocus();// 只有加上了这一句,才会自动对焦
          doAutoFocus();
        }
      }
    });
  }

  // 相机参数的初始化设置
  private void initCamera() {
    if (null == camera) {
      camera = Camera.open();
    }
    parameters = camera.getParameters();
    parameters.setPictureFormat(PixelFormat.JPEG);
    parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
    if (!Build.MODEL.equals("KORIDY H30")) {
      parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
    }else{
      parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    }
    camera.setParameters(parameters);
    setDispaly(camera);
    camera.startPreview();
    camera.cancelAutoFocus();// 2如果要实现连续的自动对焦,这一句必须加上
  }

  // 控制图像的正确显示方向
  private void setDispaly(Camera camera) {
    if (Integer.parseInt(Build.VERSION.SDK) >= 8) {
      setDisplayOrientation(camera, -90);
    } else {
      parameters.setRotation(-90);
    }

  }

  // 实现的图像的正确显示
  private void setDisplayOrientation(Camera camera, int i) {
    Method downPolymorphic;
    try {
      downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
      if (downPolymorphic != null) {
        downPolymorphic.invoke(camera, new Object[] { i });
      }
    } catch (Exception e) {
      Log.e("Came_e", "图像出错");
    }
  }

  @Override
  public void onClick(View v) {
    switch (v.getId()) {
    case R.id.surfaceView:
      doAutoFocus();
      break;
    case R.id.btn_takephoto:
      takePicture();
      break;
    case R.id.btn_goback:
      finish();
      break;
    default:
      break;
    }
  }

  // handle button auto focus
  private void doAutoFocus() {
    parameters = camera.getParameters();
    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    camera.setParameters(parameters);
    camera.autoFocus(new AutoFocusCallback() {
      @Override
      public void onAutoFocus(boolean success, Camera camera) {
        if (success) {
          camera.cancelAutoFocus();// 只有加上了这一句,才会自动对焦。
          if (!Build.MODEL.equals("KORIDY H30")) {
            parameters = camera.getParameters();
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
            camera.setParameters(parameters);
          }else{
            parameters = camera.getParameters();
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            camera.setParameters(parameters);
          }
        }
      }
    });
  }

  private void takePicture() {
    camera.takePicture(shutterCallback, rawCallback, jpegCallback);
  }

  // define shutterCallback
  ShutterCallback shutterCallback = new ShutterCallback() {
    public void onShutter() {
      // TODO Do something when the shutter closes.
    }
  };

  PictureCallback rawCallback = new PictureCallback() {
    public void onPictureTaken(byte[] data, Camera camera) {
      // TODO Do something with the image RAW data.
    }
  };

  // stroe the picture in format jpeg
  PictureCallback jpegCallback = new PictureCallback() {
    public void onPictureTaken(byte[] data, Camera camera) {
      // Save the image JPEG data to the SD card
      FileOutputStream outStream = null;
      try {
        //修改图片路径和名称
        String tempFilename = String.valueOf(System.currentTimeMillis()) + ".jpg";
        File folder = Constants.CACHE_FOLDER;
        if (!folder.isDirectory()) {
          folder.mkdirs();
        }
        String path = Constants.CACHE_FOLDER + File.separator + tempFilename;
        outStream = new FileOutputStream(path);
        outStream.write(data);
        outStream.flush();
        outStream.close();
        surfaceView.setBackgroundDrawable(new BitmapDrawable(BitmapFactory.decodeByteArray(data, 0, data.length)));
      } catch (FileNotFoundException e) {
        Log.e("TAG", "File Note Found", e);
      } catch (IOException e) {
        Log.e("TAG", "IO Exception", e);
      }
    }
  };
}

使用Camera需要Camera权限,首先添加权限:代码块 

<uses-permission android:name="android.permission.CAMERA" />

//动态申请权限
/**
 * 检查相机权限
 * @return 是否获取权限
 */
private boolean checkPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=
            PackageManager.PERMISSION_GRANTED) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
            Toast.makeText(this, "此应用需要使用相机权限", Toast.LENGTH_SHORT).show();
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUESTCODE);
        }
        Log.i("xk", "permission denied!");
        return false;
    } else {
        Log.i("xk", "permit successfully!");
        return true;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    Log.i("xk", "onRequestPermissionsResult");
    if (requestCode == CAMERA_REQUESTCODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            startScan();
        } else {
            Toast.makeText(this, "相机权限未允许", Toast.LENGTH_SHORT).show();
        }
    }
}

权限申请完成后,接下来使用Camera离不开SurfaceView进行实时更新预览界面,所以layout文件需要定义

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/qr_code_preview_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:visibility="visible" />

    <com.test.xukun.scansimpletest.qrcode.view.QrCodeFinderView
        android:id="@+id/qr_code_view_finder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:visibility="gone" />

    <View
        android:id="@+id/qr_code_view_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        android:visibility="gone" />

</RelativeLayout>

接下来需要获取Camera对象,设置相关参数,这一部分需要在SerfaceView加载成功后进行,所以需要用到SurfaceHolder.Callback,载入SerfaceView的Activity中实现SurfaceHolder.Callback接口,在其surfaceCreated方法中进行初始化操作

// CameraManager.class

public void openDriver(SurfaceHolder holder) throws IOException {
    SDKLog.d(TAG,"-->openDriver");
    if (mCamera == null) {
        // 入参为摄像头id:0为后置摄像头
        mCamera = Camera.open(0);
        if (mCamera == null) {
            throw new IOException();
        }

        if (!mInitialized) {
            mInitialized = true;
            // 获取预览界面的尺寸
            mConfigManager.initFromCameraParameters(mCamera);
        }
        // 设置Camera参数
        mConfigManager.setDesiredCameraParameters(mCamera);
        // 设置SerfaceHolder
        mCamera.setPreviewDisplay(holder);
    }
}

// CameraConfigurationManager.class

void initFromCameraParameters(Camera camera) {
    Camera.Parameters parameters = camera.getParameters();
    int sWidth = ScreenUtils.getScreenWidth(mContext);
    int sHeight = ScreenUtils.getScreenHeight(mContext);
    SDKLog.d(TAG, "init preview begin size: " + sWidth + "-" + sHeight);
    if (sWidth > sHeight) {
        sHeight = sWidth * 3 / 4;
    } else {
        sWidth = sHeight * 3 / 4;
    }
    SDKLog.d(TAG, "init preview size: " + sWidth + "-" + sHeight);
    mCameraResolution = findCloselySize(sWidth, sHeight,
            parameters.getSupportedPreviewSizes());
    SDKLog.d(TAG, "Setting preview size: " + mCameraResolution.width + "-" + mCameraResolution.height);
    mPictureResolution = findCloselySize(ScreenUtils.getScreenWidth(mContext),
            ScreenUtils.getScreenHeight(mContext), parameters.getSupportedPictureSizes());
    SDKLog.d(TAG, "Setting picture size: " + mPictureResolution.width + "-" + mPictureResolution.height);
}

void setDesiredCameraParameters(Camera camera) {
    Camera.Parameters parameters = camera.getParameters();
    // 预览大小
    parameters.setPreviewSize(mCameraResolution.width, mCameraResolution.height);
    // 图片大小 拍摄图片使用
    parameters.setPictureSize(mPictureResolution.width, mPictureResolution.height);
    // flash模式
    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
    // 对焦模式
    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
    // 提高MediaRecorder录制摄像头视频性能
    parameters.setRecordingHint(true);
    // 设置摄像头捕获数据方向
    camera.setDisplayOrientation(90);
    camera.setParameters(parameters);
}

// ScreenUtils.class
/**
 * 获取屏幕宽度
 *
 * @return
 */
public static int getScreenWidth(Context context) {
    DisplayMetrics dm = context.getResources().getDisplayMetrics();
    return dm.widthPixels;
}

/**
 * 获取屏幕高度
 *
 * @return
 */
public static int getScreenHeight(Context context) {
    DisplayMetrics dm = context.getResources().getDisplayMetrics();
    return dm.heightPixels;
}

 Camera.Parameters类可设置Camera相关设置参数,像代码中注释说明的那些基本都是常用的属性

setFlashMode:闪光灯模式,共有五种模式:
public static final String FLASH_MODE_OFF = “off” //关闭
public static final String FLASH_MODE_AUTO = “auto”; //自动
public static final String FLASH_MODE_ON = “on”; //打开(拍照时)
public static final String FLASH_MODE_RED_EYE = “red-eye”; //红眼
public static final String FLASH_MODE_TORCH = “torch”; //始终开启
//setRecordingHint:这个方法就是导致打开Camera后设置闪光灯无效的原因,默认是false,如果不进行设置或者置为false,则扫码时无法根据开关控制闪光灯

开始预览,打开摄像头预览,设置预览数据callback,设置自动对焦,同时开始解码线程,等待数据返回后进行解码

// CaptureActivityHandler.class

public void restartPreviewAndDecode() {
    if (mState != State.PREVIEW) {
        SDKLog.d(TAG, "start preview!");
        CameraManager.get().startPreview();
        mState = State.PREVIEW;
        // 设置自动对焦
        CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
        // 设置预览数据回调
        CameraManager.get().requestPreviewFrame(mDecodeThread.getHandler(), R.id.decode);
    }
}

// CameraManager.class
/**
 * A single preview frame will be returned to the handler supplied. The data will arrive as byte[] in the
 * message.obj field, with width and height encoded as message.arg1 and message.arg2, respectively.
 *
 * @param handler The handler to send the message to.
 * @param message The what field of the message to be sent.
 */
public void requestPreviewFrame(Handler handler, int message) {
    if (mCamera != null && mPreviewing) {
        mPreviewCallback.setHandler(handler, message);
        //set一次 回调一次预览数据
        mCamera.setOneShotPreviewCallback(mPreviewCallback);
    }
}

/**
 * Asks the mCamera hardware to perform an autofocus.
 *
 * @param handler The Handler to notify when the autofocus completes.
 * @param message The message to deliver.
 */
public void requestAutoFocus(Handler handler, int message) {
    if (mCamera != null && mPreviewing) {
        mAutoFocusCallback.setHandler(handler, message);
        mCamera.autoFocus(mAutoFocusCallback);
    }
}

 实现Camera.PreviewCallback的类代码

final class PreviewCallback implements Camera.PreviewCallback {
    private static final String TAG = PreviewCallback.class.getSimpleName();
    private final CameraConfigurationManager mConfigManager;
    private Handler mPreviewHandler;
    private int mPreviewMessage;

    PreviewCallback(CameraConfigurationManager configManager) {
        this.mConfigManager = configManager;
    }

    void setHandler(Handler previewHandler, int previewMessage) {
        this.mPreviewHandler = previewHandler;
        this.mPreviewMessage = previewMessage;
    }
    
    /**
     * 预览数据回调方法
     * @param data 预览数据
     * @param camera camera对象
     */
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        Camera.Size cameraResolution = mConfigManager.getCameraResolution();
        if (mPreviewHandler != null) {
            Message message =
                mPreviewHandler.obtainMessage(mPreviewMessage, cameraResolution.width, cameraResolution.height, data);
            message.sendToTarget();
        } else {
            Log.v(TAG, "no handler callback.");
        }
    }
}

//    setPreviewCallback(Camera.PreviewCallback):使用此方法注册一个Camera. PreviewCallback,这将确保在屏幕上显示一个新的预览帧时调用onPreviewFrame方法。传递到onPreviewFrame方法中的数据字节数组最有可能采用YUV格式。但是,Android 2.2是第一个包含了YUV格式解码器(YuvImage)的版本;在以前的版本中,必须手动完成解码。
//    setOneShotPreviewCallback(Camera.PreviewCallback):利用Camera对象上的这个方法注册Camera.PreviewCallback,从而当下一幅预览图像可用时调用一次onPreviewFrame。同样,传递到onPreviewFrame方法的预览图像数据最有可能采用YUV格式。可以通过使用ImageFormat中的常量检查Camera. getParameters(). getPreviewFormat()返回的结果来确定这一点。
//    setPreviewCallbackWithBuffer(Camera.PreviewCallback):在Android 2.2中引入了该方法,其与setPreviewCallback的工作方式相同,但要求指定一个字节数组作为缓冲区,用于预览图像数据。这是为了能够更好地管理处理预览图像时使用的内存。

解码

   /**
     * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, reuse the same reader
     * objects from one decode to the next.
     *
     * @param data   The YUV  SP420 preview frame.
     * @param width  The width of the preview frame.
     * @param height The height of the preview frame.
     */
    private void decode(byte[] data, int width, int height) {
        SDKLog.d(TAG, "scanning  start (width,height) = " + "(" + width + "," + height + ")");
        long timebegin = System.currentTimeMillis();
        // transfer 90'
        /*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;*/
        
        ZbarManager manager = new ZbarManager();
        String result = manager.decode(data, width, height, false, 0, 0, width,
                height);
        long end = System.currentTimeMillis();
        decodetime = (int) (end - timebegin);

        if (!TextUtils.isEmpty(result)) {
            SDKLog.d(TAG, "decode code result: " + result);
            SDKLog.d(TAG, "scan success decodetime = " + decodetime);
            Message message = Message.obtain(mActivity.getCaptureActivityHandler(), R.id.decode_succeeded, result);
            message.sendToTarget();
        } else {
            SDKLog.d(TAG, "scan failed decodetime = " + decodetime);
            Message message = Message.obtain(mActivity.getCaptureActivityHandler(), R.id.decode_failed);
            message.sendToTarget();
        }
    }


//    public native String decode(byte[] data, int width, int height, boolean isCrop, int x, int y, int cwidth, int cheight);
//    data:待解码数据 width:预览数据宽 height:预览数据高
//    isCrop:是否裁剪 x:裁剪区起始点x y:裁剪区起始点y cwidth:裁剪区宽 cheight:裁剪区高
//
//    使用zbar库需注意,调用native方法的类包名必须是com.zbar.lib,不然使用时会报zbar库链接失败;
//    另这里我注释了一段代码,这是我之前调试其他公司的扫码时发现将预览原数据直接扔到解码库,解码会失败,后面我猜测是因为他们在兼容前置时Camera模块对数据做了修改,所以才将数据进行90度旋转后再扔到解码库,就成功解码了!

 成功解码后不要忘记释放Camera资源

    /**
     * Closes the camera driver if still in use.
     */
    public void closeDriver() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

闪光灯开关

    /**
     * 打开或关闭闪光灯
     *
     * @param open 控制是否打开
     * @return 打开或关闭失败,则返回false。
     */
    public boolean setFlashLight(boolean open) {
        SDKLog.d(TAG,"-->setFlashLight("+open+")");
        if (mCamera == null) {
            return false;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return false;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        // Check if camera flash exists
        if (null == flashModes || 0 == flashModes.size()) {
            // Use the screen as a flashlight (next best thing)
            return false;
        }
        String flashMode = parameters.getFlashMode();
        SDKLog.d(TAG,"闪光灯模式 getFlashMode() = " + flashMode);
        if (open) {
            SDKLog.d(TAG,"进行 open 操作");
            if (Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
                SDKLog.d(TAG,"flashMode 已经处于 TORCH(手电筒) 模式");
                return true;
            }
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
                SDKLog.d(TAG,"设置为 TORCH(手电筒) 模式");
                return true;
            } else {
                SDKLog.d(TAG,"flashModes不包含FLASH_MODE_TORCH");
                return false;
            }
        } else {
            SDKLog.d(TAG,"进行 close 操作");
            if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
                SDKLog.d(TAG,"flashMode 已经处于 OFF(关闭) 模式");
                return true;
            }
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                mCamera.setParameters(parameters);
                SDKLog.d(TAG,"设置为 OFF(关闭) 模式");
                return true;
            } else
                SDKLog.d(TAG,"flashModes不包含FLASH_MODE_OFF");
            return false;
        }
    }

 以上为扩展功能

///

/

复制官方activity方法

 以下为官方代码

    <activity android:name=".CaptureActivity"
              android:screenOrientation="sensorLandscape"
              android:clearTaskOnLaunch="true"
              android:stateNotNeeded="true"
              android:theme="@style/CaptureTheme"
              android:windowSoftInputMode="stateAlwaysHidden"
              android:exported="false">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
      <intent-filter>
        <action android:name="com.google.zxing.client.android.SCAN"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
      <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. -->
      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan"/>
      </intent-filter>
      <!-- We also support a Google Product Search URL. -->
      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan"/>
      </intent-filter>
      <!-- And the UK version. -->
      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan"/>
      </intent-filter>
      <!-- Support zxing://scan/?... like iPhone app -->
      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="zxing" android:host="scan" android:path="/"/>
      </intent-filter>
    </activity>
    <activity android:name=".PreferencesActivity"
              android:label="@string/preferences_name"
              android:stateNotNeeded="true"/>
    <activity android:name=".encode.EncodeActivity"
              android:stateNotNeeded="true"
              android:exported="false">
      <intent-filter>
        <action android:name="com.google.zxing.client.android.ENCODE"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
      <!-- This allows us to handle the Share button in Contacts. -->
      <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/x-vcard"/>
      </intent-filter>
      <!-- This allows us to handle sharing any plain text . -->
      <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
      </intent-filter>
    </activity>
    <activity android:name=".book.SearchBookContentsActivity"
              android:label="@string/sbc_name"
              android:stateNotNeeded="true"
              android:screenOrientation="sensorLandscape"
              android:exported="false">
      <intent-filter>
        <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
    </activity>
    <activity android:name=".share.ShareActivity"
              android:stateNotNeeded="true"
              android:screenOrientation="user"
              android:exported="false">
      <intent-filter>
        <action android:name="com.google.zxing.client.android.SHARE"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
    </activity>
    <activity android:name=".history.HistoryActivity"
              android:label="@string/history_title"
              android:stateNotNeeded="true"/>
    <activity android:name=".share.BookmarkPickerActivity"
              android:label="@string/bookmark_picker_name"
              android:stateNotNeeded="true"/>
    <activity android:name=".share.AppPickerActivity"
              android:label="@string/app_picker_name"
              android:stateNotNeeded="true"/>
    <activity android:name=".HelpActivity"
              android:label="@string/menu_help"
              android:screenOrientation="user"
              android:stateNotNeeded="true"/>

 

以下为修改后的代码

        
        <activity android:name=".CaptureActivity"
            android:screenOrientation="sensorLandscape"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/CaptureTheme"
            android:windowSoftInputMode="stateAlwaysHidden"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SCAN"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan"/>
            </intent-filter>
            <!-- We also support a Google Product Search URL. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan"/>
            </intent-filter>
            <!-- And the UK version. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan"/>
            </intent-filter>
            <!-- Support zxing://scan/?... like iPhone app -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="zxing" android:host="scan" android:path="/"/>
            </intent-filter>
        </activity>
        <activity android:name=".PreferencesActivity"
            android:label="@string/preferences_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".encode.EncodeActivity"
            android:stateNotNeeded="true"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.ENCODE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <!-- This allows us to handle the Share button in Contacts. -->
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/x-vcard"/>
            </intent-filter>
            <!-- This allows us to handle sharing any plain text . -->
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/plain"/>
            </intent-filter>
        </activity>
        <activity android:name=".book.SearchBookContentsActivity"
            android:label="@string/sbc_name"
            android:stateNotNeeded="true"
            android:screenOrientation="sensorLandscape"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".share.ShareActivity"
            android:stateNotNeeded="true"
            android:screenOrientation="user"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SHARE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".history.HistoryActivity"
            android:label="@string/history_title"
            android:stateNotNeeded="true"/>
        <activity android:name=".share.BookmarkPickerActivity"
            android:label="@string/bookmark_picker_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".share.AppPickerActivity"
            android:label="@string/app_picker_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".HelpActivity"
            android:label="@string/menu_help"
            android:screenOrientation="user"
            android:stateNotNeeded="true"/>
        

以下为AndroidManifest.xml完整全部代码 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.FLASHLIGHT"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <!-- unavailable in API 23 -->
    <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

<!--    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="22"/>-->

    <uses-feature android:name="android.hardware.camera.any"/>
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
    <uses-feature android:name="android.hardware.screen.landscape"/>
    <uses-feature android:name="android.hardware.wifi" android:required="false"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
<!--            <intent-filter>-->
<!--                <action android:name="android.intent.action.MAIN" />-->

<!--                <category android:name="android.intent.category.LAUNCHER" />-->
<!--            </intent-filter>-->
        </activity>
        <activity android:name=".CaptureActivity"
            android:screenOrientation="sensorLandscape"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:windowSoftInputMode="stateAlwaysHidden"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SCAN"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan"/>
            </intent-filter>
            <!-- We also support a Google Product Search URL. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan"/>
            </intent-filter>
            <!-- And the UK version. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan"/>
            </intent-filter>
            <!-- Support zxing://scan/?... like iPhone app -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="zxing" android:host="scan" android:path="/"/>
            </intent-filter>
        </activity>
        <activity android:name=".PreferencesActivity"
            android:label="@string/preferences_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".encode.EncodeActivity"
            android:stateNotNeeded="true"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.ENCODE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <!-- This allows us to handle the Share button in Contacts. -->
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/x-vcard"/>
            </intent-filter>
            <!-- This allows us to handle sharing any plain text . -->
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/plain"/>
            </intent-filter>
        </activity>
        <activity android:name=".book.SearchBookContentsActivity"
            android:label="@string/sbc_name"
            android:stateNotNeeded="true"
            android:screenOrientation="sensorLandscape"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".share.ShareActivity"
            android:stateNotNeeded="true"
            android:screenOrientation="user"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SHARE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".history.HistoryActivity"
            android:label="@string/history_title"
            android:stateNotNeeded="true"/>
        <activity android:name=".share.BookmarkPickerActivity"
            android:label="@string/bookmark_picker_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".share.AppPickerActivity"
            android:label="@string/app_picker_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".HelpActivity"
            android:label="@string/menu_help"
            android:screenOrientation="user"
            android:stateNotNeeded="true"/>
    </application>

</manifest>

复制权限申请

  <uses-permission android:name="android.permission.CAMERA"/>
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.VIBRATE"/>
  <uses-permission android:name="android.permission.FLASHLIGHT"/>
  <uses-permission android:name="android.permission.READ_CONTACTS"/>
  <!-- unavailable in API 23 -->
  <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

  <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="22"/>

  <uses-feature android:name="android.hardware.camera.any"/>
  <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
  <uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
  <uses-feature android:name="android.hardware.screen.landscape"/>
  <uses-feature android:name="android.hardware.wifi" android:required="false"/>

复制到这里

  

删除这行代码

注释这段代码

记得这里要改true 否则导出无效 

注释这行

修改后完整的代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.FLASHLIGHT"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <!-- unavailable in API 23 -->
    <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!--    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="22"/>-->

    <uses-feature android:name="android.hardware.camera.any"/>
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
    <uses-feature android:name="android.hardware.screen.landscape"/>
    <uses-feature android:name="android.hardware.wifi" android:required="false"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <!--            <intent-filter>-->
            <!--                <action android:name="android.intent.action.MAIN" />-->

            <!--                <category android:name="android.intent.category.LAUNCHER" />-->
            <!--            </intent-filter>-->
        </activity>
        <activity android:name=".CaptureActivity"
            android:screenOrientation="sensorLandscape"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:windowSoftInputMode="stateAlwaysHidden"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SCAN"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan"/>
            </intent-filter>
            <!-- We also support a Google Product Search URL. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan"/>
            </intent-filter>
            <!-- And the UK version. -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan"/>
            </intent-filter>
            <!-- Support zxing://scan/?... like iPhone app -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="zxing" android:host="scan" android:path="/"/>
            </intent-filter>
        </activity>
        <activity android:name=".PreferencesActivity"
            android:label="@string/preferences_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".encode.EncodeActivity"
            android:stateNotNeeded="true"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.ENCODE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
            <!-- This allows us to handle the Share button in Contacts. -->
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/x-vcard"/>
            </intent-filter>
            <!-- This allows us to handle sharing any plain text . -->
            <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="text/plain"/>
            </intent-filter>
        </activity>
        <activity android:name=".book.SearchBookContentsActivity"
            android:label="@string/sbc_name"
            android:stateNotNeeded="true"
            android:screenOrientation="sensorLandscape"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".share.ShareActivity"
            android:stateNotNeeded="true"
            android:screenOrientation="user"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.zxing.client.android.SHARE"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".history.HistoryActivity"
            android:label="@string/history_title"
            android:stateNotNeeded="true"/>
        <activity android:name=".share.BookmarkPickerActivity"
            android:label="@string/bookmark_picker_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".share.AppPickerActivity"
            android:label="@string/app_picker_name"
            android:stateNotNeeded="true"/>
        <activity android:name=".HelpActivity"
            android:label="@string/menu_help"
            android:screenOrientation="user"
            android:stateNotNeeded="true"/>
    </application>

</manifest>

出现以下报错莫要慌

复制官方assets文件夹到空项目中去

相机报错解决  

得引入相机配制 参考第8步

 

 此时rebuild项目

 解决相机报错

待解决switch case 改 if else

对空项目res文件夹压缩

包名 res-backup.zip

复制官方res文件夹里所有文件替换空项目的res文件夹里所有文件  

选择全部替找

记得两个都用官方的替换

修改switch语句

方法一把switch-case语句转为if-else语句来解决。

方法二可以在gradle.properties中配置nonFinalResIds=false。

android.nonFinalResIds=false

# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

android.nonFinalResIds=false

setContentView(R.layout.activity_main)  

鼠标放R上手动导入

 选择第一个

setContentView(com.google.zxing.client.android.R.layout.activity_main);

包名导入最好不要手动,问题一堆。

 新建时用 com.google.zxing.client.android就不会有

报错解决

相机报错解决用官方的替换

相机闪退

 

    //6.0版本或以上需请求权限
    String[] permissions=new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
      requestPermissions(permissions,PERMS_REQUEST_CODE);
    }

 全部代码

/*
 * 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.
 */

package com.google.zxing.client.android;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.camera.CameraManager;
import com.google.zxing.client.android.clipboard.ClipboardInterface;
import com.google.zxing.client.android.history.HistoryActivity;
import com.google.zxing.client.android.history.HistoryItem;
import com.google.zxing.client.android.history.HistoryManager;
import com.google.zxing.client.android.result.ResultButtonListener;
import com.google.zxing.client.android.result.ResultHandler;
import com.google.zxing.client.android.result.ResultHandlerFactory;
import com.google.zxing.client.android.result.supplement.SupplementalInfoRetriever;
import com.google.zxing.client.android.share.ShareActivity;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.text.DateFormat;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;

/**
 * This activity opens the camera and does the actual scanning on a background thread. It draws a
 * viewfinder to help the user place the barcode correctly, shows feedback as the image processing
 * is happening, and then overlays the results when a scan is successful.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 * @author Sean Owen
 */
public final class CaptureActivity extends Activity implements SurfaceHolder.Callback {

  private static final String TAG = CaptureActivity.class.getSimpleName();

  private static final long DEFAULT_INTENT_RESULT_DURATION_MS = 1500L;
  private static final long BULK_MODE_SCAN_DELAY_MS = 1000L;

  private static final String[] ZXING_URLS = { "http://zxing.appspot.com/scan", "zxing://scan/" };

  private static final int HISTORY_REQUEST_CODE = 0x0000bacc;

  private static final Collection<ResultMetadataType> DISPLAYABLE_METADATA_TYPES =
          EnumSet.of(ResultMetadataType.ISSUE_NUMBER,
                  ResultMetadataType.SUGGESTED_PRICE,
                  ResultMetadataType.ERROR_CORRECTION_LEVEL,
                  ResultMetadataType.POSSIBLE_COUNTRY);

  private CameraManager cameraManager;
  private CaptureActivityHandler handler;
  private Result savedResultToShow;
  private ViewfinderView viewfinderView;
  private TextView statusView;
  private View resultView;
  private Result lastResult;
  private boolean hasSurface;
  private boolean copyToClipboard;
  private IntentSource source;
  private String sourceUrl;
  private ScanFromWebPageManager scanFromWebPageManager;
  private Collection<BarcodeFormat> decodeFormats;
  private Map<DecodeHintType,?> decodeHints;
  private String characterSet;
  private HistoryManager historyManager;
  private InactivityTimer inactivityTimer;
  private BeepManager beepManager;
  private AmbientLightManager ambientLightManager;
  protected final int PERMS_REQUEST_CODE = 202;


  ViewfinderView getViewfinderView() {
    return viewfinderView;
  }

  public Handler getHandler() {
    return handler;
  }

  CameraManager getCameraManager() {
    return cameraManager;
  }

  @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);

    Window window = getWindow();
    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    setContentView(R.layout.capture);

    //6.0版本或以上需请求权限
    String[] permissions=new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
      requestPermissions(permissions,PERMS_REQUEST_CODE);
    }

    hasSurface = false;
    inactivityTimer = new InactivityTimer(this);
    beepManager = new BeepManager(this);
    ambientLightManager = new AmbientLightManager(this);

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
  }

  @Override
  protected void onResume() {
    super.onResume();

    // historyManager must be initialized here to update the history preference
    historyManager = new HistoryManager(this);
    historyManager.trimHistory();

    // CameraManager must be initialized here, not in onCreate(). This is necessary because we don't
    // want to open the camera driver and measure the screen size if we're going to show the help on
    // first launch. That led to bugs where the scanning rectangle was the wrong size and partially
    // off screen.
    cameraManager = new CameraManager(getApplication());

    viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
    viewfinderView.setCameraManager(cameraManager);

    resultView = findViewById(R.id.result_view);
    statusView = (TextView) findViewById(R.id.status_view);

    handler = null;
    lastResult = null;

    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

    if (prefs.getBoolean(PreferencesActivity.KEY_DISABLE_AUTO_ORIENTATION, true)) {
      setRequestedOrientation(getCurrentOrientation());
    } else {
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
    }

    resetStatusView();


    beepManager.updatePrefs();
    ambientLightManager.start(cameraManager);

    inactivityTimer.onResume();

    Intent intent = getIntent();

    copyToClipboard = prefs.getBoolean(PreferencesActivity.KEY_COPY_TO_CLIPBOARD, true)
            && (intent == null || intent.getBooleanExtra(Intents.Scan.SAVE_HISTORY, true));

    source = IntentSource.NONE;
    sourceUrl = null;
    scanFromWebPageManager = null;
    decodeFormats = null;
    characterSet = null;

    if (intent != null) {

      String action = intent.getAction();
      String dataString = intent.getDataString();

      if (Intents.Scan.ACTION.equals(action)) {

        // Scan the formats the intent requested, and return the result to the calling activity.
        source = IntentSource.NATIVE_APP_INTENT;
        decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
        decodeHints = DecodeHintManager.parseDecodeHints(intent);

        if (intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
          int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
          int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
          if (width > 0 && height > 0) {
            cameraManager.setManualFramingRect(width, height);
          }
        }

        if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {
          int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);
          if (cameraId >= 0) {
            cameraManager.setManualCameraId(cameraId);
          }
        }

        String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
        if (customPromptMessage != null) {
          statusView.setText(customPromptMessage);
        }

      } else if (dataString != null &&
              dataString.contains("http://www.google") &&
              dataString.contains("/m/products/scan")) {

        // Scan only products and send the result to mobile Product Search.
        source = IntentSource.PRODUCT_SEARCH_LINK;
        sourceUrl = dataString;
        decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;

      } else if (isZXingURL(dataString)) {

        // Scan formats requested in query string (all formats if none specified).
        // If a return URL is specified, send the results there. Otherwise, handle it ourselves.
        source = IntentSource.ZXING_LINK;
        sourceUrl = dataString;
        Uri inputUri = Uri.parse(dataString);
        scanFromWebPageManager = new ScanFromWebPageManager(inputUri);
        decodeFormats = DecodeFormatManager.parseDecodeFormats(inputUri);
        // Allow a sub-set of the hints to be specified by the caller.
        decodeHints = DecodeHintManager.parseDecodeHints(inputUri);

      }

      characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);

    }

    SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
    SurfaceHolder surfaceHolder = surfaceView.getHolder();
    if (hasSurface) {
      // The activity was paused but not stopped, so the surface still exists. Therefore
      // surfaceCreated() won't be called, so init the camera here.
      initCamera(surfaceHolder);
    } else {
      // Install the callback and wait for surfaceCreated() to init the camera.
      surfaceHolder.addCallback(this);
    }
  }

  private int getCurrentOrientation() {
    int rotation = getWindowManager().getDefaultDisplay().getRotation();
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
      switch (rotation) {
        case Surface.ROTATION_0:
        case Surface.ROTATION_90:
          return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        default:
          return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
      }
    } else {
      switch (rotation) {
        case Surface.ROTATION_0:
        case Surface.ROTATION_270:
          return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        default:
          return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
      }
    }
  }

  private static boolean isZXingURL(String dataString) {
    if (dataString == null) {
      return false;
    }
    for (String url : ZXING_URLS) {
      if (dataString.startsWith(url)) {
        return true;
      }
    }
    return false;
  }

  @Override
  protected void onPause() {
    if (handler != null) {
      handler.quitSynchronously();
      handler = null;
    }
    inactivityTimer.onPause();
    ambientLightManager.stop();
    beepManager.close();
    cameraManager.closeDriver();
    //historyManager = null; // Keep for onActivityResult
    if (!hasSurface) {
      SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
      SurfaceHolder surfaceHolder = surfaceView.getHolder();
      surfaceHolder.removeCallback(this);
    }
    super.onPause();
  }

  @Override
  protected void onDestroy() {
    inactivityTimer.shutdown();
    super.onDestroy();
  }

  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    switch (keyCode) {
      case KeyEvent.KEYCODE_BACK:
        if (source == IntentSource.NATIVE_APP_INTENT) {
          setResult(RESULT_CANCELED);
          finish();
          return true;
        }
        if ((source == IntentSource.NONE || source == IntentSource.ZXING_LINK) && lastResult != null) {
          restartPreviewAfterDelay(0L);
          return true;
        }
        break;
      case KeyEvent.KEYCODE_FOCUS:
      case KeyEvent.KEYCODE_CAMERA:
        // Handle these events so they don't launch the Camera app
        return true;
      // Use volume up/down to turn on light
      case KeyEvent.KEYCODE_VOLUME_DOWN:
        cameraManager.setTorch(false);
        return true;
      case KeyEvent.KEYCODE_VOLUME_UP:
        cameraManager.setTorch(true);
        return true;
    }
    return super.onKeyDown(keyCode, event);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.capture, menu);
    return super.onCreateOptionsMenu(menu);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.addFlags(Intents.FLAG_NEW_DOC);
    switch (item.getItemId()) {
      case R.id.menu_share:
        intent.setClassName(this, ShareActivity.class.getName());
        startActivity(intent);
        break;
      case R.id.menu_history:
        intent.setClassName(this, HistoryActivity.class.getName());
        startActivityForResult(intent, HISTORY_REQUEST_CODE);
        break;
      case R.id.menu_settings:
        intent.setClassName(this, PreferencesActivity.class.getName());
        startActivity(intent);
        break;
      case R.id.menu_help:
        intent.setClassName(this, HelpActivity.class.getName());
        startActivity(intent);
        break;
      default:
        return super.onOptionsItemSelected(item);
    }
    return true;
  }

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (resultCode == RESULT_OK && requestCode == HISTORY_REQUEST_CODE && historyManager != null) {
      int itemNumber = intent.getIntExtra(Intents.History.ITEM_NUMBER, -1);
      if (itemNumber >= 0) {
        HistoryItem historyItem = historyManager.buildHistoryItem(itemNumber);
        decodeOrStoreSavedBitmap(null, historyItem.getResult());
      }
    }
  }

  private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
    // Bitmap isn't used yet -- will be used soon
    if (handler == null) {
      savedResultToShow = result;
    } else {
      if (result != null) {
        savedResultToShow = result;
      }
      if (savedResultToShow != null) {
        Message message = Message.obtain(handler, R.id.decode_succeeded, savedResultToShow);
        handler.sendMessage(message);
      }
      savedResultToShow = null;
    }
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    if (holder == null) {
      Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
    }
    if (!hasSurface) {
      hasSurface = true;
      initCamera(holder);
    }
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
    hasSurface = false;
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // do nothing
  }

  /**
   * A valid barcode has been found, so give an indication of success and show the results.
   *
   * @param rawResult The contents of the barcode.
   * @param scaleFactor amount by which thumbnail was scaled
   * @param barcode   A greyscale bitmap of the camera data which was decoded.
   */
  public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
    inactivityTimer.onActivity();
    lastResult = rawResult;
    ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);

    boolean fromLiveScan = barcode != null;
    if (fromLiveScan) {
      historyManager.addHistoryItem(rawResult, resultHandler);
      // Then not from history, so beep/vibrate and we have an image to draw on
      beepManager.playBeepSoundAndVibrate();
      drawResultPoints(barcode, scaleFactor, rawResult);
    }

    switch (source) {
      case NATIVE_APP_INTENT:
      case PRODUCT_SEARCH_LINK:
        handleDecodeExternally(rawResult, resultHandler, barcode);
        break;
      case ZXING_LINK:
        if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) {
          handleDecodeInternally(rawResult, resultHandler, barcode);
        } else {
          handleDecodeExternally(rawResult, resultHandler, barcode);
        }
        break;
      case NONE:
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) {
          Toast.makeText(getApplicationContext(),
                  getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')',
                  Toast.LENGTH_SHORT).show();
          maybeSetClipboard(resultHandler);
          // Wait a moment or else it will scan the same barcode continuously about 3 times
          restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
        } else {
          handleDecodeInternally(rawResult, resultHandler, barcode);
        }
        break;
    }
  }

  /**
   * Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
   *
   * @param barcode   A bitmap of the captured image.
   * @param scaleFactor amount by which thumbnail was scaled
   * @param rawResult The decoded results which contains the points to draw.
   */
  private void drawResultPoints(Bitmap barcode, float scaleFactor, Result rawResult) {
    ResultPoint[] points = rawResult.getResultPoints();
    if (points != null && points.length > 0) {
      Canvas canvas = new Canvas(barcode);
      Paint paint = new Paint();
      paint.setColor(getResources().getColor(R.color.result_points));
      if (points.length == 2) {
        paint.setStrokeWidth(4.0f);
        drawLine(canvas, paint, points[0], points[1], scaleFactor);
      } else if (points.length == 4 &&
              (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
                      rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
        // Hacky special case -- draw two lines, for the barcode and metadata
        drawLine(canvas, paint, points[0], points[1], scaleFactor);
        drawLine(canvas, paint, points[2], points[3], scaleFactor);
      } else {
        paint.setStrokeWidth(10.0f);
        for (ResultPoint point : points) {
          if (point != null) {
            canvas.drawPoint(scaleFactor * point.getX(), scaleFactor * point.getY(), paint);
          }
        }
      }
    }
  }

  private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b, float scaleFactor) {
    if (a != null && b != null) {
      canvas.drawLine(scaleFactor * a.getX(),
              scaleFactor * a.getY(),
              scaleFactor * b.getX(),
              scaleFactor * b.getY(),
              paint);
    }
  }

  // Put up our own UI for how to handle the decoded contents.
  private void handleDecodeInternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {

    maybeSetClipboard(resultHandler);

    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

    if (resultHandler.getDefaultButtonID() != null && prefs.getBoolean(PreferencesActivity.KEY_AUTO_OPEN_WEB, false)) {
      resultHandler.handleButtonPress(resultHandler.getDefaultButtonID());
      return;
    }

    statusView.setVisibility(View.GONE);
    viewfinderView.setVisibility(View.GONE);
    resultView.setVisibility(View.VISIBLE);

    ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
    if (barcode == null) {
      barcodeImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(),
              R.drawable.launcher_icon));
    } else {
      barcodeImageView.setImageBitmap(barcode);
    }

    TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
    formatTextView.setText(rawResult.getBarcodeFormat().toString());

    TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
    typeTextView.setText(resultHandler.getType().toString());

    DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
    TextView timeTextView = (TextView) findViewById(R.id.time_text_view);
    timeTextView.setText(formatter.format(rawResult.getTimestamp()));


    TextView metaTextView = (TextView) findViewById(R.id.meta_text_view);
    View metaTextViewLabel = findViewById(R.id.meta_text_view_label);
    metaTextView.setVisibility(View.GONE);
    metaTextViewLabel.setVisibility(View.GONE);
    Map<ResultMetadataType,Object> metadata = rawResult.getResultMetadata();
    if (metadata != null) {
      StringBuilder metadataText = new StringBuilder(20);
      for (Map.Entry<ResultMetadataType,Object> entry : metadata.entrySet()) {
        if (DISPLAYABLE_METADATA_TYPES.contains(entry.getKey())) {
          metadataText.append(entry.getValue()).append('\n');
        }
      }
      if (metadataText.length() > 0) {
        metadataText.setLength(metadataText.length() - 1);
        metaTextView.setText(metadataText);
        metaTextView.setVisibility(View.VISIBLE);
        metaTextViewLabel.setVisibility(View.VISIBLE);
      }
    }

    CharSequence displayContents = resultHandler.getDisplayContents();
    TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
    contentsTextView.setText(displayContents);
    int scaledSize = Math.max(22, 32 - displayContents.length() / 4);
    contentsTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, scaledSize);

    TextView supplementTextView = (TextView) findViewById(R.id.contents_supplement_text_view);
    supplementTextView.setText("");
    supplementTextView.setOnClickListener(null);
    if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
            PreferencesActivity.KEY_SUPPLEMENTAL, true)) {
      SupplementalInfoRetriever.maybeInvokeRetrieval(supplementTextView,
              resultHandler.getResult(),
              historyManager,
              this);
    }

    int buttonCount = resultHandler.getButtonCount();
    ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
    buttonView.requestFocus();
    for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
      TextView button = (TextView) buttonView.getChildAt(x);
      if (x < buttonCount) {
        button.setVisibility(View.VISIBLE);
        button.setText(resultHandler.getButtonText(x));
        button.setOnClickListener(new ResultButtonListener(resultHandler, x));
      } else {
        button.setVisibility(View.GONE);
      }
    }

  }

  // Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
  private void handleDecodeExternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {

    if (barcode != null) {
      viewfinderView.drawResultBitmap(barcode);
    }

    long resultDurationMS;
    if (getIntent() == null) {
      resultDurationMS = DEFAULT_INTENT_RESULT_DURATION_MS;
    } else {
      resultDurationMS = getIntent().getLongExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS,
              DEFAULT_INTENT_RESULT_DURATION_MS);
    }

    if (resultDurationMS > 0) {
      String rawResultString = String.valueOf(rawResult);
      if (rawResultString.length() > 32) {
        rawResultString = rawResultString.substring(0, 32) + " ...";
      }
      statusView.setText(getString(resultHandler.getDisplayTitle()) + " : " + rawResultString);
    }

    maybeSetClipboard(resultHandler);

    switch (source) {
      case NATIVE_APP_INTENT:
        // Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
        // the deprecated intent is retired.
        Intent intent = new Intent(getIntent().getAction());
        intent.addFlags(Intents.FLAG_NEW_DOC);
        intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
        intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
        byte[] rawBytes = rawResult.getRawBytes();
        if (rawBytes != null && rawBytes.length > 0) {
          intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);
        }
        Map<ResultMetadataType, ?> metadata = rawResult.getResultMetadata();
        if (metadata != null) {
          if (metadata.containsKey(ResultMetadataType.UPC_EAN_EXTENSION)) {
            intent.putExtra(Intents.Scan.RESULT_UPC_EAN_EXTENSION,
                    metadata.get(ResultMetadataType.UPC_EAN_EXTENSION).toString());
          }
          Number orientation = (Number) metadata.get(ResultMetadataType.ORIENTATION);
          if (orientation != null) {
            intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());
          }
          String ecLevel = (String) metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
          if (ecLevel != null) {
            intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);
          }
          @SuppressWarnings("unchecked")
          Iterable<byte[]> byteSegments = (Iterable<byte[]>) metadata.get(ResultMetadataType.BYTE_SEGMENTS);
          if (byteSegments != null) {
            int i = 0;
            for (byte[] byteSegment : byteSegments) {
              intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i, byteSegment);
              i++;
            }
          }
        }
        sendReplyMessage(R.id.return_scan_result, intent, resultDurationMS);
        break;

      case PRODUCT_SEARCH_LINK:
        // Reformulate the URL which triggered us into a query, so that the request goes to the same
        // TLD as the scan URL.
        int end = sourceUrl.lastIndexOf("/scan");
        String productReplyURL = sourceUrl.substring(0, end) + "?q=" +
                resultHandler.getDisplayContents() + "&source=zxing";
        sendReplyMessage(R.id.launch_product_query, productReplyURL, resultDurationMS);
        break;

      case ZXING_LINK:
        if (scanFromWebPageManager != null && scanFromWebPageManager.isScanFromWebPage()) {
          String linkReplyURL = scanFromWebPageManager.buildReplyURL(rawResult, resultHandler);
          scanFromWebPageManager = null;
          sendReplyMessage(R.id.launch_product_query, linkReplyURL, resultDurationMS);
        }
        break;
    }
  }

  private void maybeSetClipboard(ResultHandler resultHandler) {
    if (copyToClipboard && !resultHandler.areContentsSecure()) {
      ClipboardInterface.setText(resultHandler.getDisplayContents(), this);
    }
  }

  private void sendReplyMessage(int id, Object arg, long delayMS) {
    if (handler != null) {
      Message message = Message.obtain(handler, id, arg);
      if (delayMS > 0L) {
        handler.sendMessageDelayed(message, delayMS);
      } else {
        handler.sendMessage(message);
      }
    }
  }

  private void initCamera(SurfaceHolder surfaceHolder) {
    if (surfaceHolder == null) {
      throw new IllegalStateException("No SurfaceHolder provided");
    }
    if (cameraManager.isOpen()) {
      Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
      return;
    }
    try {
      cameraManager.openDriver(surfaceHolder);
      // Creating the handler starts the preview, which can also throw a RuntimeException.
      if (handler == null) {
        handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
      }
      decodeOrStoreSavedBitmap(null, null);
    } catch (IOException ioe) {
      Log.w(TAG, ioe);
      displayFrameworkBugMessageAndExit();
    } catch (RuntimeException e) {
      // Barcode Scanner has seen crashes in the wild of this variety:
      // java.?lang.?RuntimeException: Fail to connect to camera service
      Log.w(TAG, "Unexpected error initializing camera", e);
      displayFrameworkBugMessageAndExit();
    }
  }

  private void displayFrameworkBugMessageAndExit() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(getString(R.string.app_name));
    builder.setMessage(getString(R.string.msg_camera_framework_bug));
    builder.setPositiveButton(R.string.button_ok, new FinishListener(this));
    builder.setOnCancelListener(new FinishListener(this));
    builder.show();
  }

  public void restartPreviewAfterDelay(long delayMS) {
    if (handler != null) {
      handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
    }
    resetStatusView();
  }

  private void resetStatusView() {
    resultView.setVisibility(View.GONE);
    statusView.setText(R.string.msg_default_status);
    statusView.setVisibility(View.VISIBLE);
    viewfinderView.setVisibility(View.VISIBLE);
    lastResult = null;
  }

  public void drawViewfinder() {
    viewfinderView.drawViewfinder();
  }
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值