需求:实现扫描二维码验证身份。(服务端这部分会提供一个二维码,扫描二维码会生成一个token,根据token的结果向某个url发送网络连接请求,服务端返回验证结果。我只负责安卓扫码和网络连接请求部分)。
基本扫码功能
1.引入google开源库:
implementation 'com.journeyapps:zxing-android-embedded:3.5.0'
网上大部分使用的是zxing这个开源库,资料比较多,所以我也用了。。哈哈
2.创建菜单栏,里面增加item为扫描二维码。然后创建IntentIntegrator对象,initiateScan开始扫描。其中IntentIntegrator对象有一些set函数,这里用到的一个是CapacityActivity,默认不set的话是用默认的捕获活动来扫描,这里是自己自定义了一个扫码样式所以显式指定了一下。setPrompt是扫描框下面的一行字。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.qr_code){
IntentIntegrator intentIntegrator = new IntentIntegrator(NewMainActivity.this);
intentIntegrator.setCaptureActivity(CustomCaptureActivity.class);
intentIntegrator.setPrompt("将二维码放入框内,即可自动扫描");
intentIntegrator.initiateScan();
}
3.重写onActivityResult方法接收扫描结果。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);
if(result==null) {
super.onActivityResult(requestCode, resultCode, data);
}
if (result.getContents() == null) {
Toast.makeText(this, "取消扫描", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, result.getContents(), Toast.LENGTH_SHORT).show();
}
}
这几部完成之后,基本的扫码功能就完成了。但是扫码的时候你会发现扫描过程屏幕会横过来,因为其默认扫描界面是横屏,可以在manifest中指定。设置为竖屏。
<activity
android:name=".qrcode.CustomCaptureActivity"
android:theme="@style/AppTheme"
android:screenOrientation="portrait"
tools:replace="screenOrientation" />
基本功能完成了,但是界面比较丑。还可以自定义扫码界面的样式。
自定义扫码界面
1.源码中可以看到CapacityActivity这个活动,之前做扫码功能也主要由这个活动完成,可以直接copy这个活动,创建自己的扫码活动,CustomCapacityActivity,在manifest中进行活动的声明。然后就可以用setCapacityActivity来指定扫码活动了。完美替换。如上。如果能扫码成功,表示替换成功。
2.修改扫码布局
新创建的扫码活动使用的还是源码的布局,我们也不能修改,所以copy一份出来(zxing_capacity),进行修改(activity_zxing_layout)。然后修改customCapacityActivity的布局。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zxing_layout);
}
3.自定义样式
都copy出来了就可以自定义样式了。研究DecoratedBarcodeView会发现使用的布局是zxing_barcode_scanner。同样把这个布局copy出来。custom_barcode_scanner.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_barcode_surface"/>
<com.example.qrcode.CustomViewfinderView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_viewfinder_view"/>
<TextView android:id="@+id/zxing_status_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="160dp"
android:background="@color/zxing_transparent"
android:text="@string/zxing_msg_default_status"
android:textColor="@color/zxing_status_text"/>
</FrameLayout>
让DecoratedBarcodeView加载新定义布局,而不是默认的布局。
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_framing_rect_width="250dp"
app:zxing_framing_rect_height="250dp"
app:zxing_preview_scaling_strategy="centerCrop"
app:zxing_scanner_layout="@layout/custom_barcode_scanner"
app:zxing_use_texture_view="true" />
</LinearLayout>
开始自定义扫描视图,继承ViewfinderView重写onDraw方法。
public class CustomViewfinderView extends ViewfinderView {
/**
* 重绘时间间隔
*/
public static final long CUSTOME_ANIMATION_DELAY = 16;
/* ****************************************** 边角线相关属性 ************************************************/
/**
* "边角线长度/扫描边框长度"的占比 (比例越大,线越长)
*/
public float mLineRate = 0.1F;
/**
* 边角线厚度 (建议使用dp)
*/
public float mLineDepth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
/**
* 边角线颜色
*/
public int mLineColor = Color.RED;
/* ******************************************* 扫描线相关属性 ************************************************/
/**
* 扫描线起始位置
*/
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, 3, getResources().getDisplayMetrics());
/**
* 线性梯度
*/
public LinearGradient mLinearGradient;
/**
* 线性梯度位置
*/
public float[] mPositions = new float[]{0f, 0.5f, 1f};
/**
* 线性梯度各个位置对应的颜色值
*/
public int[] mScanLineColor = new int[]{0x00FFFFFF, Color.RED, 0x00FFFFFF};
public CustomViewfinderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void refreshSizes() {
if(cameraPreview == null) {
return;
}
Rect framingRect = cameraPreview.getFramingRect();
Rect previewFramingRect = cameraPreview.getPreviewFramingRect();
if(framingRect != null && previewFramingRect != null) {
this.framingRect = framingRect;
this.previewFramingRect = previewFramingRect;
}
}
@Override
public void onDraw(Canvas canvas) {
refreshSizes();
if (framingRect == null || previewFramingRect == null) {
return;
}
Rect frame = framingRect;
Rect previewFrame = previewFramingRect;
int width = canvas.getWidth();
int height = canvas.getHeight();
//绘制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);
// Draw the exterior (i.e. outside the framing rect) darkened
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);
float scaleX = frame.width() / (float) previewFrame.width();
float scaleY = frame.height() / (float) previewFrame.height();
List<ResultPoint> currentPossible = possibleResultPoints;
List<ResultPoint> currentLast = lastPossibleResultPoints;
int frameLeft = frame.left;
int frameTop = frame.top;
if (currentPossible.isEmpty()) {
lastPossibleResultPoints = null;
} else {
possibleResultPoints = new ArrayList<>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
POINT_SIZE, paint);
}
}
if (currentLast != null) {
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
paint.setColor(resultPointColor);
float radius = POINT_SIZE / 2.0f;
for (ResultPoint point : currentLast) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
radius, paint);
}
}
}
// Request another update at the animation interval, but only repaint the laser line,
// not the entire viewfinder mask.
postInvalidateDelayed(CUSTOME_ANIMATION_DELAY,
frame.left,
frame.top,
frame.right,
frame.bottom);
}
}
更加详细的内容参考这里:https://www.jianshu.com/p/b85812b6f7c1