ZXing自定义View:打造独特的扫码框与扫描线
ZXing(Zebra Crossing)作为一款广泛使用的条形码扫描库,提供了基础的扫码功能。但其默认的扫码界面可能无法满足所有应用的设计需求。本文将详细介绍如何通过自定义View来打造独特的扫码框与扫描线,提升应用的用户体验。
扫码界面核心组件解析
ZXing的扫码界面主要由ViewfinderView和CameraManager两个核心类控制。ViewfinderView负责绘制扫码框、扫描线及结果点,而CameraManager则管理相机参数和扫码区域的计算。
ViewfinderView类
ViewfinderView.java是ZXing中负责绘制扫码界面的核心视图类。它继承自Android的View类,通过重写onDraw方法实现扫码框、扫描线和结果点的绘制。
CameraManager类
CameraManager.java负责管理相机的配置和扫码区域的计算。它提供了获取扫码框矩形(framingRect)和预览区域矩形(framingRectInPreview)的方法,这些矩形信息将用于ViewfinderView的绘制。
自定义扫码框
ZXing默认的扫码框是一个简单的矩形。通过修改CameraManager和ViewfinderView,我们可以实现各种形状和样式的扫码框。
修改扫码框大小和位置
在CameraManager中,getFramingRect方法用于计算默认的扫码框大小。默认情况下,扫码框的宽度和高度为屏幕分辨率的5/8,且限制在[240, 1200]和[240, 675]之间。
// CameraManager.java 第224-225行
int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
要自定义扫码框大小,可以修改MIN_FRAME_WIDTH、MAX_FRAME_WIDTH、MIN_FRAME_HEIGHT和MAX_FRAME_HEIGHT常量,或者直接指定固定的宽度和高度:
// 自定义扫码框大小为屏幕宽度的3/4,高度为屏幕高度的1/3
int width = screenResolution.x * 3 / 4;
int height = screenResolution.y * 1 / 3;
绘制自定义形状的扫码框
默认的扫码框是矩形,我们可以通过修改ViewfinderView的onDraw方法来绘制其他形状,如圆角矩形、圆形或其他自定义形状。
绘制圆角矩形扫码框
在ViewfinderView的onDraw方法中,默认通过drawRect绘制矩形扫码框的外部遮罩。我们可以修改这部分代码,使用drawRoundRect绘制圆角矩形:
// ViewfinderView.java 第94-99行
// 绘制外部遮罩,留出圆角矩形的扫码框
paint.setColor(resultBitmap != null ? resultColor : maskColor);
RectF rectF = new RectF(0, 0, width, frame.top);
canvas.drawRoundRect(rectF, 20, 20, paint);
rectF.set(0, frame.top, frame.left, frame.bottom + 1);
canvas.drawRoundRect(rectF, 20, 20, paint);
rectF.set(frame.right + 1, frame.top, width, frame.bottom + 1);
canvas.drawRoundRect(rectF, 20, 20, paint);
rectF.set(0, frame.bottom + 1, width, height);
canvas.drawRoundRect(rectF, 20, 20, paint);
添加边框和角落标记
为了增强扫码框的视觉效果,我们可以添加边框和角落标记。例如,在扫码框的四个角落绘制标志性的直角或圆角:
// 在ViewfinderView的onDraw方法中添加角落标记绘制代码
paint.setColor(laserColor);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
// 左上角
canvas.drawLine(frame.left, frame.top, frame.left + 50, frame.top, paint);
canvas.drawLine(frame.left, frame.top, frame.left, frame.top + 50, paint);
// 右上角
canvas.drawLine(frame.right - 50, frame.top, frame.right, frame.top, paint);
canvas.drawLine(frame.right, frame.top, frame.right, frame.top + 50, paint);
// 左下角
canvas.drawLine(frame.left, frame.bottom, frame.left + 50, frame.bottom, paint);
canvas.drawLine(frame.left, frame.bottom - 50, frame.left, frame.bottom, paint);
// 右下角
canvas.drawLine(frame.right - 50, frame.bottom, frame.right, frame.bottom, paint);
canvas.drawLine(frame.right, frame.bottom - 50, frame.right, frame.bottom, paint);
自定义扫描线
ZXing默认的扫描线是一条红色的水平线,通过改变透明度实现闪烁效果。我们可以自定义扫描线的颜色、形状、动画方式等。
修改扫描线颜色和粗细
扫描线的颜色由laserColor定义,在ViewfinderView的构造方法中初始化:
// ViewfinderView.java 第69行
laserColor = resources.getColor(R.color.viewfinder_laser);
我们可以修改res/values/colors.xml中的viewfinder_laser值来改变扫描线颜色,或者直接在代码中指定颜色:
laserColor = Color.GREEN; // 绿色扫描线
扫描线的粗细可以通过修改绘制矩形的高度来实现:
// ViewfinderView.java 第112行
// 原代码:canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
// 修改为更粗的扫描线
canvas.drawRect(frame.left + 2, middle - 3, frame.right - 1, middle + 3, paint); // 高度为6像素
自定义扫描线动画
默认的扫描线动画是通过改变透明度实现的闪烁效果。我们可以修改动画逻辑,实现扫描线上下移动的效果。
添加扫描线位置变量
在ViewfinderView中添加一个变量来跟踪扫描线的位置:
private int scanLineTop; // 扫描线顶部位置
private int scanLineSpeed = 5; // 扫描线移动速度
修改onDraw方法实现扫描线移动
在onDraw方法中,更新扫描线的位置并绘制:
// ViewfinderView.java 第107-112行
// 移除原扫描线绘制代码,添加以下代码
scanLineTop += scanLineSpeed;
if (scanLineTop > frame.bottom) {
scanLineTop = frame.top;
}
canvas.drawRect(frame.left + 2, scanLineTop, frame.right - 1, scanLineTop + 5, paint);
更新重绘逻辑
修改postInvalidateDelayed的参数,确保扫描线动画的流畅性:
// ViewfinderView.java 第151-155行
postInvalidateDelayed(ANIMATION_DELAY,
frame.left - POINT_SIZE,
frame.top - POINT_SIZE,
frame.right + POINT_SIZE,
frame.bottom + POINT_SIZE);
使用图片作为扫描线
除了简单的矩形扫描线,我们还可以使用图片作为扫描线,实现更丰富的视觉效果。
添加扫描线图片资源
将扫描线图片添加到项目的res/drawable目录下,例如scan_line.png。
绘制图片扫描线
在ViewfinderView的onDraw方法中,使用drawBitmap绘制图片扫描线:
// 加载扫描线图片
Bitmap scanLineBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.scan_line);
// 绘制扫描线图片
canvas.drawBitmap(scanLineBitmap, frame.left, scanLineTop, paint);
完整自定义View示例
下面是一个完整的ViewfinderView自定义示例,包含圆角矩形扫码框、绿色移动扫描线和角落标记:
public final class CustomViewfinderView extends View {
private static final long ANIMATION_DELAY = 10L;
private static final int POINT_SIZE = 6;
private CameraManager cameraManager;
private final Paint paint;
private Bitmap resultBitmap;
private final int maskColor;
private final int resultColor;
private final int laserColor;
private final int resultPointColor;
private int scanLineTop;
private int scanLineSpeed = 3;
private List<ResultPoint> possibleResultPoints;
private List<ResultPoint> lastPossibleResultPoints;
public CustomViewfinderView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Resources resources = getResources();
maskColor = resources.getColor(R.color.viewfinder_mask);
resultColor = resources.getColor(R.color.result_view);
laserColor = Color.GREEN; // 绿色扫描线
resultPointColor = resources.getColor(R.color.possible_result_points);
possibleResultPoints = new ArrayList<>(5);
lastPossibleResultPoints = null;
scanLineTop = 0;
}
public void setCameraManager(CameraManager cameraManager) {
this.cameraManager = cameraManager;
}
@Override
public void onDraw(Canvas canvas) {
if (cameraManager == null) {
return;
}
Rect frame = cameraManager.getFramingRect();
Rect previewFrame = cameraManager.getFramingRectInPreview();
if (frame == null || previewFrame == null) {
return;
}
int width = canvas.getWidth();
int height = canvas.getHeight();
// 绘制外部遮罩,留出圆角矩形扫码框
paint.setColor(resultBitmap != null ? resultColor : maskColor);
canvas.drawRoundRect(new RectF(0, 0, width, frame.top), 20, 20, paint);
canvas.drawRoundRect(new RectF(0, frame.top, frame.left, frame.bottom + 1), 20, 20, paint);
canvas.drawRoundRect(new RectF(frame.right + 1, frame.top, width, frame.bottom + 1), 20, 20, paint);
canvas.drawRoundRect(new RectF(0, frame.bottom + 1, width, height), 20, 20, paint);
if (resultBitmap != null) {
// 绘制解码结果
paint.setAlpha(100);
canvas.drawBitmap(resultBitmap, null, frame, paint);
} else {
// 绘制角落标记
paint.setColor(laserColor);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
// 左上角
canvas.drawLine(frame.left, frame.top, frame.left + 50, frame.top, paint);
canvas.drawLine(frame.left, frame.top, frame.left, frame.top + 50, paint);
// 右上角
canvas.drawLine(frame.right - 50, frame.top, frame.right, frame.top, paint);
canvas.drawLine(frame.right, frame.top, frame.right, frame.top + 50, paint);
// 左下角
canvas.drawLine(frame.left, frame.bottom, frame.left + 50, frame.bottom, paint);
canvas.drawLine(frame.left, frame.bottom - 50, frame.left, frame.bottom, paint);
// 右下角
canvas.drawLine(frame.right - 50, frame.bottom, frame.right, frame.bottom, paint);
canvas.drawLine(frame.right, frame.bottom - 50, frame.right, frame.bottom, paint);
// 绘制移动扫描线
paint.setColor(laserColor);
paint.setStyle(Paint.Style.FILL);
if (scanLineTop == 0) {
scanLineTop = frame.top;
}
scanLineTop += scanLineSpeed;
if (scanLineTop > frame.bottom) {
scanLineTop = frame.top;
}
canvas.drawRect(frame.left + 2, scanLineTop, frame.right - 2, scanLineTop + 5, paint);
// 绘制结果点
// ...(省略结果点绘制代码)
// 重绘
postInvalidateDelayed(ANIMATION_DELAY,
frame.left - POINT_SIZE,
frame.top - POINT_SIZE,
frame.right + POINT_SIZE,
frame.bottom + POINT_SIZE);
}
}
// 其他方法(drawViewfinder, drawResultBitmap, addPossibleResultPoint)与原ViewfinderView相同
}
扫码界面效果展示
通过以上自定义,我们可以实现各种独特的扫码界面效果。下面是一些常见的自定义效果示例:
圆角矩形扫码框
带角落标记的扫码框
自定义图片扫描线
总结
通过自定义ViewfinderView和CameraManager,我们可以轻松实现各种独特的扫码框和扫描线效果,提升应用的用户体验。关键步骤包括修改扫码框的大小和形状、自定义扫描线的样式和动画,以及添加额外的视觉元素如角落标记。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考






