注册

Android:实现一个自定义View扫描框

扫码功能都用过吧,打开扫码功能后都会有类似封面图的效果。其实就是一个自定义View的遮罩,话不多说,今天这篇我们就来讲解如何实现一个扫面框动效。


首先,我们先分析下动效的组成,有利于待会拆分实现:



  1. 四周类似角标的白线
  2. 角标框住的浅白色背景
  3. 一条由上而下由快到慢移动的扫描线

一经分析,其实非常简单,整体效果就是由这几个简单的元素组成。接下来我们就创建一个ScanView继承自View来实现这个动效。(由于代码古老,这里使用Java)


public final class ScanView extends View {

private Paint paint, scanLinePaint,reactPaint;//三种画笔
private Rect frame;//整个区域

public ScanView(Context context) {
this(context, null);
}

public ScanView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public ScanView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint()
}

private void initPaint() {
/*遮罩画笔*/
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setAlpha(CURRENT_POINT_OPACITY);
paint.setStyle(Paint.Style.FILL);

/*边框线画笔*/
reactPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
reactPaint.setColor(reactColor);
reactPaint.setStyle(Paint.Style.FILL);

/*扫描线画笔*/
scanLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scanLinePaint.setStyle(Paint.Style.FILL);
scanLinePaint.setDither(true);
scanLinePaint.setColor(scanLineColor);
}
}

三种画笔初始化完成接下来就是使用画笔在画布上绘制效果图了,重写onDraw()方法


public void onDraw(Canvas canvas) {
//绘制取景边框
drawFrameBounds(canvas, frame);
//绘制遮罩
drawMaskView(canvas, frame);
//绘制扫描线
drawScanLight(canvas, frame);
}

再来分析,边框的四个角其实拆开来看,就是两条线组成,或者说是两个填充的矩形框垂直相交组成,那么四个角就可以按照这个思路完成,遮罩其实就是一个矩形框。


//绘制四个角,注意是外线而不是内线
private void drawFrameBounds(Canvas canvas, Rect frame) {
// 左上角
canvas.drawRect(frame.left - corWidth, frame.top, frame.left, frame.top + corLength, reactPaint);
canvas.drawRect(frame.left - corWidth, frame.top - corWidth, frame.left + corLength, frame.top, reactPaint);
// 右上角
canvas.drawRect(frame.right, frame.top, frame.right + corWidth,frame.top + corLength, reactPaint);
canvas.drawRect(frame.right - corLength, frame.top - corWidth, frame.right + corWidth, frame.top, reactPaint);
// 左下角
canvas.drawRect(frame.left - corWidth, frame.bottom - corLength,frame.left, frame.bottom, reactPaint);
canvas.drawRect(frame.left - corWidth, frame.bottom, frame.left + corLength, frame.bottom + corWidth, reactPaint);
// 右下角
canvas.drawRect(frame.right, frame.bottom - corLength, frame.right + corWidth, frame.bottom, reactPaint);
canvas.drawRect(frame.right - corLength, frame.bottom, frame.right + corWidth, frame.bottom + corWidth, reactPaint);
}

//绘制遮罩
private void drawMaskView(Canvas canvas, Rect frame) {
canvas.drawRect(frame.left, frame.top, frame.right, frame.bottom, paint);
}

到此,我们还剩最后一个扫描线的动画效果,这条线其实就是一张图片,首先需要将图片以Bitmap形式绘制在扫描区域内,然后通过ValueAnimator来控制图片Y坐标点,这样就能达到图片上下移动的效果,至于由快到慢的效果是添加了插值器,这里使用内置的DecelerateInterpolator,同学们可以根据自己想要的效果自己搭配。


scan_light.png


if (valueAnimator == null) {
valueAnimator = ValueAnimator.ofInt(frame.top, frame.bottom-10);//图片Y坐标取值范围
valueAnimator.setDuration(3000);//单次动画时间3秒
valueAnimator.setInterpolator(new DecelerateInterpolator());//由快到慢插值器
valueAnimator.setRepeatMode(ValueAnimator.RESTART);//重复动画
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);//无限次数
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scanLineTop = (int) animation.getAnimatedValue();//当前时刻获取的Y值
invalidate();//刷新视图
}
});
valueAnimator.start();
}

到此就可以实现封面的效果,甚至可以添加别的酷炫效果,只要你敢想敢做。


总结


其实一些动效看似很复杂,但通过认真分析,我们可以将其拆分成多个简单的小块,将每个小块实现后再逐个拼装,最后达到完整的效果。本节主要是通过自定义View实现,用到绘制矩形框(drawRect),属性动画(ValueAnimator),两者使用也是非常简单。另外需要注意动画的使用和释放,避免导致不必要的内存泄漏。


好了,以上便是本篇所有内容,希望对大家有所帮助!


作者:似曾相识2022
链接:https://juejin.cn/post/7205585832232403005
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册