实现效果如下图:
实现原理:
通过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();
}