Android扫码 有仿微信版
第三方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>
第三种:改进版,仿微信扫描条(本来是动图的,扫描线会动)
扫描条是图片,利用动画实现扫描条活动,第二种的扫描条是绘制的线,调快移动距离的话会感觉一卡一卡的,效果不好。
国际惯例,先上图:因虚拟机录制,看着扫描条一卡一卡的,实际手机调试不是
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 :自定义 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!打完收工
更多相关文章
- 后台动态添加布局文件、控件与动态设置属性2
- androidUI布局仿猫扑界面
- Android如何获得系统(system)权限 !!!
- 一个关于android中ListView的子控件中按钮事件的简单方法
- 【摘录】 Android中如何修改系统时间(应用程序获得系统权限)
- Android Layout 布局 && Android自带样式(theme)&& CheckBox样例 &
- 使用Android常用控件与布局实现美观的登录页面
- Android Studio3.1.2在项目的build.gradle中添加百分比布局库的
- andorid 将布局文件(layout)转换为图片(Bitmap)简单使用详解