android仿58跳动图形加载动画

想要知道关于更多自定义View的实例,请参考:android自定义View索引
先上个效果图,以免大家跑错地了。

image

用高清的每次只能录三秒,不管了,大家能懂啥意思就行。
大家看这效果就感觉比较复杂了,事实上比一般的复杂一点。
看到这个样子的动画,首先我们需要理智分析,不要别表面现象所迷糊了,其实就是两个东西:
一:属性
二:动画
详细说呢就是,一是物体颜色的变化和形状的变化,二是动画的跳动。

属性

那么怎么动态修改一个物体的形状和颜色,从上面的图我们可以看出来,我们使用了三个形状,方形,三角形和圆形,给出三个绘制函数:

方形 :canvas.drawPath(rectPath,paint);
三角形:canvas.drawPath(trianglePath,paint);
圆形 :canvas.drawCircle(controlWidth /2,controlWidth /2,controlWidth /2,paint);

这里的方形和三角形,我们采用的是用Path的方式进行绘制,不知道啥是Path的百度一下吧,为了缩减篇幅就不说了。
好了,那现在我们能够随心所欲的绘制这三个图形,但是我们什么时候该绘制什么图形呢?我们可以写一个枚举类,用来储存我们的形状,然后进行切换:
变形的集中图形列表,分别是方形,三角形,圆形

public enum ShapeKind {
*RECT,TRIANGLE,CIRCLE*
}

枚举类,写好了,包含三个图形,现在来实现了动态切换图形的形状了,我们可以用一个变量去递增,例如,我给个变量 i 初始值为 1 , 但是每次重绘的时候添加了,如果到了100,那么就开始切换成另外一个图形,并且将 i 重置为 100, 然后又去循环,这样就能不断动态的切换形状了,至于颜色不就更简单了,每个图形给不同的画笔,不就有三种颜色了 。
但是有时我们会发现我们的重绘速度太快了,那么我们可以用定时器减慢速度:

handler.postDelayed(new
    Runnable() {
        @Override
        public void run () {
            Message message = new Message();
            message.what = 1;
            handler.sendMessage(message);
        }
    },20);
}

这样的话就会每个0.02秒进行重绘一次了,速度就在我们的掌控之中了。

整合上面的知识,我们就能绘制出一个不断变换形状和颜色的控件了,我们起名为: ** TransformationLoading**, 然后被下面动画的类进行使用即可。

动画

下面看看我们的物体的运动变化,观察到了三种动作,一个上升,一个下降,还有一个旋转,并且都是同时进行的,那么这样我就必须要用到一个平时不常用到的函数了:

AnimatorSet
这个函数的作用就是用来把单独的动画集合起来的工具,简单给个例子:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading,"translationY",0,jumpDistance);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow,"scaleX",0.2f,1f);
downAnimator =new AnimatorSet();
downAnimator.playTogether(objectAnimator, scaleIndication);
downAnimator.setDuration(time);

上面的前两行分别是位移和旋转的动画,第四行则是把两个动画集合在了一起,然后第五行是设置动画时间,基本用法就是这样。

但是我们仔细观察上面的图形,我们发现跳动的物体下面还有一个阴影和加载中三个字,不用慌,看到这里我们想到我们现在这个控件是一个容器,那我们就继承 LinearLayout 吧,起名为 ** JumpLoading ** 。

这样的话就能包含三个控件了,一个TransformationLoading,也就是我们上面属性部分创造的控件,一个阴影,其实就是一个ImageView,外加一个TextView。

那动画我们知道怎么集合再一起了,那动画上面时候开始执行和结束呢?下面给出两个函数:
//界面加载结束

    @Override
    protected void onFinishInflate() {*
        super.onFinishInflate();*
        startAnimate(0);      //开始动画*
    }

//界面被销毁

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopAnimate();     //停止动画
    }

两个函数属于linearLayout的生命周期里面的函数,分别是刚被绘制时和被销毁时回调的函数,再这里我们可以开始和结束动画。

说到这里所有的运转流程大家应该也就比较清楚了,具体的操作大家还是要去代码里面感受一下,具体的使用大家直接使用动画控件就行,动画控件的代码是在属性代码的基础上使用的。
先给属性的代码
不断变换图形的加载动画,仿58,参考链接:https://github.com/zzz40500/android-shapeLoadingView
总的来说,颜色变换的速度收两个值控制,一个是:colorChangeRate,一个是time,一个颜色变换率,一个是切换颜色的速度

    public class TransformationLoadingextends View {
        public static ShapeKindshapeKind =RECT;
        private float currentColorChange = 0f;//当前颜色切换总值
        private ArgbEvaluatormAnimPercent =new
        ArgbEvaluator();//用来转化两种颜色的系统工具,详情自查
        private Paintpaint;
        private float controlWidth;//控件宽度
        private Handlerhandler;
    }

//下面的参数可以根据实际需要进行更改

 private int mRectColor;//方形的颜色
    private int mTriangleColor;//三角形的颜色
    private int mCircle;//圆形的颜色
    private float colorChangeRate = 0.02f;//集中图形切换时颜色的变化速度,推荐0.01-0.5,数值越小颜色变化越不突兀
    private long time = 10;//动画的执行速度,推荐10 - 100,数值越大,动画越慢
    private boolean changeKind = false;//是否开启主动切换形状的控制器
    private boolean FlashingOrNot = false;//是否开启颜色闪动
    private boolean animateOrNot = true;//是否执行不断刷新
    public TransformationLoading(Context context) {
        super(context);
        initData();
    }
    public TransformationLoading(Context context, AttributeSet attrs) {
        super(context, attrs);
        initData();
    }

//初始化几个图形的颜色

   private void initData() {
        mRectColor = Color.argb(255, 114, 213, 114);
        mTriangleColor = Color.argb(255, 115, 153, 254);
        mCircle = Color.argb(255, 232, 78, 64);
        paint = new Paint();
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        invalidate();//重绘界面
                       break;
                }
            }
        };
    }
    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        controlWidth = getMeasuredWidth();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (shapeKind) {
            case RECT://绘制一个正方形
               currentColorChange += colorChangeRate;
                if (FlashingOrNot) {
                    int rectColor = (int) mAnimPercent.evaluate(currentColorChange, mRectColor, mTriangleColor);
                    paint.setColor(rectColor);
                } else {
                    paint.setColor(mRectColor);
                }
                if (currentColorChange > 1) {
                    if (changeKind) {
                        shapeKind = TRIANGLE;
                    }
                    currentColorChange = 0f;
                }
                Path rectPath = new Path();
                rectPath.moveTo(0, 0);
                rectPath.lineTo(controlWidth, 0);
                rectPath.lineTo(controlWidth, controlWidth);
                rectPath.lineTo(0, controlWidth);
                rectPath.close();
                canvas.drawPath(rectPath, paint);
                if (animateOrNot) {
                    toRefresh();
                }
                break;
            case TRIANGLE://绘制一个三角形
               currentColorChange += colorChangeRate;
                if (FlashingOrNot) {
                    int triangleColor = (int) mAnimPercent.evaluate(currentColorChange, mTriangleColor, mCircle);
                    paint.setColor(triangleColor);
                } else {
                    paint.setColor(mTriangleColor);
                }
                if (currentColorChange > 1) {
                    if (changeKind) {
                        shapeKind = CIRCLE;
                    }
                    currentColorChange = 0f;
                }
                Path trianglePath = new Path();
                trianglePath.moveTo(0.5f * controlWidth, 0);
                trianglePath.lineTo(controlWidth, controlWidth);
                trianglePath.lineTo(0, controlWidth);
                trianglePath.close();
                canvas.drawPath(trianglePath, paint);
                if (animateOrNot) {
                    toRefresh();
                }
                break;
            case CIRCLE://绘制一个圆形
               currentColorChange += colorChangeRate;
                if (FlashingOrNot) {
                    int circleColor = (int) mAnimPercent.evaluate(currentColorChange, mCircle, mRectColor);
                    paint.setColor(circleColor);
                } else {
                    paint.setColor(mCircle);
                }
                if (currentColorChange > 1) {
                    if (changeKind) {
                        shapeKind = RECT;
                    }
                    currentColorChange = 0f;
                }
                canvas.drawCircle(controlWidth / 2, controlWidth / 2, controlWidth / 2, paint);
                if (animateOrNot) {
                    toRefresh();
                }
                break;
        }
    }

//用来供主动切换形状的方法

    public void changeShape() {
       currentColorChange = 0f;//重置颜色渐变数值
       changeKind = false;//关闭自动切换图形的功能
       switch (shapeKind) {
            case RECT:
                shapeKind = TRIANGLE;
                break;
            case TRIANGLE:
                shapeKind = CIRCLE;
                break;
            case CIRCLE:
                shapeKind = RECT;
                break;
        }
    }

//重新刷新界面

    private void toRefresh() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = 1;
                handler.sendMessage(message);
            }
        }, time);
    }

//变形的集中图形列表,分别是方形,三角形,圆形

    public enum ShapeKind {
        RECT, TRIANGLE, CIRCLE
    }

//停止动画

    public void stopAnimate() {
        animateOrNot = false;
    }

//开始动画

    public void startAnimate() {
        if (!animateOrNot) {
            animateOrNot = true;
            toRefresh();
        }
    }

再给动画的代码:

  • 仿58加载动画跳动的小球,和TransformationLoading控件结合起来使用,并且要关闭TransformationLoading的主动切换形状的控制器,也就是:changeKind
    public class JumpLoadingextends LinearLayout {
        private AnimatorSetupAnimator;//上升的动画集合
        private AnimatorSetdownAnimator;//下降的动画集合
        private boolean move = false;//当前有没有执行动画
        private Runnablerunnable;
        private TransformationLoadingtransformationLoading;//跳动的小球
        private ImageViewjumpShadow;//下面的阴影

//下面的参数可以根据实际需要进行更改
        private int jumpDistance = 200;//跳动的距离
        private int time = 500;//跳动一个周期的时间,0.5秒
        private int angle = 180;//跳动过程中的旋转角度
        public JumpLoading(Context context) {
            super(context);
            initData(context);
        }
        public JumpLoading(Context context, AttributeSet attrs) {
            super(context, attrs);
            initData(context);
        }
        private void initData(Context context) {
            LayoutInflater.from(context).inflate(R.layout.jump_layout, this, true);
            transformationLoading = (TransformationLoading) findViewById(R.id.transformationLoading);
            jumpShadow = (ImageView) findViewById(R.id.jumpShadow);
            runnable = new Runnable() {
                @Override
                public void run() {
                    ViewHelper.setRotation(transformationLoading, 180f);
                    ViewHelper.setTranslationY(transformationLoading, 0f);
                    ViewHelper.setScaleX(jumpShadow, 0.2f);
                    move = true;
                    freeFall();//先执行下降动画
                }
            };
        }
//下降动画
        private void freeFall() {
            if (downAnimator == null) {
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading, "translationY", 0, jumpDistance);
                ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow, "scaleX", 0.2f, 1f);
                downAnimator = new AnimatorSet();
                downAnimator.playTogether(objectAnimator, scaleIndication);
                downAnimator.setDuration(time);
                downAnimator.setInterpolator(new AccelerateInterpolator(1.2f));
                downAnimator.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        if (move) {
                            transformationLoading.changeShape();
                            rise();
                        }
                    }
                    @Override
                    public void onAnimationCancel(Animator animation) {
                    }
                    @Override
                    public void onAnimationRepeat(Animator animation) {
                    }
                });
            }
            downAnimator.start();
        }
//上升动画
        private void rise() {
            if (upAnimator == null) {
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading, "translationY", jumpDistance, 0);
                ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow, "scaleX", 1f, 0.2f);
                ObjectAnimator objectAnimator1 = null;
                objectAnimator1 = ObjectAnimator.ofFloat(transformationLoading, "rotation", 0, angle);
                upAnimator = new AnimatorSet();
                upAnimator.playTogether(objectAnimator, objectAnimator1, scaleIndication);
                upAnimator.setDuration(time);
                upAnimator.setInterpolator(new DecelerateInterpolator(1.2f));
                upAnimator.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        if (move) {
                            freeFall();
                        }
                    }
                    @Override
                    public void onAnimationCancel(Animator animation) {
                    }
                    @Override
                    public void onAnimationRepeat(Animator animation) {
                    }
                });
            }
            upAnimator.start();
        }
//开始执行动画,从runnable开始,然后rise和freeFall互调
        public void startAnimate(int delay) {
            if (!move) {
                if (downAnimator != null && downAnimator.isRunning()) {
                    return;
                }
                this.removeCallbacks(runnable);
                if (delay > 0) {
                    this.postDelayed(runnable, delay);
                } else {
                    this.post(runnable);
                }
            }
        }
//停止动画,停止所有动画和监听
        public void stopAnimate() {
            move = false;
            if (upAnimator != null) {
                if (upAnimator.isRunning()) {
                    upAnimator.cancel();
                }
                upAnimator.removeAllListeners();
                for (Animator animator : upAnimator.getChildAnimations()) {
                    animator.removeAllListeners();
                }
                upAnimator = null;
            }
            if (downAnimator != null) {
                if (downAnimator.isRunning()) {
                    downAnimator.cancel();
                }
                downAnimator.removeAllListeners();
                for (Animator animator : downAnimator.getChildAnimations()) {
                    animator.removeAllListeners();
                }
                downAnimator = null;
            }
            this.removeCallbacks(runnable);
        }
//界面被加载结束
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            startAnimate(0);
        }
//界面被销毁
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            stopAnimate();
        }
    }

喜欢我的文章的,请点击关注我哦。万学冬的简书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,030评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,198评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,995评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,973评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,869评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,766评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,967评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,599评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,886评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,901评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,728评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,504评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,967评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,128评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,445评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,018评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,224评论 2 339

推荐阅读更多精彩内容