自定义drawable实现扫描效果

实现效果如下图:

实现原理:

通过LayerDrawable将不同的图形组合在一起,最终形成完整的图形。

  • 圆环drawable:

定义好画笔颜色以及透明度,通过RectF确定圆弧的位置区域,在canvas上画出圆环即可:

private void initPaint(){
        this.mPaint=new Paint();
        mPaint.setColor(mColor);
        mPaint.setAlpha(mAlpha);
        mPaint.setStrokeWidth(mStrokeWidth); // 设置圆环的宽度
        mPaint.setAntiAlias(true); // 消除锯齿
        mPaint.setStyle(Paint.Style.STROKE); // 设置空心
        // 用于定义的圆弧的形状和大小的界限
        mRectF = new RectF(mWidth/2+mStrokeWidth/2-mRadius, mWidth/2+mStrokeWidth/2-mRadius, mWidth/2-mStrokeWidth/2+mRadius, mWidth/2-mStrokeWidth/2+mRadius);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        canvas.drawArc(mRectF,0, 360, false, mPaint); //画圆弧
    }
  • 圆心图片Drawable:

获取到图片,并将其画在最里面一个的圆环的中心:

public CenterDrawable(Context context, int resId, float radius, float width, float height) {
        this.mContext = context;
        this.mHeight = dp2px(context, height);
        this.mWidth = dp2px(context, width);
        this.mRadius = dp2px(context, radius);
        mBitmap = BitmapFactory.decodeResource(context.getResources(), resId);
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true); // 消除锯齿

        mSrcRect = new Rect(0, 0, mBitmap.getWidth(),mBitmap.getHeight());
        mDestRectF = new RectF(mRadius - mWidth / 2, mRadius - mHeight / 2, mRadius + mWidth / 2, mRadius + mHeight / 2);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        canvas.drawBitmap(mBitmap, mSrcRect, mDestRectF, mPaint);
    }
  • 扇形扫描drawable:

最上次是扇形扫描的drawable,首先定义画笔:

思路是从270°到360°画出一个圆弧,并通过SweepGradient为其添加渐变效果

private void initPaint() {
        mPaint = new Paint();
        mPaint.setStrokeWidth(mStrokeWidth); // 设置圆环的宽度
        colors = new int[]{Color.parseColor("#00000000"), Color.parseColor("#1a000000")};
        float startPosition = defaultmStartangle / 360.0f;
        float positions[] = {startPosition, 1.0f};
        SweepGradient lg = new SweepGradient(mRadius, mRadius, colors, positions);
        mPaint.setShader(lg);
        mPaint.setAntiAlias(true); // 消除锯齿
        mPaint.setStyle(Paint.Style.STROKE); // 设置空心
        // 用于定义的圆弧的形状和大小的界限
        mRectF = new RectF(mStrokeWidth / 2, mStrokeWidth / 2, mRadius * 2 - mStrokeWidth / 2, mRadius * 2 - mStrokeWidth / 2);
        //初始化矩阵
        matrix = new Matrix();
    }

画出圆弧:

 @Override
    public void draw(@NonNull Canvas canvas) {
        canvas.concat(matrix);
        canvas.drawArc(mRectF, defaultmStartangle, sweep, false, mPaint); //画圆弧
    }

添加动画:

通过属性动画+Matrix的方式让扇形动起来,产生动画效果;

属性动画采用匀速插值器,匀速得到0-360之间的值,然后将得到的角度通过matrix.setRotate(mStartAngle, mRadius, mRadius);设置到矩阵,invalidateSelf()让其生效;

private void initAnimator() {
        mAnimator = new ValueAnimator();
        mAnimator = ValueAnimator.ofInt(0, 360);
        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mAnimator.setDuration(1500);
        mAnimator.setRepeatMode(ValueAnimator.RESTART);
        mAnimator.setInterpolator(new LinearInterpolator());
        // 设置动画的回调
        mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mStartAngle = (int) animation.getAnimatedValue();
                //重置矩阵
                matrix.reset();
                //设置旋转角度
                matrix.setRotate(mStartAngle, mRadius, mRadius);
                invalidateSelf();
            }
        };
    }

最后实现接口,接口规定开始、停止动画等,便于规范代码和外部调用:

接口代码:

public interface Animatable {
    /**
     * Starts the drawable's animation.
     */
    void start();

    /**
     * Stops the drawable's animation.
     */
    void stop();

    /**
     * Indicates whether the animation is running.
     *
     * @return True if the animation is running, false otherwise.
     */
    boolean isRunning();
}

实现接口:

@Override
    public void start() {
        mAnimator.addUpdateListener(mAnimatorUpdateListener);
        mAnimator.start();
    }

    @Override
    public void stop() {
        mAnimator.removeAllUpdateListeners();
        mAnimator.end();
    }

    @Override
    public boolean isRunning() {
        return mAnimator.isRunning();
    }

通过LayerDrawable将以上drawable集成在一起:

首先循环添加四个圆环:

int defaultAlpha = 225;
        int totalCircle = 4;
        Drawable[] layers = new Drawable[totalCircle + 2];
        //4个圆环
        float[] strokwidths = {0.208f * radius, 0.192f * radius, 0.169f * radius, 0.142f * radius};
        float[] radiuses = {radius, radius - strokwidths[0], radius - strokwidths[0] - strokwidths[1], radius - strokwidths[0] - strokwidths[1] - strokwidths[2]};
        int[] alpha = {(int) (defaultAlpha * 0.15), (int) (defaultAlpha * 0.35), (int) (defaultAlpha * 0.45), (int) (defaultAlpha * 0.65)};

        for (int i = 0; i < totalCircle; i++) {
            ScanLevelDrawable drawable = new ScanLevelDrawable(context, color, radius * 2, strokwidths[i], radiuses[i], alpha[i]);
            layers[i] = drawable;
        }

然后加上中间的图片:

注意图片放到最里面圆环的中心;

//中心的图片
        //最中心圆的图片
        float lastRadius = radiuses[totalCircle - 1] - strokwidths[totalCircle - 1];
        //width是最中心圆的圆内正方形的边长*2/3
        float centerWidth = (float) (lastRadius * Math.sqrt(2) / 2);
        CenterDrawable drawableCenter = new CenterDrawable(context, centerResId, radius, centerWidth, centerWidth);
        layers[totalCircle] = drawableCenter;

最后加上扇形扫描区域得到最终的layerDrawable:

//扇形渐变遮罩
        layers[totalCircle + 1] = mGradientDrawable = new GradientDrawable(context, radius, radius - (radiuses[totalCircle - 1] - strokwidths[totalCircle - 1]));
        return new LayerDrawable(layers);

最后暴露方法给外部调用,整个效果就完成了

public static ScanDrawableBuider getInstance() {
        if (instance == null) {
            instance = new ScanDrawableBuider();
        }
        return instance;
    }

    public void starAnimation() {
        if (mGradientDrawable == null) {
            return;
        }
        mGradientDrawable.start();
    }

    public void stopAnimation() {
        if (mGradientDrawable == null) {
            return;
        }
        mGradientDrawable.stop();
    }

    public boolean isRunning() {
        if (mGradientDrawable == null) {
            return false;
        }
        return mGradientDrawable.isRunning();
    }

源码地址

发布了11 篇原创文章 · 获赞 1 · 访问量 3913

猜你喜欢

转载自blog.csdn.net/qin19930929/article/details/82350273