Android动画——仿vivo X6闪充动画效果

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

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

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

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

下面讲讲实现原理:

该动画实际上是由四条弧线,和中间的带线条的圆形组成。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,很方便很快捷。

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

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

时间: 2024-10-13 15:28:12

Android动画——仿vivo X6闪充动画效果的相关文章

Android之仿今日头条顶部导航栏效果

随着时间的推移现在的软件要求显示的内容越来越多,所以要在小的屏幕上能够更好的显示更多的内容,首先我们会想到底部菜单栏,但是有时候像今日头条新闻客户端要显示的内容太多,而且又想在主界面全部显示出来,所以有加了顶部导航栏. 今日头条顶部导航栏区域的主要部分是一个导航菜单.导航菜单是一组标签的集合,在新闻客户端中,每个标签标示一个新闻类别,对应下面ViewPager控件的一个分页面.当用户在ViewPager区域滑动页面时,对应的导航菜单标签也会相应的被选中,选中的标签通过一个矩形红框高亮显示,红框背

Android自定义View——仿vivo i管家病毒扫描动画效果

技术是永无止境的,如果真的爱技术,那就勇敢的坚持下去.我很喜欢这句话,当我在遇到问题的时候.当我觉得代码枯燥的时候,我就会问自己,到底是不是真的热爱技术,这个时候,我心里总是起着波澜,我的答案是肯定的,我深深的爱着这门技术. 今天我们继续聊聊Android的自定义View系列.先看看效果吧: 这个是我手机杀毒软件的一个动画效果,类似于雷达搜索,所以用途还是很广泛的,特别是先了解一下这里的具体逻辑和写法,对技术的进步一定很有用. 先简单的分析一下这里的元素,主要有四个圆.一个扇形.还有八条虚线.当

Android自定义下拉刷新动画--仿百度外卖下拉刷新

好久没写博客了,小编之前一段时间一直在找工作,从天津来到了我们的大帝都,感觉还不错.好了废话不多说了,开始我们今天的主题吧.现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了"百度外卖"的下拉刷新,今天的主题就是它–自定义下拉刷新动画. 看一下实现效果吧: 动画 我们先来看看Android中的动画吧: Android中的动画分为三种: Tween动画,这一类的动画提供了旋转.平移.缩放等效果. Alpha – 淡入淡出 Scale

Android攻城狮四种基础动画

AlphaAnimation(透明动画) 1.xml文件 <set xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 渐变动画,表示从透明度10%到100%,持续时间为1秒 --> <alpha android:fromAlpha="0.1" android:toAlpha="1" android:duration="1000&

基本动画CABasicAnimation - 完成之后闪回初始状态

基本动画CABasicAnimation 结束之后,默认闪回初始状态,那怎么解决呢? position需要设备两个属性: 1 // MARK: - 结束后不要闪回去 2 anim.removedOnCompletion = NO; 3 anim.fillMode = kCAFillModeForwards; 设置之后,不会再闪回去,但其实控件的位置并未改变,还在原来的位置,只是"显示层"挪到了新位置. 可以通过动画的代理方法来实现: // MARK: - 通过代理方法,修正按钮的位置!

[android] 练习样式主题自定义activity切换动画

主要练习了自定义样式和主题,继承android系统默认的样式并修改,练习xml定义淡入淡出动画 anim/fade_in.xml <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="3000" android:from

[转]Android Activity和Fragment的转场动画

Android Activity和Fragment的转场动画 Activity转场动画 Activity的转场动画是通过overridePendingTransition(int enterAnim, int exitAnim)实现的. 这个方法是API Level 5 加入的. 这个方法在startActivity(Intent) or finish()之后被调用,指定接下来的这个转场动画. 方法的第一个参数:enterAnim,是新的Activity的进入动画的resource ID: 第二

Android分别通过代码和xml实现动画效果

一.Android动画类型 Android的animation由四种类型组成: XML中 alph 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 JavaCode中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸伸缩动画效果 TranslateAnimation 画面转换位置移动动画效果 RotateAnimation 画面转移旋转动画效果 二.Android动画模

基于jQuery仿搜狐辩论投票动画代码

基于jQuery仿搜狐辩论投票动画代码.这是一款个性的卡通小人正方反方辩论投票特效代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <script type="text/javascript"> $(document).ready(function () { var a = 500; var b = 130; $("#white").animate({ width: 0, left: "250px" }, 10