android仿58跳动图形加载动画

    想要知道关于更多自定义View的实例,请参考:android自定义View索引

先上个效果图,以免大家跑错地了。


    用高清的每次只能录三秒,不管了,大家能懂啥意思就行。

    大家看这效果就感觉比较复杂了,事实上比一般的复杂一点。

    看到这个样子的动画,首先我们需要理智分析,不要别表面现象所迷糊了,其实就是两个东西:

一:属性

二:动画

    详细说呢就是,一是物体颜色的变化和形状的变化,二是动画的跳动。

属性

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

方形: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,参考链接:github.com/zzz40500/an…

* 总的来说,颜色变换的速度收两个值控制,一个是: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();
}
}

复制代码

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


转载于:https://juejin.im/post/5a541dd2518825734978c524

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值