做程序猿这么久一直没有写博客,是不正常的,故以此为第一篇博客,开始我的博客生涯。

 

前不久,看到一篇博客,关于X6闪充动画的效果,是一个叫什么“瓶子盖子”写的,暂时就叫这个名字吧,具体名字没记清(sorry)。跑了一下他的代码,发现各种卡顿。。。一查他的代码发现,在死循环里面不停地new Message(),不卡有鬼了。。。当然,我并没有对他有不敬的意思,只是想改良一下效果。

 

先上vivo X6闪充真机效果图:

Android 充电View android 充电弹出动画_Android 充电View

充电时会不停地旋转,效果绚丽。

 

下面讲讲实现原理:

该动画实际上是由四条弧线,和中间的带线条的圆形组成。android里面给了画弧线的方法canvas.drawArc()。这个不难,难的是里面的圆圈带线条。其实“瓶子盖子”的那个方案很不错(给他个赞),画72条线,通过画面的旋转,使得各条线达到对应的位置,下面是代码:

/**
 * 绘制内切圆和锯齿,通过canvas的旋转,画出对应的锯齿线
 * @param canvas
 */
private void drawCircle(Canvas canvas) {
    float radius = HEIGHT - (HEIGHT * 0.3f) * 2 - (WIDTH * 0.17f);
    canvas.drawCircle(WIDTH / 2, HEIGHT / 2, radius, mInCrilePaint);
    canvas.save();

    for (int i = 0; i < 72; i++) {
        if (i >= mProgress) {
            mInLine.setColor(Color.parseColor("#555555"));
        } else {
            mInLine.setColor(Color.parseColor("#00ff00"));
        }
        canvas.drawLine(WIDTH / 2, HEIGHT / 3.7f, WIDTH / 2, HEIGHT / 3.7f + HEIGHT * 0.05f , mInLine);
        canvas.rotate(5, getWidth() / 2, getHeight() / 2);
    }
}

 

静态的显示效果出来了,但是还缺少动画效果。“瓶子盖子”的跑动画是通过一个死循环,去跑动画,实现完全可以不用这么做。

 

安卓里面已经提供了Animator来帮助我们实现动画效果。

 

事实上,我们可以通过Animator的setRepartCount(-1)来实现无限循环。我猜想,vivo的攻城狮也大致是这样写的吧。

 

下面贴下代码:

mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setDuration(1500);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setRepeatCount(Animation.INFINITE);
mAnimator.addUpdateListener(this);

 

整体思路:

1.首先创建一个新的类FlashChargeView继承View,实现动画的监听,以监听到动画变化。在该view的构造方法里,创建一个ValueAnimator,初始化该Animator,使其无限循环播放。提供给外部的只有三个方法:开始播放动画、停止播放动画、设置当前电量。具体代码:

/**
 * Created by Jxr33 on 2016/8/23.
 */
public class FlashChargeView extends View implements ValueAnimator.AnimatorUpdateListener {

    /** 细线,粗线,圆,圆内进度,文字的画笔 */
    private Paint mPaintSmall, mPaintBig, mInCrilePaint, mInLine, mTextPaint;
    /** 控件的高宽 */
    private static float WIDTH, HEIGHT;
    /** 动画 */
    private ValueAnimator mAnimator;
    /** 圆弧起始角度 */
    private float startAngle = 0;
    /** 圆弧旋转角度 */
    private float offset = 0;
    /** 当前电量 */
    private int mCurrPower = 70;
    /** 当前电量的进度 */
    private float mProgress;


    public FlashChargeView(Context context) {
        super(context);
        init();
    }

    public FlashChargeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public FlashChargeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaintSmall = new Paint();
        mPaintSmall.setAntiAlias(true);
        mPaintSmall.setStrokeWidth(5);
        mPaintSmall.setStyle(Paint.Style.STROKE);
        mPaintSmall.setColor(Color.parseColor("#12ADFF"));

        mPaintBig = new Paint();
        mPaintBig.setAntiAlias(true);
        mPaintBig.setStrokeWidth(20);
        mPaintBig.setStyle(Paint.Style.STROKE);
        mPaintBig.setColor(Color.parseColor("#12ADFF"));

        mInCrilePaint = new Paint();
        mInCrilePaint.setAntiAlias(true);
        mInCrilePaint.setStrokeWidth(.5f);
        mInCrilePaint.setStyle(Paint.Style.STROKE);
        mInCrilePaint.setColor(Color.parseColor("#eeeeee"));

        mInLine = new Paint();
        mInLine.setAntiAlias(true);
        mInLine.setStrokeWidth(3);
        mInLine.setColor(Color.parseColor("#00ff00"));

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStrokeWidth(3);
        mTextPaint.setTextSize(80);
        mTextPaint.setColor(Color.parseColor("#ffffff"));

        mAnimator = ValueAnimator.ofFloat(0, 1);
        mAnimator.setDuration(1500);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.setRepeatCount(Animation.INFINITE);
        mAnimator.addUpdateListener(this);

        this.mProgress = mCurrPower * 72.0f / 100;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        WIDTH = w;
        HEIGHT = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawOutArc1(canvas);
        drawOutArc2(canvas);
        drawOutArc3(canvas);
        drawOutArc4(canvas);
        drawCircle(canvas);
        drawCircleIn(canvas);
        drawText(canvas);
    }

    /**
     * 绘制文字
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        float textSize = mTextPaint.measureText(mCurrPower + "%");
        float x = WIDTH / 2 - textSize / 2;
        float y = HEIGHT / 2 + textSize / 5;
        canvas.drawText(mCurrPower + "%", x, y, mTextPaint);
    }

    /**
     * 绘制最里面的圆
     * @param canvas
     */
    private void drawCircleIn(Canvas canvas) {
        float radius = (float) (HEIGHT - (HEIGHT * 0.3) * 2 - (WIDTH * 0.22));
        canvas.drawCircle(WIDTH / 2, HEIGHT / 2, radius, mInCrilePaint);
        canvas.save();
    }

    /**
     * 绘制内切圆和锯齿,通过canvas的旋转,画出对应的锯齿线
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        float radius = HEIGHT - (HEIGHT * 0.3f) * 2 - (WIDTH * 0.17f);
        canvas.drawCircle(WIDTH / 2, HEIGHT / 2, radius, mInCrilePaint);
        canvas.save();

        for (int i = 0; i < 72; i++) {
            if (i >= mProgress) {
                mInLine.setColor(Color.parseColor("#555555"));
            } else {
                mInLine.setColor(Color.parseColor("#00ff00"));
            }
            canvas.drawLine(WIDTH / 2, HEIGHT / 3.7f, WIDTH / 2, HEIGHT / 3.7f + HEIGHT * 0.05f , mInLine);
            canvas.rotate(5, getWidth() / 2, getHeight() / 2);
        }
    }

    /**
     * 绘制最外层弧线
     * @param canvas
     */
    private void drawOutArc1(Canvas canvas) {
        RectF mRectF = new RectF(WIDTH * 0.1f, WIDTH * 0.1f, WIDTH * 0.9f, WIDTH * 0.9f);
        canvas.drawArc(mRectF, startAngle + offset, 200, false, mPaintSmall);
    }

    /**
     * 绘制外层的第二条弧线
     * @param canvas
     */
    private void drawOutArc2(Canvas canvas) {
        RectF mRectF = new RectF(WIDTH * 0.14f, WIDTH * 0.14f, WIDTH * 0.85f, WIDTH * 0.85f);
        canvas.drawArc(mRectF, -(startAngle + offset), 150, false, mPaintBig);
    }

    /**
     * 绘制外层第三条弧线
     * @param canvas
     */
    private void drawOutArc3(Canvas canvas) {
        RectF mRectF = new RectF(WIDTH * 0.22f, WIDTH * 0.22f, WIDTH * 0.795f, WIDTH * 0.795f);
        canvas.drawArc(mRectF, startAngle + offset - 90, 110, false, mPaintSmall);
    }

    /**
     * 绘制外层第四条弧线
     * @param canvas
     */
    private void drawOutArc4(Canvas canvas) {
        RectF mRectF = new RectF(WIDTH * 0.255f, WIDTH * 0.255f, WIDTH * 0.75f, WIDTH * 0.75f);
        canvas.drawArc(mRectF, -(startAngle + offset + 150), 150, false, mPaintBig);
    }

    /**
     * 开始播放闪充动画
     */
    public void startChargeAnimator() {
        if (mAnimator != null) {
            mAnimator.start();
        }
    }

    /**
     * 停止闪充动画
     */
    public void endChargeAnimator() {
        if (mAnimator != null) {
            mAnimator.cancel();
        }
    }

    /**
     * 设置当前电量
     * @param power
     */
    public void setPower(int power) {
        this.mCurrPower = power;
        this.mProgress = power * 72.0f / 100;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float fraction = animation.getAnimatedFraction();
        this.offset = 360 * fraction;
        this.invalidate();
    }
}

 

2.在布局文件layout里,引用该View:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    tools:context="com.vivo.flashcharge.MainActivity">

    <com.vivo.flashcharge.FlashChargeView
        android:id="@+id/chargeView"
        android:layout_width="180dip"
        android:layout_height="180dip"
        android:layout_centerInParent="true"/>

</RelativeLayout>

 

3.Activity里引用该layout,并开始播放动画,设置当前电量。(这里用到了ButterKnife插件,该插件封装了findViewById等方法,具体以下再讲):

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.chargeView)
    FlashChargeView chargeView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        chargeView.startChargeAnimator();
        chargeView.setPower(90);
    }

}

 

好了,具体效果开发完成了,运行了一下,一点都不卡顿。这里我用的开发工具是Android Studio,很方便很快捷。

 

最后,得感谢下“瓶子盖”,没有他,我的第一篇博客也不知道要到什么时候才会诞生。估计以后会写更多的博客,大家一块交流嘛~~

 

不积跬步,无以至千里。。。