1、新增CaptureFragment

2、将扫码相关逻辑与界面分离,ZXingLite使用更容易扩展。
This commit is contained in:
jenly1314
2019-04-19 18:45:13 +08:00
parent 4c7757fcb0
commit 36ccf48b7f
33 changed files with 1483 additions and 1186 deletions

Binary file not shown.

View File

@@ -45,17 +45,17 @@ ZXingLite for Android 是ZXing的精简版基于ZXing库优化扫码和生成
<dependency> <dependency>
<groupId>com.king.zxing</groupId> <groupId>com.king.zxing</groupId>
<artifactId>zxing-lite</artifactId> <artifactId>zxing-lite</artifactId>
<version>1.0.7</version> <version>1.1.0</version>
<type>pom</type> <type>pom</type>
</dependency> </dependency>
``` ```
### Gradle: ### Gradle:
```gradle ```gradle
implementation 'com.king.zxing:zxing-lite:1.0.7' implementation 'com.king.zxing:zxing-lite:1.1.0'
``` ```
### Lvy: ### Lvy:
```lvy ```lvy
<dependency org='com.king.zxing' name='zxing-lite' rev='1.0.7'> <dependency org='com.king.zxing' name='zxing-lite' rev='1.1.0'>
<artifact name='$AID' ext='pom'></artifact> <artifact name='$AID' ext='pom'></artifact>
</dependency> </dependency>
``` ```
@@ -79,16 +79,20 @@ api 'com.google.zxing:core:3.3.3'
布局示例 可自定义布局布局内至少要保证有SurfaceView和ViewfinderView控件id可根据重写CaptureActivity 的 getPreviewViewId 和 getViewFinderViewId方法自定义 布局示例 可自定义布局布局内至少要保证有SurfaceView和ViewfinderView控件id可根据重写CaptureActivity 的 getPreviewViewId 和 getViewFinderViewId方法自定义
```Xml ```Xml
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView <SurfaceView
android:id="@+id/preview_view" android:id="@+id/surfaceView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView <com.king.zxing.ViewfinderView
android:id="@+id/viewfinder_view" android:id="@+id/viewfinderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
</merge>
</FrameLayout>
``` ```
代码示例 (二维码/条形码) 代码示例 (二维码/条形码)
@@ -102,9 +106,25 @@ api 'com.google.zxing:core:3.3.3'
CodeUtils.createBarCode(content, BarcodeFormat.CODE_128,800,200); CodeUtils.createBarCode(content, BarcodeFormat.CODE_128,800,200);
``` ```
### 快速实现扫码有以下几种方式:
> 1、直接使用CaptureActivity或者CaptureFragment。(纯洁的扫码,无任何添加剂)
> 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。适用于大多场景并无需关心扫码相关逻辑
> 3、在你项目的Activity或者Fragment中创建创建一个CaptureHelper并在相应的生命周期中调用CaptureHelper的周期。适用于想在扫码界面写交互逻辑又因为项目架构或其它原因无法直接或间接继承CaptureActivity或CaptureFragment时使用
> 4、参照CaptureHelper写一个自定义的扫码帮助类其它步骤同方式3。扩展高级用法谨慎使用
更多使用详情,请查看[app](app)中的源码使用示例 更多使用详情,请查看[app](app)中的源码使用示例
## 版本记录 ## 版本记录
#### v1.1.02019-4-19
* 将扫码相关逻辑与界面分离ZXingLite使用更容易扩展。
* 新增CaptureFragment
#### v1.0.72019-4-9 #### v1.0.72019-4-9
* 新增网格样式的扫描激光(类似支付宝扫码样式) * 新增网格样式的扫描激光(类似支付宝扫码样式)
* 升级Gradle至v4.6 * 升级Gradle至v4.6

Binary file not shown.

View File

@@ -1 +1 @@
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":8,"versionName":"1.0.7","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":9,"versionName":"1.1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

View File

@@ -21,12 +21,25 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="com.king.zxing.CaptureActivity"
android:screenOrientation="portrait"/>
<activity <activity
android:name=".EasyCaptureActivity" android:name=".EasyCaptureActivity"
android:screenOrientation="portrait"/> android:screenOrientation="portrait"/>
<activity <activity
android:name=".CustomCaptureActivity" android:name=".CustomCaptureActivity"
android:screenOrientation="portrait"/> android:screenOrientation="portrait"/>
<activity
android:name=".CaptureFragmentActivity"
android:screenOrientation="portrait"/>
<activity
android:name=".CustomActivity"
android:screenOrientation="portrait"/>
<activity <activity
android:name=".CodeActivity" android:name=".CodeActivity"
android:screenOrientation="portrait"/> android:screenOrientation="portrait"/>

View File

@@ -0,0 +1,39 @@
package com.king.zxing.app;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.TextView;
import com.king.zxing.CaptureFragment;
import com.king.zxing.app.util.StatusBarUtils;
/**
* Fragment扫码
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
StatusBarUtils.immersiveStatusBar(this,toolbar,0.2f);
TextView tvTitle = findViewById(R.id.tvTitle);
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
replaceFragment(CaptureFragment.newInstance());
}
public void replaceFragment(Fragment fragment){
replaceFragment( R.id.fragmentContent,fragment);
}
public void replaceFragment(@IdRes int id, Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(id, fragment).commit();
}
}

View File

@@ -0,0 +1,143 @@
package com.king.zxing.app;
import android.hardware.Camera;
import android.os.Bundle;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.support.v4.app.Fragment;
import android.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.king.zxing.CaptureHelper;
import com.king.zxing.OnCaptureCallback;
import com.king.zxing.ViewfinderView;
import com.king.zxing.app.util.StatusBarUtils;
/**
* 自定义扫码当直接使用CaptureActivity
* 自定义扫码,切记自定义扫码需在{@link Activity}或者{@link Fragment}相对应的生命周期里面调用{@link #mCaptureHelper}对应的生命周期
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CustomActivity extends AppCompatActivity implements OnCaptureCallback {
private boolean isContinuousScan;
private CaptureHelper mCaptureHelper;
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.custom_activity);
initUI();
}
private void initUI(){
Toolbar toolbar = findViewById(R.id.toolbar);
StatusBarUtils.immersiveStatusBar(this,toolbar,0.2f);
TextView tvTitle = findViewById(R.id.tvTitle);
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
surfaceView = findViewById(R.id.surfaceView);
viewfinderView = findViewById(R.id.viewfinderView);
isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false);
mCaptureHelper = new CaptureHelper(this,surfaceView,viewfinderView);
mCaptureHelper.onCreate();
mCaptureHelper.vibrate(true)
.continuousScan(isContinuousScan);
}
@Override
protected void onResume() {
super.onResume();
mCaptureHelper.onResume();
}
@Override
protected void onPause() {
super.onPause();
mCaptureHelper.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mCaptureHelper.onDestroy();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mCaptureHelper.onTouchEvent(event);
return super.onTouchEvent(event);
}
/**
* 关闭闪光灯(手电筒)
*/
private void offFlash(){
Camera camera = mCaptureHelper.getCameraManager().getOpenCamera().getCamera();
Camera.Parameters parameters = camera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
}
/**
* 开启闪光灯(手电筒)
*/
public void openFlash(){
Camera camera = mCaptureHelper.getCameraManager().getOpenCamera().getCamera();
Camera.Parameters parameters = camera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
}
/**
* 扫码结果回调
* @param result 扫码结果
* @return
*/
@Override
public boolean onResultCallback(String result) {
if(isContinuousScan){
Toast.makeText(this,result,Toast.LENGTH_SHORT).show();
}
return false;
}
private void clickFlash(View v){
if(v.isSelected()){
offFlash();
v.setSelected(false);
}else{
openFlash();
v.setSelected(true);
}
}
public void OnClick(View v){
switch (v.getId()){
case R.id.ivLeft:
onBackPressed();
break;
case R.id.ivFlash:
clickFlash(v);
break;
}
}
}

View File

@@ -27,6 +27,7 @@ import com.king.zxing.CaptureActivity;
import com.king.zxing.app.util.StatusBarUtils; import com.king.zxing.app.util.StatusBarUtils;
/** /**
* 自定义继承CaptureActivity
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class CustomCaptureActivity extends CaptureActivity { public class CustomCaptureActivity extends CaptureActivity {
@@ -46,9 +47,10 @@ public class CustomCaptureActivity extends CaptureActivity {
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE)); tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false); isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false);
//获取CaptureHelper里面有扫码相关的配置设置
getBeepManager().setPlayBeep(true); getCaptureHelper().playBeep(true)//播放音效
getBeepManager().setVibrate(true); .vibrate(true)//震动
.continuousScan(isContinuousScan);//是否连扫
} }
/** /**
@@ -71,37 +73,21 @@ public class CustomCaptureActivity extends CaptureActivity {
camera.setParameters(parameters); camera.setParameters(parameters);
} }
/** /**
* 接收扫码结果,想支持连扫时,可将{@link #isContinuousScan()}返回为{@code true}并重写此方法 * 扫码结果回调
* 如果{@link #isContinuousScan()}支持连扫,则默认重启扫码和解码器;当连扫逻辑太复杂时,
* 请将{@link #isAutoRestartPreviewAndDecode()}返回为{@code false},并手动调用{@link #restartPreviewAndDecode()}
* @param result 扫码结果 * @param result 扫码结果
* @return
*/ */
@Override @Override
public void onResult(Result result) { public boolean onResultCallback(String result) {
super.onResult(result);
if(isContinuousScan){//连续扫码时,直接弹出结果 if(isContinuousScan){//连续扫码时,直接弹出结果
Toast.makeText(this,result.getText(),Toast.LENGTH_SHORT).show(); Toast.makeText(this,result,Toast.LENGTH_SHORT).show();
} }
return super.onResultCallback(result);
} }
/**
* 是否连续扫码,如果想支持连续扫码,则将此方法返回{@code true}并重写{@link #onResult(Result)}
* @return 默认返回 false
*/
@Override
public boolean isContinuousScan() {
return isContinuousScan;
}
/**
* 是否自动重启扫码和解码器,当支持连扫时才起作用。
* @return 默认返回 true
*/
@Override
public boolean isAutoRestartPreviewAndDecode() {
return super.isAutoRestartPreviewAndDecode();
}
private void clickFlash(View v){ private void clickFlash(View v){
if(v.isSelected()){ if(v.isSelected()){

View File

@@ -18,7 +18,6 @@ package com.king.zxing.app;
import android.Manifest; import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.BitmapFactory;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ActivityOptionsCompat;
@@ -30,8 +29,6 @@ import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.Toast; import android.widget.Toast;
import com.google.zxing.DecodeHintType;
import com.google.zxing.common.StringUtils;
import com.king.zxing.CaptureActivity; import com.king.zxing.CaptureActivity;
import com.king.zxing.Intents; import com.king.zxing.Intents;
import com.king.zxing.app.util.UriUtils; import com.king.zxing.app.util.UriUtils;
@@ -42,6 +39,20 @@ import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted; import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions; import pub.devrel.easypermissions.EasyPermissions;
/**
* 扫码Demo示例说明
*
* 快速实现扫码有以下几种方式:
*
* 1、直接使用CaptureActivity或者CaptureFragment。(纯洁的扫码,无任何添加剂)
*
* 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。适用于大多场景并无需关心扫码相关逻辑
*
* 3、在你项目的Activity或者Fragment中创建创建一个CaptureHelper并在相应的生命周期中调用CaptureHelper的周期。适用于想在扫码界面写交互逻辑又因为项目架构或其它原因无法直接或间接继承CaptureActivity或CaptureFragment时使用
*
* 4、参照CaptureHelper写一个自定义的扫码帮助类其它步骤同方式3。扩展高级用法谨慎使用
*
*/
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{ public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
public static final String KEY_TITLE = "key_title"; public static final String KEY_TITLE = "key_title";
@@ -195,18 +206,18 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
isContinuousScan = false; isContinuousScan = false;
switch (v.getId()){ switch (v.getId()){
case R.id.btn0: case R.id.btn0:
this.cls = CaptureActivity.class;
this.title = ((Button)v).getText().toString();
checkCameraPermissions();
break;
case R.id.btn1:
this.cls = CustomCaptureActivity.class; this.cls = CustomCaptureActivity.class;
this.title = ((Button)v).getText().toString(); this.title = ((Button)v).getText().toString();
isContinuousScan = true; isContinuousScan = true;
checkCameraPermissions(); checkCameraPermissions();
break; break;
case R.id.btn1:
this.cls = CaptureActivity.class;
this.title = ((Button)v).getText().toString();
checkCameraPermissions();
break;
case R.id.btn2: case R.id.btn2:
this.cls = EasyCaptureActivity.class; this.cls = CaptureFragmentActivity.class;
this.title = ((Button)v).getText().toString(); this.title = ((Button)v).getText().toString();
checkCameraPermissions(); checkCameraPermissions();
break; break;
@@ -216,12 +227,17 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
checkCameraPermissions(); checkCameraPermissions();
break; break;
case R.id.btn4: case R.id.btn4:
startCode(false); this.cls = CustomActivity.class;
this.title = ((Button)v).getText().toString();
checkCameraPermissions();
break; break;
case R.id.btn5: case R.id.btn5:
startCode(true); startCode(false);
break; break;
case R.id.btn6: case R.id.btn6:
startCode(true);
break;
case R.id.btn7:
checkExternalStoragePermissions(); checkExternalStoragePermissions();
break; break;
} }

View File

@@ -26,13 +26,6 @@
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:text="@string/app_name"/> android:text="@string/app_name"/>
</android.support.v7.widget.Toolbar> </android.support.v7.widget.Toolbar>
<android.support.constraint.Guideline
android:id="@+id/line"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:layout_constraintGuide_percent="0.2"/>
<Button <Button
android:id="@+id/btn0" android:id="@+id/btn0"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -41,10 +34,10 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="连续扫码" android:text="默认扫码"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line" app:layout_constraintTop_toBottomOf="@+id/toolbar"
style="@style/OnClick"/> style="@style/OnClick"/>
<Button <Button
android:id="@+id/btn1" android:id="@+id/btn1"
@@ -54,7 +47,7 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="默认扫码" android:text="连续扫码"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn0" app:layout_constraintTop_toBottomOf="@+id/btn0"
@@ -67,7 +60,8 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="简单自定义布局扫码" android:text="Fragment扫码"
android:textAllCaps="false"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn1" app:layout_constraintTop_toBottomOf="@+id/btn1"
@@ -93,7 +87,7 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="生成条形码" android:text="自定义扫码"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn3" app:layout_constraintTop_toBottomOf="@+id/btn3"
@@ -106,7 +100,7 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="生成二维码" android:text="生成条形码"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn4" app:layout_constraintTop_toBottomOf="@+id/btn4"
@@ -119,9 +113,22 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:text="识别一维码/二维码图片" android:text="生成二维码"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn5" app:layout_constraintTop_toBottomOf="@+id/btn5"
style="@style/OnClick"/> style="@style/OnClick"/>
<Button
android:id="@+id/btn7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:text="识别一维码/二维码图片"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn6"
style="@style/OnClick"/>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="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"
app:labelText="@string/tips_scan_code"
app:labelTextSize="@dimen/size_14sp"
app:laserColor="@color/colorAccent"
app:frameColor="@color/colorPrimary"
app:cornerColor="@color/colorPrimary"
app:resultPointColor="@color/colorAccent"
app:labelTextLocation="bottom"
app:laserStyle="grid"
app:gridHeight="0dp"/>
<ImageView
android:id="@+id/ivFlash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/flash_selected_selector"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="160dp"
style="@style/OnClick"/>
<include layout="@layout/toolbar_capture"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -6,11 +6,11 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<SurfaceView <SurfaceView
android:id="@+id/preview_view" android:id="@+id/surfaceView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView <com.king.zxing.ViewfinderView
android:id="@+id/viewfinder_view" android:id="@+id/viewfinderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:labelText="@string/tips_scan_code" app:labelText="@string/tips_scan_code"

View File

@@ -3,6 +3,6 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<include layout="@layout/capture"/> <include layout="@layout/zxl_capture"/>
<include layout="@layout/toolbar_capture"/> <include layout="@layout/toolbar_capture"/>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/fragmentContent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/toolbar_capture"/>
</FrameLayout>

View File

@@ -2,7 +2,7 @@
<string name="app_name">ZXingLite</string> <string name="app_name">ZXingLite</string>
<string name="permission_camera">App扫码需要用到拍摄权限</string> <string name="permission_camera">App扫码需要用到拍摄权限</string>
<string name="permission_external_storage">App需要用到读写权限</string> <string name="permission_external_storage">App需要用到读写权限</string>
<string name="tips_scan_code">将二维码/条形码放入框内,即可自动扫</string> <string name="tips_scan_code">将二维码/条形码放入框内,即可自动扫</string>
<string name="qr_code">二维码</string> <string name="qr_code">二维码</string>
<string name="bar_code">条形码</string> <string name="bar_code">条形码</string>
</resources> </resources>

View File

@@ -5,14 +5,10 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/> <uses-permission android:name="android.permission.FLASHLIGHT"/>
<!-- unavailable in API 23 -->
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application> <application>
<activity
android:name="com.king.zxing.CaptureActivity"
android:screenOrientation="portrait"/>
</application> </application>
</manifest> </manifest>

View File

@@ -32,7 +32,7 @@ import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
/** /**
* Manages beeps and vibrations for {@link CaptureActivity}. * Manages beeps and vibrations for {@link Activity}.
*/ */
public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable { public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
@@ -62,7 +62,7 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
synchronized void updatePrefs() { synchronized void updatePrefs() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
playBeep = shouldBeep(prefs, activity); shouldBeep(prefs, activity);
// vibrate = prefs.getBoolean(Preferences.KEY_VIBRATE, false); // vibrate = prefs.getBoolean(Preferences.KEY_VIBRATE, false);
if (playBeep && mediaPlayer == null) { if (playBeep && mediaPlayer == null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it too loud, // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
@@ -83,7 +83,7 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
} }
private static boolean shouldBeep(SharedPreferences prefs, Context activity) { private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
boolean shouldPlayBeep = prefs.getBoolean(Preferences.KEY_PLAY_BEEP, true); boolean shouldPlayBeep = prefs.getBoolean(Preferences.KEY_PLAY_BEEP, false);
if (shouldPlayBeep) { if (shouldPlayBeep) {
// See if sound settings overrides this // See if sound settings overrides this
AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
@@ -97,7 +97,7 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
@TargetApi(Build.VERSION_CODES.KITKAT) @TargetApi(Build.VERSION_CODES.KITKAT)
private MediaPlayer buildMediaPlayer(Context activity) { private MediaPlayer buildMediaPlayer(Context activity) {
MediaPlayer mediaPlayer = new MediaPlayer(); MediaPlayer mediaPlayer = new MediaPlayer();
try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep)) { try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.zxl_beep)) {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
mediaPlayer.setOnErrorListener(this); mediaPlayer.setOnErrorListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.king.zxing;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import com.king.zxing.camera.CameraManager;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureFragment extends Fragment implements OnCaptureCallback {
public static final String KEY_RESULT = Intents.Scan.RESULT;
private View mRootView;
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
private CaptureHelper mCaptureHelper;
public static CaptureFragment newInstance() {
Bundle args = new Bundle();
CaptureFragment fragment = new CaptureFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
int layoutId = getLayoutId();
if(isContentView(layoutId)){
mRootView = inflater.inflate(getLayoutId(),container,false);
}
initUI();
return mRootView;
}
/**
* 初始化
*/
public void initUI(){
surfaceView = mRootView.findViewById(getSurfaceViewId());
viewfinderView = mRootView.findViewById(getViewfinderViewId());
mCaptureHelper = new CaptureHelper(this,surfaceView,viewfinderView);
mCaptureHelper.setOnCaptureCallback(this);
}
/**
* 返回true时会自动初始化{@link #mRootView}返回为false时需自己去通过{@link #setRootView(View)}初始化{@link #mRootView}
* @param layoutId
* @return 默认返回true
*/
public boolean isContentView(@LayoutRes int layoutId){
return true;
}
/**
* 布局id
* @return
*/
public int getLayoutId(){
return R.layout.zxl_capture;
}
/**
* {@link ViewfinderView} 的 id
* @return
*/
public int getViewfinderViewId(){
return R.id.viewfinderView;
}
/**
* 预览界面{@link #surfaceView} 的id
* @return
*/
public int getSurfaceViewId(){
return R.id.surfaceView;
}
/**
* Get {@link CaptureHelper}
* @return {@link #mCaptureHelper}
*/
public CaptureHelper getCaptureHelper(){
return mCaptureHelper;
}
/**
* Get {@link CameraManager}
* @return {@link #mCaptureHelper#getCameraManager()}
*/
public CameraManager getCameraManager(){
return mCaptureHelper.getCameraManager();
}
//--------------------------------------------
public View getRootView() {
return mRootView;
}
public void setRootView(View rootView) {
this.mRootView = rootView;
}
//--------------------------------------------
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mCaptureHelper.onCreate();
}
@Override
public void onResume() {
super.onResume();
mCaptureHelper.onResume();
}
@Override
public void onPause() {
super.onPause();
mCaptureHelper.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
mCaptureHelper.onDestroy();
}
/**
* 接收扫码结果回调
* @param result 扫码结果
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截
*/
@Override
public boolean onResultCallback(String result) {
return false;
}
}

View File

@@ -1,169 +1,154 @@
package com.king.zxing; package com.king.zxing;
/* import android.app.Activity;
* Copyright (C) 2008 ZXing authors import android.content.ActivityNotFoundException;
* import android.content.Intent;
* Licensed under the Apache License, Version 2.0 (the "License"); import android.content.pm.PackageManager;
* you may not use this file except in compliance with the License. import android.content.pm.ResolveInfo;
* You may obtain a copy of the License at import android.graphics.Bitmap;
* import android.graphics.BitmapFactory;
* http://www.apache.org/licenses/LICENSE-2.0 import android.net.Uri;
* import android.os.Bundle;
* Unless required by applicable law or agreed to in writing, software import android.os.Handler;
* distributed under the License is distributed on an "AS IS" BASIS, import android.os.Message;
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. import android.provider.Browser;
* See the License for the specific language governing permissions and import android.util.Log;
* limitations under the License.
*/ import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import android.content.ActivityNotFoundException; import com.king.zxing.camera.CameraManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import java.util.Collection;
import android.graphics.BitmapFactory; import java.util.Map;
import android.provider.Browser;
import com.google.zxing.BarcodeFormat; /**
import com.google.zxing.DecodeHintType; * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
import com.google.zxing.Result; */
import com.king.zxing.camera.CameraManager; public class CaptureHandler extends Handler {
import android.app.Activity; private static final String TAG = CaptureHandler.class.getSimpleName();
import android.content.Intent;
import android.graphics.Bitmap; private final OnCaptureListener onCaptureListener;
import android.net.Uri; private final DecodeThread decodeThread;
import android.os.Bundle; private State state;
import android.os.Handler; private final CameraManager cameraManager;
import android.os.Message; private final Activity activity;
import android.util.Log; private final ViewfinderView viewfinderView;
import java.util.Collection;
import java.util.Map; private enum State {
PREVIEW,
/** SUCCESS,
* This class handles all the messaging which comprises the state machine for capture. DONE
* }
* @author dswitkin@google.com (Daniel Switkin)
*/ CaptureHandler(Activity activity,ViewfinderView viewfinderView,OnCaptureListener onCaptureListener,
public final class CaptureActivityHandler extends Handler { Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints,
private static final String TAG = CaptureActivityHandler.class.getSimpleName(); String characterSet,
CameraManager cameraManager) {
private final CaptureActivity activity; this.activity = activity;
private final DecodeThread decodeThread; this.viewfinderView = viewfinderView;
private State state; this.onCaptureListener = onCaptureListener;
private final CameraManager cameraManager; decodeThread = new DecodeThread(activity,cameraManager,this, decodeFormats, baseHints, characterSet,
new ViewfinderResultPointCallback(viewfinderView));
private enum State { decodeThread.start();
PREVIEW, state = State.SUCCESS;
SUCCESS,
DONE // Start ourselves capturing previews and decoding.
} this.cameraManager = cameraManager;
cameraManager.startPreview();
CaptureActivityHandler(CaptureActivity activity, restartPreviewAndDecode();
Collection<BarcodeFormat> decodeFormats, }
Map<DecodeHintType,?> baseHints,
String characterSet, @Override
CameraManager cameraManager) { public void handleMessage(Message message) {
this.activity = activity; if (message.what == R.id.restart_preview) {
decodeThread = new DecodeThread(activity, decodeFormats, baseHints, characterSet, restartPreviewAndDecode();
new ViewfinderResultPointCallback(activity.getViewfinderView()));
decodeThread.start(); } else if (message.what == R.id.decode_succeeded) {
state = State.SUCCESS; state = State.SUCCESS;
Bundle bundle = message.getData();
// Start ourselves capturing previews and decoding. Bitmap barcode = null;
this.cameraManager = cameraManager; float scaleFactor = 1.0f;
cameraManager.startPreview(); if (bundle != null) {
restartPreviewAndDecode(); byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
} if (compressedBitmap != null) {
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
@Override // Mutable copy:
public void handleMessage(Message message) { barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
if (message.what == R.id.restart_preview) { }
restartPreviewAndDecode(); scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
}
} else if (message.what == R.id.decode_succeeded) { onCaptureListener.onHandleDecode((Result) message.obj, barcode, scaleFactor);
state = State.SUCCESS;
Bundle bundle = message.getData(); } else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails, start another.
Bitmap barcode = null; state = State.PREVIEW;
float scaleFactor = 1.0f; cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
if (bundle != null) {
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP); } else if (message.what == R.id.return_scan_result) {
if (compressedBitmap != null) { activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null); activity.finish();
// Mutable copy:
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true); } else if (message.what == R.id.launch_product_query) {
} String url = (String) message.obj;
scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
} Intent intent = new Intent(Intent.ACTION_VIEW);
activity.handleDecode((Result) message.obj, barcode, scaleFactor); intent.addFlags(Intents.FLAG_NEW_DOC);
intent.setData(Uri.parse(url));
} else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails, start another.
state = State.PREVIEW; ResolveInfo resolveInfo =
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode); activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
String browserPackageName = null;
} else if (message.what == R.id.return_scan_result) { if (resolveInfo != null && resolveInfo.activityInfo != null) {
activity.setResult(Activity.RESULT_OK, (Intent) message.obj); browserPackageName = resolveInfo.activityInfo.packageName;
activity.finish(); Log.d(TAG, "Using browser in package " + browserPackageName);
}
} else if (message.what == R.id.launch_product_query) {
String url = (String) message.obj; // Needed for default Android browser / Chrome only apparently
if (browserPackageName != null) {
Intent intent = new Intent(Intent.ACTION_VIEW); switch (browserPackageName) {
intent.addFlags(Intents.FLAG_NEW_DOC); case "com.android.browser":
intent.setData(Uri.parse(url)); case "com.android.chrome":
intent.setPackage(browserPackageName);
ResolveInfo resolveInfo = intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);
String browserPackageName = null; break;
if (resolveInfo != null && resolveInfo.activityInfo != null) { }
browserPackageName = resolveInfo.activityInfo.packageName; }
Log.d(TAG, "Using browser in package " + browserPackageName);
} try {
activity.startActivity(intent);
// Needed for default Android browser / Chrome only apparently } catch (ActivityNotFoundException ignored) {
if (browserPackageName != null) { Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);
switch (browserPackageName) { }
case "com.android.browser":
case "com.android.chrome": }
intent.setPackage(browserPackageName); }
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName); public void quitSynchronously() {
break; state = State.DONE;
} cameraManager.stopPreview();
} Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
quit.sendToTarget();
try { try {
activity.startActivity(intent); // Wait at most half a second; should be enough time, and onPause() will timeout quickly
} catch (ActivityNotFoundException ignored) { decodeThread.join(500L);
Log.w(TAG, "Can't find anything to handle VIEW of URI " + url); } catch (InterruptedException e) {
} // continue
}
}
} // Be absolutely sure we don't send any queued up messages
removeMessages(R.id.decode_succeeded);
public void quitSynchronously() { removeMessages(R.id.decode_failed);
state = State.DONE; }
cameraManager.stopPreview();
Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit); public void restartPreviewAndDecode() {
quit.sendToTarget(); if (state == State.SUCCESS) {
try { state = State.PREVIEW;
// Wait at most half a second; should be enough time, and onPause() will timeout quickly cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
decodeThread.join(500L); viewfinderView.drawViewfinder();
} catch (InterruptedException e) { }
// continue }
} }
// Be absolutely sure we don't send any queued up messages
removeMessages(R.id.decode_succeeded);
removeMessages(R.id.decode_failed);
}
public void restartPreviewAndDecode() {
if (state == State.SUCCESS) {
state = State.PREVIEW;
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
activity.drawViewfinder();
}
}
}

View File

@@ -0,0 +1,522 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.king.zxing;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.king.zxing.camera.CameraManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureHelper implements CaptureLifecycle,CaptureTouchEvent,CaptureManager {
public static final String TAG = CaptureHelper.class.getSimpleName();
private Activity activity;
private CaptureHandler captureHandler;
private OnCaptureListener onCaptureListener;
private CameraManager cameraManager;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private AmbientLightManager ambientLightManager;
private ViewfinderView viewfinderView;
private SurfaceHolder surfaceHolder;
private SurfaceHolder.Callback callback;
private Collection<BarcodeFormat> decodeFormats;
private Map<DecodeHintType,?> decodeHints;
private String characterSet;
private boolean hasSurface;
/**
* 默认触控误差值
*/
private static final int DEVIATION = 6;
/**
* 是否支持缩放(变焦),默认支持
*/
private boolean isSupportZoom = true;
private float oldDistance;
/**
* 是否支持连扫,默认不支持
*/
private boolean isContinuousScan = false;
/**
* 连扫时,是否自动重置预览和解码器,默认自动重置
*/
private boolean isAutoRestartPreviewAndDecode = true;
/**
* 是否播放音效
*/
private boolean isPlayBeep;
/**
* 是否震动
*/
private boolean isVibrate;
private OnCaptureCallback onCaptureCallback;
public CaptureHelper(Fragment fragment, SurfaceView surfaceView, ViewfinderView viewfinderView){
this(fragment.getActivity(),surfaceView,viewfinderView);
}
public CaptureHelper(Activity activity,SurfaceView surfaceView,ViewfinderView viewfinderView){
this.activity = activity;
this.viewfinderView = viewfinderView;
surfaceHolder = surfaceView.getHolder();
hasSurface = false;
}
@Override
public void onCreate(){
inactivityTimer = new InactivityTimer(activity);
beepManager = new BeepManager(activity);
ambientLightManager = new AmbientLightManager(activity);
cameraManager = new CameraManager(activity);
callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
};
onCaptureListener = new OnCaptureListener() {
@Override
public void onHandleDecode(Result result, Bitmap barcode, float scaleFactor) {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();
onResult(result);
}
};
//设置是否播放音效和震动
beepManager.setPlayBeep(isPlayBeep);
beepManager.setVibrate(isVibrate);
}
/**
* {@link Activity#onResume()}
*/
@Override
public void onResume(){
beepManager.updatePrefs();
ambientLightManager.start(cameraManager);
inactivityTimer.onResume();
surfaceHolder.addCallback(callback);
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(callback);
}
}
/**
* {@link Activity#onPause()}
*/
@Override
public void onPause(){
if (captureHandler != null) {
captureHandler.quitSynchronously();
captureHandler = null;
}
inactivityTimer.onPause();
ambientLightManager.stop();
beepManager.close();
cameraManager.closeDriver();
if (!hasSurface) {
surfaceHolder.removeCallback(callback);
}
}
/**
* {@link Activity#onDestroy()}
*/
@Override
public void onDestroy(){
inactivityTimer.shutdown();
}
/**
* 支持缩放时,须在{@link Activity#onTouchEvent(MotionEvent)}调用此方法
* @param event
*/
@Override
public boolean onTouchEvent(MotionEvent event){
if(isSupportZoom && cameraManager.isOpen()){
Camera camera = cameraManager.getOpenCamera().getCamera();
if(camera ==null){
return false;
}
if(event.getPointerCount() > 1) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {//多点触控
case MotionEvent.ACTION_POINTER_DOWN:
oldDistance = calcFingerSpacing(event);
break;
case MotionEvent.ACTION_MOVE:
float newDistance = calcFingerSpacing(event);
if (newDistance > oldDistance + DEVIATION) {//
handleZoom(true, camera);
} else if (newDistance < oldDistance - DEVIATION) {
handleZoom(false, camera);
}
oldDistance = newDistance;
break;
}
return true;
}
}
return false;
}
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (captureHandler == null) {
captureHandler = new CaptureHandler(activity,viewfinderView,onCaptureListener, decodeFormats, decodeHints, characterSet, cameraManager);
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
}
}
/**
* 处理变焦缩放
* @param isZoomIn
* @param camera
*/
private void handleZoom(boolean isZoomIn, Camera camera) {
Camera.Parameters params = camera.getParameters();
if (params.isZoomSupported()) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
if (isZoomIn && zoom < maxZoom) {
zoom++;
} else if (zoom > 0) {
zoom--;
}
params.setZoom(zoom);
camera.setParameters(params);
} else {
Log.i(TAG, "zoom not supported");
}
}
/**
* 聚焦
* @param event
* @param camera
*/
private void focusOnTouch(MotionEvent event,Camera camera) {
Camera.Parameters params = camera.getParameters();
Camera.Size previewSize = params.getPreviewSize();
Rect focusRect = calcTapArea(event.getRawX(), event.getRawY(), 1f,previewSize);
Rect meteringRect = calcTapArea(event.getRawX(), event.getRawY(), 1.5f,previewSize);
Camera.Parameters parameters = camera.getParameters();
if (parameters.getMaxNumFocusAreas() > 0) {
List<Camera.Area> focusAreas = new ArrayList<>();
focusAreas.add(new Camera.Area(focusRect, 600));
parameters.setFocusAreas(focusAreas);
}
if (parameters.getMaxNumMeteringAreas() > 0) {
List<Camera.Area> meteringAreas = new ArrayList<>();
meteringAreas.add(new Camera.Area(meteringRect, 600));
parameters.setMeteringAreas(meteringAreas);
}
final String currentFocusMode = params.getFocusMode();
params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
camera.setParameters(params);
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
Camera.Parameters params = camera.getParameters();
params.setFocusMode(currentFocusMode);
camera.setParameters(params);
}
});
}
/**
* 计算两指间距离
* @param event
* @return
*/
private float calcFingerSpacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
/**
* 计算对焦区域
* @param x
* @param y
* @param coefficient
* @param previewSize
* @return
*/
private Rect calcTapArea(float x, float y, float coefficient, Camera.Size previewSize) {
float focusAreaSize = 200;
int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
int centerX = (int) ((x / previewSize.width) * 2000 - 1000);
int centerY = (int) ((y / previewSize.height) * 2000 - 1000);
int left = clamp(centerX - (areaSize / 2), -1000, 1000);
int top = clamp(centerY - (areaSize / 2), -1000, 1000);
RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
return new Rect(Math.round(rectF.left), Math.round(rectF.top),
Math.round(rectF.right), Math.round(rectF.bottom));
}
/**
*
* @param x
* @param min
* @param max
* @return
*/
private int clamp(int x, int min, int max) {
if (x > max) {
return max;
}
if (x < min) {
return min;
}
return x;
}
/**
* 重新启动扫码和解码器
*/
public void restartPreviewAndDecode(){
if(captureHandler!=null){
captureHandler.restartPreviewAndDecode();
}
}
/**
* 接收扫码结果,想支持连扫时,可将{@link #continuousScan(boolean)}设置为{@code true}
* 如果{@link #isContinuousScan}支持连扫,则默认重启扫码和解码器;当连扫逻辑太复杂时,
* 请将{@link #autoRestartPreviewAndDecode(boolean)}设置为{@code false},并手动调用{@link #restartPreviewAndDecode()}
* @param result 扫码结果
*/
public void onResult(Result result){
String text = result.getText();
if(isContinuousScan){
if(isAutoRestartPreviewAndDecode){
if(onCaptureCallback!=null){
onCaptureCallback.onResultCallback(text);
}
restartPreviewAndDecode();
}
}else{
//如果设置了回调并且onCallback返回为true则表示拦截
if(onCaptureCallback!=null && onCaptureCallback.onResultCallback(text)){
return;
}
Intent intent = new Intent();
intent.putExtra(Intents.Scan.RESULT,text);
activity.setResult(Activity.RESULT_OK,intent);
activity.finish();
}
}
/**
* 设置是否连续扫码,如果想支持连续扫码,则将此方法返回{@code true}并重写{@link #onResult(Result)}
*/
public CaptureHelper continuousScan(boolean isContinuousScan){
this.isContinuousScan = isContinuousScan;
return this;
}
/**
* 设置是否自动重启扫码和解码器,当支持连扫时才起作用。
* @return 默认返回 true
*/
public CaptureHelper autoRestartPreviewAndDecode(boolean isAutoRestartPreviewAndDecode){
this.isAutoRestartPreviewAndDecode = isAutoRestartPreviewAndDecode;
return this;
}
/**
* 设置是否播放音效
* @return
*/
public CaptureHelper playBeep(boolean playBeep){
this.isPlayBeep = playBeep;
if(beepManager!=null){
beepManager.setPlayBeep(playBeep);
}
return this;
}
/**
* 设置是否震动
* @return
*/
public CaptureHelper vibrate(boolean vibrate){
this.isVibrate = vibrate;
if(beepManager!=null){
beepManager.setVibrate(vibrate);
}
return this;
}
/**
* 设置是否支持缩放
* @param supportZoom
* @return
*/
public CaptureHelper supportZoom(boolean supportZoom) {
isSupportZoom = supportZoom;
return this;
}
/**
*
* @param decodeFormats
* @return
*/
public CaptureHelper decodeFormats(Collection<BarcodeFormat> decodeFormats) {
this.decodeFormats = decodeFormats;
return this;
}
/**
*
* @param decodeHints
* @return
*/
public CaptureHelper decodeHints(Map<DecodeHintType,?> decodeHints) {
this.decodeHints = decodeHints;
return this;
}
/**
*
* @param characterSet
* @return
*/
public CaptureHelper characterSet(String characterSet) {
this.characterSet = characterSet;
return this;
}
/**
* 设置扫码回调
* @param callback
* @return
*/
public CaptureHelper setOnCaptureCallback(OnCaptureCallback callback) {
this.onCaptureCallback = callback;
return this;
}
@Override
public CameraManager getCameraManager() {
return cameraManager;
}
@Override
public BeepManager getBeepManager() {
return beepManager;
}
@Override
public AmbientLightManager getAmbientLightManager() {
return ambientLightManager;
}
@Override
public InactivityTimer getInactivityTimer() {
return inactivityTimer;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.king.zxing;
import android.app.Activity;
import android.os.Bundle;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface CaptureLifecycle {
/**
* {@link android.app.Activity#onCreate(Bundle)}
*/
void onCreate();
/**
* {@link Activity#onResume()}
*/
void onResume();
/**
* {@link Activity#onPause()}
*/
void onPause();
/**
* {@link Activity#onDestroy()}
*/
void onDestroy();
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.king.zxing;
import com.king.zxing.camera.CameraManager;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface CaptureManager {
/**
* Get {@link CameraManager}
* @return {@link CameraManager}
*/
CameraManager getCameraManager();
/**
* Get {@link BeepManager}
* @return {@link BeepManager}
*/
BeepManager getBeepManager();
/**
* Get {@link AmbientLightManager}
* @return {@link AmbientLightManager}
*/
AmbientLightManager getAmbientLightManager();
/**
* Get {@link InactivityTimer}
* @return {@link InactivityTimer}
*/
InactivityTimer getInactivityTimer();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.king.zxing;
import android.view.MotionEvent;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface CaptureTouchEvent {
/**
* {@link android.app.Activity#onTouchEvent(MotionEvent)}
*/
boolean onTouchEvent(MotionEvent event);
}

View File

@@ -23,10 +23,10 @@ import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException; import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer; import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer; import com.google.zxing.common.HybridBinarizer;
import com.king.zxing.camera.CameraManager;
import android.graphics.Point; import android.graphics.Point;
import android.os.Bundle; import android.os.Bundle;
@@ -44,14 +44,18 @@ final class DecodeHandler extends Handler {
private static final String TAG = DecodeHandler.class.getSimpleName(); private static final String TAG = DecodeHandler.class.getSimpleName();
private final CaptureActivity activity; private final Context context;
private final CameraManager cameraManager;
private final Handler handler;
private final MultiFormatReader multiFormatReader; private final MultiFormatReader multiFormatReader;
private boolean running = true; private boolean running = true;
DecodeHandler(CaptureActivity activity, Map<DecodeHintType,Object> hints) { DecodeHandler(Context context, CameraManager cameraManager,Handler handler, Map<DecodeHintType,Object> hints) {
multiFormatReader = new MultiFormatReader(); multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(hints); multiFormatReader.setHints(hints);
this.activity = activity; this.context = context;
this.cameraManager = cameraManager;
this.handler = handler;
} }
@Override @Override
@@ -70,7 +74,7 @@ final class DecodeHandler extends Handler {
} }
private boolean isScreenPortrait(){ private boolean isScreenPortrait(){
WindowManager manager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay(); Display display = manager.getDefaultDisplay();
Point screenResolution = new Point(); Point screenResolution = new Point();
display.getSize(screenResolution); display.getSize(screenResolution);
@@ -98,9 +102,9 @@ final class DecodeHandler extends Handler {
int tmp = width; int tmp = width;
width = height; width = height;
height = tmp; height = tmp;
source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height); source = cameraManager.buildLuminanceSource(rotatedData, width, height);
}else{ }else{
source = activity.getCameraManager().buildLuminanceSource(data, width, height); source = cameraManager.buildLuminanceSource(data, width, height);
} }
if (source != null) { if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
@@ -118,7 +122,6 @@ final class DecodeHandler extends Handler {
} }
} }
Handler handler = activity.getHandler();
if (rawResult != null) { if (rawResult != null) {
// Don't log the barcode contents for security. // Don't log the barcode contents for security.
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();

View File

@@ -20,7 +20,9 @@ package com.king.zxing;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType; import com.google.zxing.DecodeHintType;
import com.google.zxing.ResultPointCallback; import com.google.zxing.ResultPointCallback;
import com.king.zxing.camera.CameraManager;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -43,18 +45,23 @@ final class DecodeThread extends Thread {
public static final String BARCODE_BITMAP = "barcode_bitmap"; public static final String BARCODE_BITMAP = "barcode_bitmap";
public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor"; public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";
private final CaptureActivity activity; private final Context context;
private final CameraManager cameraManager;
private final Map<DecodeHintType,Object> hints; private final Map<DecodeHintType,Object> hints;
private Handler handler; private Handler handler;
private CaptureHandler captureHandler;
private final CountDownLatch handlerInitLatch; private final CountDownLatch handlerInitLatch;
DecodeThread(CaptureActivity activity, DecodeThread(Context context,CameraManager cameraManager,
CaptureHandler captureHandler,
Collection<BarcodeFormat> decodeFormats, Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints, Map<DecodeHintType,?> baseHints,
String characterSet, String characterSet,
ResultPointCallback resultPointCallback) { ResultPointCallback resultPointCallback) {
this.activity = activity; this.context = context;
this.cameraManager = cameraManager;
this.captureHandler = captureHandler;
handlerInitLatch = new CountDownLatch(1); handlerInitLatch = new CountDownLatch(1);
hints = new EnumMap<>(DecodeHintType.class); hints = new EnumMap<>(DecodeHintType.class);
@@ -64,7 +71,7 @@ final class DecodeThread extends Thread {
// The prefs can't change while the thread is running, so pick them up once here. // The prefs can't change while the thread is running, so pick them up once here.
if (decodeFormats == null || decodeFormats.isEmpty()) { if (decodeFormats == null || decodeFormats.isEmpty()) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
decodeFormats = EnumSet.noneOf(BarcodeFormat.class); decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
if (prefs.getBoolean(Preferences.KEY_DECODE_1D_PRODUCT, true)) { if (prefs.getBoolean(Preferences.KEY_DECODE_1D_PRODUCT, true)) {
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS); decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
@@ -106,7 +113,7 @@ final class DecodeThread extends Thread {
@Override @Override
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
handler = new DecodeHandler(activity, hints); handler = new DecodeHandler(context,cameraManager,captureHandler, hints);
handlerInitLatch.countDown(); handlerInitLatch.countDown();
Looper.loop(); Looper.loop();
} }

View File

@@ -0,0 +1,16 @@
package com.king.zxing;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface OnCaptureCallback {
/**
* 接收扫码结果回调
* @param result 扫码结果
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截
*/
boolean onResultCallback(String result);
}

View File

@@ -0,0 +1,22 @@
package com.king.zxing;
import android.graphics.Bitmap;
import com.google.zxing.Result;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface OnCaptureListener {
/**
* 接收解码后的扫码结果
* @param result
* @param barcode
* @param scaleFactor
*/
void onHandleDecode(Result result, Bitmap barcode, float scaleFactor);
}

View File

@@ -66,7 +66,7 @@ public final class CameraManager {
private final PreviewCallback previewCallback; private final PreviewCallback previewCallback;
public CameraManager(Context context) { public CameraManager(Context context) {
this.context = context; this.context = context.getApplicationContext();
this.configManager = new CameraConfigurationManager(context); this.configManager = new CameraConfigurationManager(context);
previewCallback = new PreviewCallback(configManager); previewCallback = new PreviewCallback(configManager);
} }

View File

@@ -1,25 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!--
Copyright (C) 2008 ZXing authors Copyright (C) 2008 ZXing authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
<SurfaceView android:layout_height="match_parent">
android:id="@+id/preview_view"
android:layout_width="match_parent" <SurfaceView
android:layout_height="match_parent"/> android:id="@+id/surfaceView"
<com.king.zxing.ViewfinderView android:layout_width="match_parent"
android:id="@+id/viewfinder_view" android:layout_height="match_parent"/>
android:layout_width="match_parent" <com.king.zxing.ViewfinderView
android:layout_height="match_parent"/> android:id="@+id/viewfinderView"
android:layout_width="match_parent"
</merge> android:layout_height="match_parent"/>
</FrameLayout>

View File

@@ -4,7 +4,7 @@
<color name="viewfinder_mask">#60000000</color> <color name="viewfinder_mask">#60000000</color>
<color name="viewfinder_frame">#7F1FB3E2</color> <color name="viewfinder_frame">#7F1FB3E2</color>
<color name="viewfinder_corner">#FF1FB3E2</color> <color name="viewfinder_corner">#FF1FB3E2</color>
<color name="viewfinder_laser">#FF1FB3E2</color> <!-- Android standard ICS color --> <color name="viewfinder_laser">#FF1FB3E2</color>
<color name="viewfinder_result_point_color">#C0FFBD21</color> <color name="viewfinder_result_point_color">#C0FFBD21</color>
<color name="viewfinder_text_color">#FFC0C0C0</color> <color name="viewfinder_text_color">#FFC0C0C0</color>

View File

@@ -1,7 +1,7 @@
//App //App
def app_version = [:] def app_version = [:]
app_version.versionCode = 8 app_version.versionCode = 9
app_version.versionName = "1.0.7" app_version.versionName = "1.1.0"
ext.app_version = app_version ext.app_version = app_version
//build version //build version