前言
- 探索
ViewfinderView
如何绘制默认UI - 仿微信定制ZXing UI
默认扫描UI
查看zxing源码,自定义这块是在类 ViewfinderView 中实现。主要是在 onDraw
方法中处理。
接下来先分析ZXing默认的扫描线,然后再其基础上修改为微信的UI。
默认矩形尺寸的计算
默认横屏扫描界面这里暂时不考虑图中的菜单等选项。
抽象为坐标图像为:
这里以横轴演示,左上角为整个手机屏幕的原点,向右延伸为X轴, 向下延伸为Y轴。
在 ViewfinderView -> onDraw
中,先会获取中间frame的尺寸。
Rect frame = cameraManager.getFramingRect();
分析代码发现,ZXing默认的扫描框存在一个范围,最小是240 x 240, 最大的是 1200 x 675, 最大的尺寸是按照 1920 x 1080的 5/8而来的。
private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080
获得尺寸后,就可以在画布上画出frame的尺寸大小,将周边蒙上阴影!
// 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);
具体的思路按照图上四个区域而来!
默认激光线
// 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;
int middle = frame.height() / 2 + frame.top;
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
默认麻点
扫描过程中,会出现麻点,这个是由如下关键代码段处理!
possibleResultPoints = new ArrayList<>(5);
lastPossibleResultPoints = currentPossible;
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setColor(resultPointColor);
synchronized (currentPossible) {
for (ResultPoint point : currentPossible) {
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
frameTop + (int) (point.getY() * scaleY),
POINT_SIZE, paint);
}
}
}
仿微信扫描UI
微信扫描UI,需要修改ViewfinderView 以及添加某些资源文件!先来看下微信扫描UI图。
仿微信扫描框,需要明确以下几个方面:
- 矩形框大小
- 矩形框四角的图形&颜色
- 扫描动画
矩形框大小
横屏时候, ZXing使用了默认的大小 675 x 1200, 而竖屏时候,ZXing使用了默认大小 240 x 240。
关键代码段如下:@CameraManager
public synchronized Rect getFramingRect() {
int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - height) / 2;
... ...
}
private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {
int dim = 5 * resolution / 8; // Target 5/8 of each dimension
if (dim < hardMin) {
return hardMin;
}
if (dim > hardMax) {
return hardMax;
}
return dim;
}
矩形框四角的图形&颜色
颜色很好确定,画图形的选择有几种方式
- 画8条线
- 画8个矩形
扫描动画
上下移动的线条,使用了图片形式。扫描动画原理很简单, onDraw方法被系统不断的调用,在其中控制到上下移动的距离,以及对距离的判断即可!
private void drawScanLight(Canvas canvas, Rect frame) {
if (scanLineTop == 0) {
scanLineTop = frame.top;
}
if (scanLineTop >= frame.bottom - 30) {
scanLineTop = frame.top;
} else {
scanLineTop += SCAN_VELOCITY;// SCAN_VELOCITY可以在属性中设置,默认为5
}
Rect scanRect = new Rect(frame.left, scanLineTop, frame.right, scanLineTop + 30);
canvas.drawBitmap(scanLight, null, scanRect, paint);
}
实现步骤
1.attrs文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="innerrect">
<attr name="inner_width" format="dimension"/>
<attr name="inner_height" format="dimension"/>
<attr name="inner_margintop" format="dimension" />
<attr name="inner_corner_color" format="color" />
<attr name="inner_corner_length" format="dimension" />
<attr name="inner_corner_width" format="dimension" />
<attr name="inner_scan_bitmap" format="reference" />
<attr name="inner_scan_speed" format="integer" />
<attr name="inner_scan_iscircle" format="boolean" />
</declare-styleable>
</resources>
本文并没有使用到所有属性,这些属性在哪里使用,答案是在 工程目录下的layout-> capture.xml
<com.google.zxing.client.android.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:inner_corner_length="30dp" //四角绿颜色矩形的长度
app:inner_corner_width="5dp" //四角绿颜色矩形的宽度
app:inner_scan_bitmap="@drawable/scanline" //需要添加scanline图片到drawable目录中
app:inner_scan_speed="10" // 扫描线移动距离
app:inner_scan_iscircle="true" /> //扫描过程中是否显示麻点
2.ViewfinderView修改
修改了其中很多内容, 涉及的方法包括:
- public ViewfinderView(Context context, AttributeSet attrs)
- private void initInnerRect(Context context, AttributeSet attrs)
- public void onDraw(Canvas canvas)
- private void drawFrameBounds(Canvas canvas, Rect frame)
- private void drawScanLight(Canvas canvas, Rect frame)
关键点都添加了 //add by tan 注释