第三方Zxing的GitHub地址:
tip:前面几种是为了更好的理解,也可直接划到第三种看仿微信扫码的那种。
Step 1 :添加依赖
//第三方zxing
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
Step 2 :添加权限
<uses-permission android:name="android.permission.CAMERA"/>
Step 3 :activity_main.xml布局 添加 测试用的两个控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始"
android:id="@+id/button"/>
<ImageView
android:id="@+id/iv_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Step 4 :MainActivity 代码:
public class MainActivity extends AppCompatActivity {
private Button button;
private ImageView ivImage;
// Step 1 : 初始化 获取控件 设置监听
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取测试的控件
button = findViewById(R.id.button);//点击跳转到扫码活动
ivImage = findViewById(R.id.iv_image);//输出二维码图片
//控件监听
listenerView();
}
private void listenerView() {
// Step 2 :跳转到扫描活动
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//=======设置扫描活动 可根据需求设置以下内容
IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);
// 1.扫描成功后的提示音,默认关闭
intentIntegrator.setBeepEnabled(true);
// 2.启动后置摄像头扫描,若为 1 为前置摄像头,默认后置
intentIntegrator.setCameraId(0);
/* 3.设置扫描的条码的格式:默认为所有类型
* IntentIntegrator.PRODUCT_CODE_TYPES:商品码类型
* IntentIntegrator.ONE_D_CODE_TYPES:一维码类型
* IntentIntegrator.QR_CODE:二维码
* IntentIntegrator.DATA_MATRIX:数据矩阵类型
* IntentIntegrator.ALL_CODE_TYPES:所类有型
* */
intentIntegrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES);
/* 4.方向锁:true为锁定,false反之,默认锁定.
ps:在AndroidManifest.xml里设置以下属性,则扫码界面完全依赖传感器(tools红色提示,指向它会提示,点击左边蓝色Create...即可)
* */
intentIntegrator.setOrientationLocked(true);
// 5.设置扫描界面的提示信息:默认为:请将条码置于取景框内扫描。(ps:设置没提示文字:setPrompt(""))
intentIntegrator.setPrompt("请选择二维码");
// 6.设置关闭扫描的时间(单位:毫秒),不设置不关闭
intentIntegrator.setTimeout(60000);
// 7.保存二维码图片:在onActivityResult方法里可获取保存的路径,根据需要来是否需要保存
intentIntegrator.setBarcodeImageEnabled(true);
//启动扫描
intentIntegrator.initiateScan();
}
});
}
// Step 3 :处理扫码后返回的结果
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);
if(result!=null){
//==是否扫到内容
if (result.getContents()!=null){
Toast.makeText(this,"扫描结果:"+result.getContents(),Toast.LENGTH_LONG).show();
}else{
Toast.makeText(this,"取消扫码",Toast.LENGTH_LONG).show();
}
//==是否有保存照片的路径 在intentIntegrator已设置保存照片
if(result.getBarcodeImagePath()!=null){
FileInputStream file=null;
try {
file=new FileInputStream(new File(result.getBarcodeImagePath()));
ivImage.setImageBitmap(BitmapFactory.decodeStream(file));//显示获取的照片
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/* 获取条码种类:在intentIntegrator.setDesiredBarcodeFormats那设置扫码格式后(点击格式可进入查看该格式有多少个类型)
例如:PRODUCT_CODE_TYPES:商品码类型,它就有 UPC_A, UPC_E, EAN_8, EAN_13, RSS_14 种类
public static final Collection PRODUCT_CODE_TYPES = list(UPC_A, UPC_E, EAN_8, EAN_13, RSS_14);
根据getFormatName获取到的种类,就知道是哪个扫码格式,进而根据需求进行相关操作
*/
if (result.getFormatName()!=null){
Toast.makeText(this,"图片格式:"+result.getFormatName(),Toast.LENGTH_LONG).show();
}
}else{
super.onActivityResult(requestCode, resultCode, data);
}
}
//=========PS:Android6.0以后的版本还需动态申请权限,若需动态申请,请在MainActivity中添加申请权限(调用此方法)再做打开相机扫描
//暂时未发现需要动态申请(本人手机安卓10),可能不同手机厂商做法
private void requsetPermission(){
if (Build.VERSION.SDK_INT>22){
if (ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
//先判断有没有权限 ,没有就在这里进行权限的申请
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{android.Manifest.permission.CAMERA},1);
}else {
}
}else {
}
}
//============PS:申请权限后的方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
//已获取权限,写需要做的代码
}else {
//拒绝摄像头权限,可以提示用户给权限
Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
第一种:带闪光灯
Step 1 :引入依赖:
//第三方zxing
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
Step 2 :申请权限:
<!--相机-->
<uses-permission android:name="android.permission.CAMERA"/>
<!--若需要闪光灯权限 ,请加入此权限(自测不需要)-->
<!--
<uses-permission android:name="android.permission.FLASHLIGHT" />
-->
Step 3 :准备3个布局
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始"
android:id="@+id/button"/>
</LinearLayout>
content_scan.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--
layout_width、layout_height:启动扫描界面的布局参数
zxing_framing_rect_width、zxing_framing_rect_height:
在扫描界面中,只能扫描二维码的宽高,去掉后会有默认的宽高
-->
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zxing_barcode_surface"
app:zxing_framing_rect_width="250dp"
app:zxing_framing_rect_height="250dp"/>
<com.journeyapps.barcodescanner.ViewfinderView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zxing_viewfinder_view"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
</merge>
activity_scan.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--装扫描界面的控件
@layout/content_scan:为嵌入content_scan.xml的布局
-->
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/dbv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
app:zxing_scanner_layout="@layout/content_scan">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
<!--闪光灯图片 自行找图片样式
@drawable/ic_flashlight_close 关闭时的图片
-->
<ImageButton
android:id="@+id/ib_flashlight_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:background="@drawable/ic_flashlight_close"/>
</RelativeLayout>
Step 4 :ScanActivity.java
public class ScanActivity extends AppCompatActivity {
private CaptureManager capture;
private ImageButton ibFlashlight;
private DecoratedBarcodeView barcodeScannerView;
private boolean bTorch = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//==设置布局、获取控件
setContentView(R.layout.activity_scan);
barcodeScannerView = findViewById(R.id.dbv);
ibFlashlight= findViewById(R.id.ib_flashlight_close);
//==监听: 根据barcodeScannerView设置闪光灯ibFlashlight状态
barcodeScannerView.setTorchListener(new DecoratedBarcodeView.TorchListener() {
@Override
public void onTorchOn() {//开灯
//R.drawable.ic_flashlight_open) 开灯显示的图片 自行找图片样式
ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_open));
bTorch = true;
}
@Override
public void onTorchOff() {//关灯
//R.drawable.ic_flashlight_close) 关灯显示的图片 自行找图片样式
ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_close));
bTorch = false;
}
});
//==开或关灯
ibFlashlight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(bTorch){
barcodeScannerView.setTorchOff();
} else {
barcodeScannerView.setTorchOn();
}
}
});
//==初始化活动
capture = new CaptureManager(this, barcodeScannerView);
capture.initializeFromIntent(getIntent(), savedInstanceState);
capture.decode();
}
@Override
protected void onResume() {
super.onResume();
capture.onResume();
}
@Override
protected void onPause() {
super.onPause();
capture.onPause();
barcodeScannerView.setTorchOff();
}
@Override
protected void onDestroy() {
super.onDestroy();
capture.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
capture.onSaveInstanceState(outState);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
}
Step 5 :MainActivity 代码:
public class MainActivity extends AppCompatActivity {
private Button button;
// Step 1 : 初始化 获取控件 设置监听
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取测试的控件
button = findViewById(R.id.button);//点击跳转到扫码活动
//控件监听
listenerView();
}
private void listenerView() {
// Step 2 :跳转到扫描活动
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//=======设置扫描活动 可根据需求设置以下内容
IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);
//启动自定义的扫描活动,不设置则启动默认的活动
intentIntegrator.setCaptureActivity(ScanActivity.class);
//启动扫描
intentIntegrator.initiateScan();
}
});
}
// Step 3 :处理扫码后返回的结果
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);
if(result!=null){
//==是否扫到内容
if (result.getContents()!=null){
Toast.makeText(this,"扫描结果:"+result.getContents(),Toast.LENGTH_LONG).show();
}else{
Toast.makeText(this,"取消扫码",Toast.LENGTH_LONG).show();
}
}else{
super.onActivityResult(requestCode, resultCode, data);
}
}
//=========PS:Android6.0以后的版本还需动态申请权限,若需动态申请,请在MainActivity中添加申请权限(调用此方法)再做打开相机扫描
//暂时未发现需要动态申请(本人手机安卓10),可能不同手机厂商做法
private void requsetPermission(){
if (Build.VERSION.SDK_INT>22){
if (ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
//先判断有没有权限 ,没有就在这里进行权限的申请
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{android.Manifest.permission.CAMERA},1);
}else {
}
}else {
}
}
//============PS:申请权限后的方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
//已获取权限,写需要做的代码
}else {
//拒绝摄像头权限,可以提示用户给权限
Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
第二种:带闪光灯 并修改其扫描界面
国际惯例,先上图(ScanWidget代码里有介绍去掉四个角样式)本来是动图的
tip:上面已经有介绍添加依赖和权限了,这里不多说,
并且,所用到的xml和activity和第一种的相同。
不同之处:
1.新建一个自定义的扫描活动:ScanWidget,代码如下
public class ScanWidget extends ViewfinderView {
/* ****************************************** 边角线相关属性 ************************************************/
/**
* "边角线长度/扫描边框长度"的占比 (比例越大,线越长)
*/
public float mLineRate = 0.1F;
/**
* 边角线厚度 (建议使用dp)
*/
public float mLineDepth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
/**
* 边角线颜色
*/
public int mLineColor = Color.WHITE;
/* ******************************************* 扫描线相关属性 ************************************************/
/**
* 扫描线起始位置
*/
public int mScanLinePosition = 0;
/**
* 扫描线厚度
*/
public float mScanLineDepth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
/**
* 扫描线每次重绘的移动距离
*/
public float mScanLineDy = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
/**
* 线性梯度
*/
public LinearGradient mLinearGradient;
/**
* 线性梯度位置
*/
public float[] mPositions = new float[]{0f, 0.5f, 1f};
/**
* 线性梯度各个位置对应的颜色值
*/
public int[] mScanLineColor = new int[]{0x00FFFFFF, Color.WHITE, 0x00FFFFFF};
// This constructor is used when the class is built from an XML resource.
public ScanWidget(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onDraw(Canvas canvas) {
refreshSizes();
if (framingRect == null || previewFramingRect == null) {
return;
}
final Rect frame = framingRect;
final Rect previewFrame = previewFramingRect;
//=====绘制4个角 可以注释此段代码,就像微信那样只要扫描线在动的样式了
paint.setColor(mLineColor); // 定义四个角画笔的颜色(本身整个扫描界面都为此颜色,通过设置四个角距离而被覆盖,进而形成四个角)
//左上角
canvas.drawRect(frame.left, frame.top, frame.left + frame.width() * mLineRate, frame.top + mLineDepth, paint);
canvas.drawRect(frame.left, frame.top, frame.left + mLineDepth, frame.top + frame.height() * mLineRate, paint);
//右上角
canvas.drawRect(frame.right - frame.width() * mLineRate, frame.top, frame.right, frame.top + mLineDepth, paint);
canvas.drawRect(frame.right - mLineDepth, frame.top, frame.right, frame.top + frame.height() * mLineRate, paint);
//左下角
canvas.drawRect(frame.left, frame.bottom - mLineDepth, frame.left + frame.width() * mLineRate, frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - frame.height() * mLineRate, frame.left + mLineDepth, frame.bottom, paint);
//右下角
canvas.drawRect(frame.right - frame.width() * mLineRate, frame.bottom - mLineDepth, frame.right, frame.bottom, paint);
canvas.drawRect(frame.right - mLineDepth, frame.bottom - frame.height() * mLineRate, frame.right, frame.bottom, paint);
//=======扫描框为的颜色,灰色遮罩层,删除则无灰色遮罩层
/*
int width = canvas.getWidth();
int height = canvas.getHeight();
paint.setColor(resultBitmap != null ? resultColor : maskColor);//遮罩层的颜色
canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
*/
if (resultBitmap != null) {
// Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(CURRENT_POINT_OPACITY);
canvas.drawBitmap(resultBitmap, null, frame, paint);
} else {
// ===绘制扫描线
mScanLinePosition += mScanLineDy;
if(mScanLinePosition > frame.height()){
mScanLinePosition = 0;
}
mLinearGradient = new LinearGradient(frame.left, frame.top + mScanLinePosition, frame.right, frame.top + mScanLinePosition, mScanLineColor, mPositions, Shader.TileMode.CLAMP);
paint.setShader(mLinearGradient);
canvas.drawRect(frame.left, frame.top + mScanLinePosition, frame.right, frame.top + mScanLinePosition + mScanLineDepth, paint);
paint.setShader(null);
final float scaleX = frame.width() / (float) previewFrame.width();
final float scaleY = frame.height() / (float) previewFrame.height();
final int frameLeft = frame.left;
final int frameTop = frame.top;
/*去掉扫描区域的闪光点
if (!lastPossibleResultPoints.isEmpty()) {
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
paint.setColor(resultPointColor);
float radius = POINT_SIZE / 2.0f;
for (final ResultPoint point : lastPossibleResultPoints) {
canvas.drawCircle(
frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
radius, paint
);
}
lastPossibleResultPoints.clear();
}
*/
// draw current possible result points
if (!possibleResultPoints.isEmpty()) {
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
for (final ResultPoint point : possibleResultPoints) {
canvas.drawCircle(
frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
POINT_SIZE, paint
);
}
// swap and clear buffers
final List<ResultPoint> temp = possibleResultPoints;
possibleResultPoints = lastPossibleResultPoints;
lastPossibleResultPoints = temp;
possibleResultPoints.clear();
}
// Request another update at the animation interval, but only repaint the laser line,
// not the entire viewfinder mask.
postInvalidateDelayed(ANIMATION_DELAY,
frame.left - POINT_SIZE,
frame.top - POINT_SIZE,
frame.right + POINT_SIZE,
frame.bottom + POINT_SIZE);
}
}
}
2.content_scan.xml的代码为:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--
layout_width、layout_height:启动扫描界面的布局参数
zxing_framing_rect_width、zxing_framing_rect_height:
在扫描界面中,只能扫描二维码的宽高,去掉后会有默认的宽高
-->
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zxing_barcode_surface" />
<!--使用的是自定义的扫描活动-->
<com.gx.test.widget.ScanWidget
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zxing_viewfinder_view" />
</merge>
第三种:改进版,仿微信扫描条(本来是动图的,扫描线会动)
扫描条是图片,利用动画实现扫描条活动,第二种的扫描条是绘制的线,调快移动距离的话会感觉一卡一卡的,效果不好。
国际惯例,先上图:因虚拟机录制,看着扫描条一卡一卡的,实际手机调试不是
//第三方zxing
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
Step 2 :申请权限:
<!--相机-->
<uses-permission android:name="android.permission.CAMERA"/>
<!--若需要闪光灯权限 ,请加入此权限(自测不需要)-->
<!--
<uses-permission android:name="android.permission.FLASHLIGHT" />
-->
Step 3 :自定义 MyApplication
public class MyApplication extends Application {
private View view;
public View getView() {
return view;
}
public void setView(View view) {
this.view = view;
}
}
并在AndroidManifest.xml配置好
<application
android:name=".MyApplication"
Step 4 :ScanWidget 自定义扫描活动界面:
// Step 1 :继承 ViewfinderView 并 加控制器
public class ScanWidget extends ViewfinderView {
//边角线厚度
public float mLineDepth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
//边角线长度/扫描边框长度"的占比 (比例越大,线越长)
public float mLineRate = 0.1F;
public ScanWidget(Context context, AttributeSet attrs) {
super(context, attrs);
}
// Step 2 : 重写此方法:在此方法删除原来的扫描条等等样式,并加入自己的扫描样式
@Override
public void onDraw(Canvas canvas) {
refreshSizes();
if (framingRect == null || previewFramingRect == null) {
return;
}
final Rect frame = framingRect;
final Rect previewFrame = previewFramingRect;
final int width = canvas.getWidth();
final int height = canvas.getHeight();
//====================自己加入的扫描条动画在此处(扫描条其实是View控件放了个背景,view加入动画就实现了扫描条运动)↓
//ps:若是启用下面代码(带有 PS 的注释那段,请看其作用),请在全局定义boolean b=false,然后例:
//if(!b){这里写这段自己加入的动画代码; b=true;} 否则出现扫描条不运动,也不会因下面那PS提示的代码让这段代码反复执行。
//=====加入扫描条
MyApplication myApplication= (MyApplication) this.getContext().getApplicationContext();
//设置扫描条的参数
View view=myApplication.getView();
FrameLayout.LayoutParams params= (FrameLayout.LayoutParams) view.getLayoutParams();
params.width=frame.right-frame.left;//这是计算扫描框的宽度,进而设置扫描条的宽度
params.setMargins(frame.left,0,0,0);//设置左边距,让扫描条在横方向在扫描框里
view.setLayoutParams(params);
//设置扫描条的动画 注意这个 60 是扫描条的宽度 单位是px
//参数 3:运动开始的地方:frame.top是扫描框离屏幕顶部的距离,减60是因为 这个扫描条 的高是 60 px,
// 参数3的单位也是px,所以运动开始的地方就是 frame.top-60;参数4作用同3
Animation animation = new TranslateAnimation(0, 0, frame.top-70, frame.bottom-70);
animation.setRepeatMode(Animation.RESTART);
animation.setRepeatCount(Animation.INFINITE);
animation.setDuration(2000);
view.startAnimation(animation);
//清除内存
myApplication.setView(null);
//=====为矩形扫描区域四个角加上边框 根据情况可以去掉该段代码,像微信扫码了
paint.setColor(Color.GREEN); // 定义四个角画笔的颜色(本身整个扫描界面都为此颜色,通过设置四个角距离而被覆盖,进而形成四个角)
//左上角
canvas.drawRect(frame.left, frame.top, frame.left + frame.width() * mLineRate, frame.top + mLineDepth, paint);
canvas.drawRect(frame.left, frame.top, frame.left + mLineDepth, frame.top + frame.height() * mLineRate, paint);
//右上角
canvas.drawRect(frame.right - frame.width() * mLineRate, frame.top, frame.right, frame.top + mLineDepth, paint);
canvas.drawRect(frame.right - mLineDepth, frame.top, frame.right, frame.top + frame.height() * mLineRate, paint);
//左下角
canvas.drawRect(frame.left, frame.bottom - mLineDepth, frame.left + frame.width() * mLineRate, frame.bottom, paint);
canvas.drawRect(frame.left, frame.bottom - frame.height() * mLineRate, frame.left + mLineDepth, frame.bottom, paint);
//右下角
canvas.drawRect(frame.right - frame.width() * mLineRate, frame.bottom - mLineDepth, frame.right, frame.bottom, paint);
canvas.drawRect(frame.right - mLineDepth, frame.bottom - frame.height() * mLineRate, frame.right, frame.bottom, paint);
//============================自己加入动画↑=========================
// 灰色遮罩层 可以去掉
paint.setColor(resultBitmap != null ? resultColor : maskColor);
canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
/*
//===========PS:以下方法,不断执行onDraw方法绘制扫描线等样式进而产生自带的扫描线和闪光点,
// 若是扫描到了,就会把结果图绘制在矩形框上,根据情况选择是否注释以下代码或部分动画代码
if (resultBitmap != null) {
//扫描到后在矩形上绘制不透明的图
// Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(CURRENT_POINT_OPACITY);
canvas.drawBitmap(resultBitmap, null, frame, paint);
} else {
//自带的红色扫描线
// Draw a red "laser scanner" line through the middle to show decoding is active
paint.setColor(laserColor);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
final int middle = frame.height() / 2 + frame.top;
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
final float scaleX = frame.width() / (float) previewFrame.width();
final float scaleY = frame.height() / (float) previewFrame.height();
final int frameLeft = frame.left;
final int frameTop = frame.top;
// draw the last possible result points
if (!lastPossibleResultPoints.isEmpty()) {
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
paint.setColor(resultPointColor);
float radius = POINT_SIZE / 2.0f;
for (final ResultPoint point : lastPossibleResultPoints) {
canvas.drawCircle(
frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
radius, paint
);
}
lastPossibleResultPoints.clear();
}
// draw current possible result points
if (!possibleResultPoints.isEmpty()) {
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
for (final ResultPoint point : possibleResultPoints) {
canvas.drawCircle(
frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
POINT_SIZE, paint
);
}
// swap and clear buffers
final List temp = possibleResultPoints;
possibleResultPoints = lastPossibleResultPoints;
lastPossibleResultPoints = temp;
possibleResultPoints.clear();
}
//不断调用执行绘制该活动界面进出现自动的动画
// Request another update at the animation interval, but only repaint the laser line,
// not the entire viewfinder mask.
postInvalidateDelayed(ANIMATION_DELAY,
frame.left - POINT_SIZE,
frame.top - POINT_SIZE,
frame.right + POINT_SIZE,
frame.bottom + POINT_SIZE);
}
*/
}
}
Step 5 :准备3个布局:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始"
android:id="@+id/button"/>
</LinearLayout>
content_scan.xml 其实这个布局,BarcodeView和ScanWidget相当于不在布局里,所以该布局像只有View控件,进而达到扫描条运动
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--
layout_width、layout_height:启动扫描界面的布局参数
zxing_framing_rect_width、zxing_framing_rect_height:
在扫描界面中,只能扫描二维码的宽高,去掉后会有默认的宽高
-->
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zxing_barcode_surface" />
<!--注意自定义的扫描活动路径-->
<com.gx.qr.ScanWidget
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zxing_viewfinder_view"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
<!--@drawable/bmt 扫描条图标 请自行找素材-->
<View
android:id="@+id/scan_the"
android:background="@drawable/bmt"
android:layout_width="wrap_content"
android:layout_height="70px" />
</merge>
activity_scan.xml 其实这个布局,DecoratedBarcodeView相当于不在布局里,所以该布局像只有ImageButton控件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--装扫描界面的控件
@layout/content_scan:为嵌入content_scan.xml的布局
-->
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/dbv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
app:zxing_scanner_layout="@layout/content_scan">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
<!--闪光灯图片 自行找图片样式
@drawable/ic_flashlight_close 关闭时的图片
-->
<ImageButton
android:id="@+id/ib_flashlight_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:background="@drawable/ic_flashlight_close"/>
</RelativeLayout>
Step 6 :ScanActivity
public class ScanActivity extends AppCompatActivity {
private CaptureManager capture;
private ImageButton ibFlashlight;
private DecoratedBarcodeView barcodeScannerView;
private boolean bTorch = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//==设置布局、获取控件
setContentView(R.layout.activity_scan);
barcodeScannerView = findViewById(R.id.dbv);
ibFlashlight= findViewById(R.id.ib_flashlight_close);
//==保存扫描条到Application里
View view = findViewById(R.id.scan_the);
MyApplication myApplication= (MyApplication) getApplication();
myApplication.setView(view);
//==监听: 根据barcodeScannerView设置闪光灯ibFlashlight状态
barcodeScannerView.setTorchListener(new DecoratedBarcodeView.TorchListener() {
@Override
public void onTorchOn() {//开灯
//R.drawable.ic_flashlight_open) 开灯显示的图片 自行找图片样式
ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_open));
bTorch = true;
}
@Override
public void onTorchOff() {//关灯
//R.drawable.ic_flashlight_close) 关灯显示的图片 自行找图片样式
ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_close));
bTorch = false;
}
});
//==开或关灯
ibFlashlight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(bTorch){
barcodeScannerView.setTorchOff();
} else {
barcodeScannerView.setTorchOn();
}
}
});
//==初始化活动
capture = new CaptureManager(this, barcodeScannerView);
capture.initializeFromIntent(getIntent(), savedInstanceState);
capture.decode();
}
@Override
protected void onResume() {
super.onResume();
capture.onResume();
}
@Override
protected void onPause() {
super.onPause();
capture.onPause();
barcodeScannerView.setTorchOff();
}
@Override
protected void onDestroy() {
super.onDestroy();
capture.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
capture.onSaveInstanceState(outState);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
}
Step 7 :MainActivity
public class MainActivity extends AppCompatActivity {
private Button button;
// Step 1 : 初始化 获取控件 设置监听
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);//点击跳转到扫码活动
//控件监听
listenerView();
}
private void listenerView() {
// Step 2 :跳转到扫描活动
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//=======设置扫描活动 可根据需求设置以下内容
IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);
//启动自定义的扫描活动,不设置则启动默认的活动
intentIntegrator.setCaptureActivity(ScanActivity.class);
//启动扫描
intentIntegrator.initiateScan();
}
});
}
// Step 3 :处理扫码后返回的结果
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);
if(result!=null){
//==是否扫到内容
if (result.getContents()!=null){
Toast.makeText(this,"扫描结果:"+result.getContents(),Toast.LENGTH_LONG).show();
}else{
Toast.makeText(this,"取消扫码",Toast.LENGTH_LONG).show();
}
}else{
super.onActivityResult(requestCode, resultCode, data);
}
}
//=========PS:Android6.0以后的版本还需动态申请权限,若需动态申请,请在MainActivity中添加申请权限(调用此方法)再做打开相机扫描
//暂时未发现需要动态申请(本人手机安卓10),可能不同手机厂商做法
private void requsetPermission(){
if (Build.VERSION.SDK_INT>22){
if (ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
//先判断有没有权限 ,没有就在这里进行权限的申请
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{android.Manifest.permission.CAMERA},1);
}else {
}
}else {
}
}
//============PS:申请权限后的方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
//已获取权限,写需要做的代码
}else {
//拒绝摄像头权限,可以提示用户给权限
Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
OK!打完收工