更新CameraX至v1.2.1

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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