敞开成长之旅!这是我参加「日新方案 2 月更文挑战」的第 27 天,点击检查活动概况

扫码功能都用过吧,打开扫码功能后都会有类似封面图的作用。其实就是一个自定义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,同学们能够根据自己想要的作用自己搭配。

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),两者运用也是十分简略。别的需要留意动画的运用和开释,防止导致不必要的内存泄漏。

好了,以上就是本篇所有内容,期望对大家有所协助!

敞开成长之旅!这是我参加「日新方案 2 月更文挑战」的第 27 天,点击检查活动概况