Android仿视频加载旋转小球动画效果的实例代码

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

嗯,除了只能录三秒,其他没啥问题。

下面分析一下怎么实现上面这个效果。

理性分析后我们可以看到是几个小球绕着一个圆进行运动,那这里面的重点我们看看什么。

绘制五个球,没什么难度,让球绕圆进行运动,这个好像我们没有见到是怎么去实现了,那下就说这个。

从本质上看,球绕圆运动,其实我们可以看作是一个物体绕指定的路劲运动,那我们就有下面几个东西需要说一下:

1:Path
2:ValueAnimator
3:PathMeasure

前两个大家应该都见过,一个是路径,就是可以自己绘制路线的一个工具,一个是动画,用来指定物体运动的工具,那第三个是一个关于测量路径的类。

下面说说PathMeasure的用法。

首先是初始化:

pathMeasure = new PathMeasure(path, false);  

两个参数第一个,第一个就是我们需要用到的路径,第二个参数意思就是这个以路径头尾是否相连来计算结果,通常我们就写false就行,不会有问题。

然后是用法:

private float[] mCurrentPositionOne = new float[2];
float value = (Float)
animation.getAnimatedValue();
pathMeasure.getPosTan(value, mCurrentPositionOne, null);
  我们可以看见把一个二维数组放到了getPosTan这个方法里面,然后还有一个animation,这里的animation来自哪里呢?来自这里:valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    // 获取当前点坐标封装到mCurrentPosition
    float value = (Float) animation.getAnimatedValue();
    pathMeasure.getPosTan(value, mCurrentPositionOne, null);
    postInvalidate();
  }
});

看见没,是动画的监听里面来的,getPosTan的最后一个参数通常也就写null就行了,那么这整个一行的代码意思就是当动画发生了变化,就执行这行代码,然后这行代码会把这个时间点的路径上的坐标赋值给mCurrentPositionOne。

那我们获取到看这个路径上的坐标点怎么办呢?

立马用来ondraw里面啊,我的小球此时就可以根据这个坐标点去绘制自己的位置,这个的话,当动画开始时,小球就会不断接受新的坐标,然后不断重绘,最终产生旋转小球的效果。

我先把属性动画的代码贴出来:

if (valueAnimatorOne == null) {
   valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength());
  valueAnimatorOne.setDuration(800);
  // 减速插值器
  valueAnimatorOne.setInterpolator(new DecelerateInterpolator());
  valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
      // 获取当前点坐标封装到mCurrentPosition
      float value = (Float) animation.getAnimatedValue();
      pathMeasure.getPosTan(value, mCurrentPositionOne, null);
      postInvalidate();
    }
  });
  valueAnimatorOne.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animator) {
      finishAnimateOne = 1;
    }
    @Override
    public void onAnimationEnd(Animator animator) {
      finishAnimateOne = 0;
    }
    @Override
    public void onAnimationCancel(Animator animator) {
    }
    @Override
    public void onAnimationRepeat(Animator animator) {
    }
  });
}
valueAnimatorOne.start();

我写了个800,也就是动画的维持时间,但是我们发现有啊后几个小球,所以我们需要绘制好几个小球,然后给他们不同的动画,为什么呢?因为动画都一样,小球就叠加在一起了,我们就只能看见一个球了。

说到这里的话,我们的目标算时完成了,具体的操作,大家参考以下代码,或者去:android自定义View索引

里面动画的demo进行下载,大家随意,下面给出代码:

/**

 * 仿视频加载动画,旋转的蓝色小球
 */
public class RotaryBall extends View {
  private Path rotationPath;
  private float radius;
  private Paint circlePaintOne;
  private PathMeasure pathMeasure;
  private int finishAnimateOne = 0;  // 用来判断当前动画有没有开始
  private int finishAnimateTwo = 0;  // 用来判断当前动画有没有开始
  private int finishAnimateThree = 0;  // 用来判断当前动画有没有开始
  private int finishAnimateFour = 0;  // 用来判断当前动画有没有开始
  private int finishAnimateFive = 0;  // 用来判断当前动画有没有开始
  private Handler handler;
  private float[] mCurrentPositionOne = new float[2];
  private float[] mCurrentPositionTwo = new float[2];
  private float[] mCurrentPositionThree = new float[2];
  private float[] mCurrentPositionFour = new float[2];
  private float[] mCurrentPositionFive = new float[2];
  private ValueAnimator valueAnimatorOne = null;
  private ValueAnimator valueAnimatorTwo = null;
  private ValueAnimator valueAnimatorThree = null;
  private ValueAnimator valueAnimatorFour = null;
  private ValueAnimator valueAnimatorFive = null;
  private int currentStatus = -1;  //-1表示第一次运行,0表示动画结束或者没开始,1表示正在运动中
  private boolean animateOrNot = true;  //用来决定是否开启动画
  public RotaryBall(Context context) {
    super(context);
    initData();
  }
  public RotaryBall(Context context, AttributeSet attrs) {
    super(context, attrs);
    initData();
  }
  private void initData() {
    rotationPath = new Path();
    circlePaintOne = new Paint();
    circlePaintOne.setColor(Color.BLUE);
    circlePaintOne.setAntiAlias(true);
    handler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
          case 4:
            if (finishAnimateOne == 0) {
              startAnimatorOne();
            }
            if (finishAnimateTwo == 0) {
              startAnimatorTwo();
            }
            if (finishAnimateThree == 0) {
              startAnimatorThree();
            }
            if (finishAnimateFour == 0) {
              startAnimatorFour();
            }
            if (finishAnimateFive == 0) {
              startAnimatorFive();
            }
            currentStatus = 0;
        }
      }
    };
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    radius = getMeasuredWidth() / 2;
  }
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
//    rotationPath.addCircle(radius, radius, radius - 10, CW);
    rotationPath.moveTo(radius, 0 + 10);
    rotationPath.cubicTo(radius, 0 + 10, radius * 2 - 10, 0 + 10, radius * 2 - 10, radius);
    rotationPath.cubicTo(radius * 2 - 10, radius, radius * 2 - 10, radius * 2 - 10, radius, radius * 2 - 10);
    rotationPath.cubicTo(radius, radius * 2 - 10, 0 + 10, radius * 2 - 10, 0 + 10, radius);
    rotationPath.cubicTo(0 + 10, radius, 0 + 10, 0 + 10, radius, 0 + 10);
    rotationPath.close();
    pathMeasure = new PathMeasure(rotationPath, false);
    //下面绘制不同半径的小圆
    canvas.drawCircle(mCurrentPositionOne[0], mCurrentPositionOne[1], 10, circlePaintOne);
    canvas.drawCircle(mCurrentPositionTwo[0], mCurrentPositionTwo[1], 9, circlePaintOne);
    canvas.drawCircle(mCurrentPositionThree[0], mCurrentPositionThree[1], 7, circlePaintOne);
    canvas.drawCircle(mCurrentPositionFour[0], mCurrentPositionFour[1], 5, circlePaintOne);
    canvas.drawCircle(mCurrentPositionFive[0], mCurrentPositionFive[1], 3, circlePaintOne);
    if (currentStatus == -1) {
      Message message = new Message();
      message.what = 4;
      handler.sendMessage(message);
    }
    if (animateOrNot) {
      if (currentStatus == 0) {
        currentStatus = 1;
        new Thread() {      //用线程来统一五个圆的周期
          @Override
          public void run() {
            super.run();
            try {
              Log.d("thread", "thread");
              Thread.sleep(1600);
              Message message = new Message();
              message.what = 4;
              handler.sendMessage(message);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
        }.start();
      }
    }
  }
  //供外部调用,开始动画
  public void startAnimate() {
    if (!animateOrNot) {
      animateOrNot = true;
      currentStatus = -1;
      invalidate();
    }
  }
  //供外部调用,停止动画
  public void stopAnimate() {
    if (animateOrNot) {
      animateOrNot = false;
    }
  }
  //界面被销毁
  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    stopAnimate();
    clearAllAnimation();
  }
  //清除所有动画效果
  private void clearAllAnimation() {
    if (valueAnimatorOne != null){
      if (valueAnimatorOne.isRunning()){
        valueAnimatorOne.cancel();
      }
      valueAnimatorOne.removeAllUpdateListeners();
      valueAnimatorOne = null;
    }
    if (valueAnimatorTwo != null){
      if (valueAnimatorTwo.isRunning()){
        valueAnimatorTwo.cancel();
      }
      valueAnimatorTwo.removeAllUpdateListeners();
      valueAnimatorTwo = null;
    }
    if (valueAnimatorThree != null){
      if (valueAnimatorThree.isRunning()){
        valueAnimatorThree.cancel();
      }
      valueAnimatorThree.removeAllUpdateListeners();
      valueAnimatorThree = null;
    }
    if (valueAnimatorFour != null){
      if (valueAnimatorFour.isRunning()){
        valueAnimatorFour.cancel();
      }
      valueAnimatorFour.removeAllUpdateListeners();
      valueAnimatorFour = null;
    }
    if (valueAnimatorFive != null){
      if (valueAnimatorFive.isRunning()){
        valueAnimatorFive.cancel();
      }
      valueAnimatorFive.removeAllUpdateListeners();
      valueAnimatorFive = null;
    }
  }
  //开始第一个小球的动画
  private void startAnimatorOne() {
    if (valueAnimatorOne == null) {
      Log.d("valueAnimatorOne", "valueAnimatorOne");
      valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength());
      valueAnimatorOne.setDuration(800);
      // 减速插值器
      valueAnimatorOne.setInterpolator(new DecelerateInterpolator());
      valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          // 获取当前点坐标封装到mCurrentPosition
          float value = (Float) animation.getAnimatedValue();
          pathMeasure.getPosTan(value, mCurrentPositionOne, null);
          postInvalidate();
        }
      });
      valueAnimatorOne.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
          finishAnimateOne = 1;
        }
        @Override
        public void onAnimationEnd(Animator animator) {
          finishAnimateOne = 0;
        }
        @Override
        public void onAnimationCancel(Animator animator) {
        }
        @Override
        public void onAnimationRepeat(Animator animator) {
        }
      });
    }
    valueAnimatorOne.start();
  }
  //开始第二个小球的动画
  private void startAnimatorTwo() {
    if (valueAnimatorTwo == null) {
      valueAnimatorTwo = ValueAnimator.ofFloat(0, pathMeasure.getLength());
      valueAnimatorTwo.setDuration(1000);
      // 减速插值器
      valueAnimatorTwo.setInterpolator(new DecelerateInterpolator());
      valueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          float value = (Float) animation.getAnimatedValue();
          // 获取当前点坐标封装到mCurrentPosition
          pathMeasure.getPosTan(value, mCurrentPositionTwo, null);
          postInvalidate();
        }
      });
      valueAnimatorTwo.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
          finishAnimateTwo = 1;
        }
        @Override
        public void onAnimationEnd(Animator animator) {
          finishAnimateTwo = 0;
        }
        @Override
        public void onAnimationCancel(Animator animator) {
        }
        @Override
        public void onAnimationRepeat(Animator animator) {
        }
      });
    }
    valueAnimatorTwo.start();
  }
  //开始第三个小球的动画
  private void startAnimatorThree() {
    if (valueAnimatorThree == null) {
      valueAnimatorThree = ValueAnimator.ofFloat(0, pathMeasure.getLength());
      valueAnimatorThree.setDuration(1200);
      // 减速插值器
      valueAnimatorThree.setInterpolator(new DecelerateInterpolator());
      valueAnimatorThree.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          float value = (Float) animation.getAnimatedValue();
          // 获取当前点坐标封装到mCurrentPosition
          pathMeasure.getPosTan(value, mCurrentPositionThree, null);
          postInvalidate();
        }
      });
      valueAnimatorThree.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
          finishAnimateThree = 1;
        }
        @Override
        public void onAnimationEnd(Animator animator) {
          finishAnimateThree = 0;
        }
        @Override
        public void onAnimationCancel(Animator animator) {
        }
        @Override
        public void onAnimationRepeat(Animator animator) {
        }
      });
    }
    valueAnimatorThree.start();
  }
  //开始第四个小球的动画
  private void startAnimatorFour() {
    if (valueAnimatorFour == null) {
      valueAnimatorFour = ValueAnimator.ofFloat(0, pathMeasure.getLength());
      valueAnimatorFour.setDuration(1400);
      // 减速插值器
      valueAnimatorFour.setInterpolator(new DecelerateInterpolator());
      valueAnimatorFour.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          float value = (Float) animation.getAnimatedValue();
          // 获取当前点坐标封装到mCurrentPosition
          pathMeasure.getPosTan(value, mCurrentPositionFour, null);
          postInvalidate();
        }
      });
      valueAnimatorFour.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
          finishAnimateFour = 1;
        }
        @Override
        public void onAnimationEnd(Animator animator) {
          finishAnimateFour = 0;
        }
        @Override
        public void onAnimationCancel(Animator animator) {
        }
        @Override
        public void onAnimationRepeat(Animator animator) {
        }
      });
    }
    valueAnimatorFour.start();
  }
  //开始第五个小球的动画
  private void startAnimatorFive() {
    if (valueAnimatorFive == null) {
      valueAnimatorFive = ValueAnimator.ofFloat(0, pathMeasure.getLength());
      valueAnimatorFive.setDuration(1600);
      // 减速插值器
      valueAnimatorFive.setInterpolator(new DecelerateInterpolator());
      valueAnimatorFive.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          float value = (Float) animation.getAnimatedValue();
          // 获取当前点坐标封装到mCurrentPosition
          pathMeasure.getPosTan(value, mCurrentPositionFive, null);
          postInvalidate();
        }
      });
      valueAnimatorFive.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
          finishAnimateFive = 1;
        }
        @Override
        public void onAnimationEnd(Animator animator) {
          finishAnimateFive = 0;
        }
        @Override
        public void onAnimationCancel(Animator animator) {
        }
        @Override
        public void onAnimationRepeat(Animator animator) {
        }
      });
    }
    valueAnimatorFive.start();
  }
}

总结

以上所述是小编给大家介绍的Android仿视频加载旋转小球动画实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android webview旋转屏幕导致页面重新加载问题解决办法

    Android webview旋转屏幕导致页面重新加载问题解决办法 1. 在create时候加个状态判断 protected void onCreate(Bundle savedInstanceState){ ... if (savedInstanceState == null) { mWebView.loadUrl("your_url"); } ... } 2. 重载保存状态的函数: @Override protected void onSaveInstanceState(Bundl

  • Android使用Matrix旋转图片模拟碟片加载过程

    今天实现了一个模拟碟片加载过程的小demo,在此展示一下.由于在公司,不好截取动态图片,因此就在这截取两张静态图片看看效果先. 下面简单的将代码列出来. setp1.准备两张用于旋转的图片,如下:loading_disc.png是第一张图片,loading_light.png是第二张图片.      step2.自定义一个View,用来控制这两个图片的旋转.com.oyp.loadingdisk.LoadingDiscView.java package com.oyp.loadingdisk;

  • Android仿视频加载旋转小球动画效果的实例代码

    先上个效果图,以免大家跑错地了. 嗯,除了只能录三秒,其他没啥问题. 下面分析一下怎么实现上面这个效果. 理性分析后我们可以看到是几个小球绕着一个圆进行运动,那这里面的重点我们看看什么. 绘制五个球,没什么难度,让球绕圆进行运动,这个好像我们没有见到是怎么去实现了,那下就说这个. 从本质上看,球绕圆运动,其实我们可以看作是一个物体绕指定的路劲运动,那我们就有下面几个东西需要说一下: 1:Path 2:ValueAnimator 3:PathMeasure 前两个大家应该都见过,一个是路径,就是可

  • Android仿ios加载loading菊花图效果

    项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况的显示,还有显示文字.   使用ProgressBar 来加载动画转圈,这里使用drawable文件 定义转圈动画, indeterminateDrawable 属性进行加载. <?xml version="1.0" encoding="utf-8"?> &

  • Android中Glide加载圆形图片和圆角图片实例代码

    一.简介: 介绍两种使用 BitmapTransformation 来实现 Glide 加载圆形图片和圆角图片的方法.Glide 并不能直接支持 Round Pictures ,需要使用 BitmapTransformation 来进行处理. 二.网上的实现方式 这里介绍下网上常见的方式和使用 RoundedBitmapDrawable 两种方法,本质上是差不多的: 使用 Canvas 和 Paint 来绘制 使用 Android.support.v4.graphics.drawable.Rou

  • jquery Ajax 实现加载数据前动画效果的示例代码

    复制代码 代码如下: $(document).ready(function(){     $.ajax({        type:"get",        cache:false,        url:"ajaxpage.aspx?t=getcity",        dataType:"json",        beforeSend:function(){           $("#vvv").append('&l

  • Android加载loading对话框的功能及实例代码(不退出沉浸式效果)

    一.自定义Dialog 在沉浸式效果下,当界面弹出对话框时,对话框将获取到焦点,这将导致界面退出沉浸式效果,那么是不是能通过屏蔽对话框获取焦点来达到不退出沉浸式的目的呢.说干就干,我们先来看一下改善后的效果图. 普通对话框弹出效果 LoadingDialog弹出效果 自定义LoadingDialog public class LoadingDialog extends Dialog { public LoadingDialog(Context context) { super(context);

  • Android仿支付宝中余额宝的数字动画效果

    实现效果图: 下面是具体代码,可直接复制: package com.lcw.rabbit.widget; import android.animation.ObjectAnimator; import android.content.Context; import android.text.TextUtils; import android.util.AttributeSet; import android.view.animation.AccelerateDecelerateInterpola

  • Vue实现上拉加载下一页效果的示例代码

    之前从来没有单独的做过手机端的网页.当然,之前我也没有独立的从切图到写代码交互做过前端的页面. 这里边的分页还是和响应电脑端的数字分页.但是,其实独立做一个手机端的网站,而不是响应式的网站的时候,这种数字分页看起来可能是不太好. 手机端网站嘛,还是仿照着APP或者是微信小程序那种上拉触底分页比较好.但是,这个玩意怎么实现呢? 第一种想法,监听滚动条,滚动条滚动到页面底部,触发方法,请求接口加载下一页的数据.嗯,应该是可行的,我们来尝试一下: 监听滚动条所在位置的方法如下: /** * @name

  • Android仿微博个人详情页滚动到顶部的实例代码

    个人详情页滑动到顶部 最近产品提了个新需求,需要实现点击App内的某个按钮跳转到个人详情页并且滑动到顶部,个人详情页的页面交互稍微复杂,技术角度上包含了状态栏颜色变换,view滑动联动等问题,技术实现上采用了Google出的CoordinatorLayout那套组件,由于App的个人详情页跟微博的相似,这里就拿微博为例来描述.微博默认的效果以及手动滑动到顶部的效果图如下. 个人详情页技术实现分析: 先看看xml布局的伪代码: <?xml version="1.0" encodin

  • php类自动加载失败的处理方案及实例代码

    1.打开相应的PHP代码文件. 2.添加"$class = str_replace("\\","/",$class);"代码即可. 文件在本地win系统下测试无异常,代码如下: function stu_autoload($class){ if(file_exists($class.".php")){ require ( $class.".php"); }else{ die("unable to

  • iOS自带动画效果的实例代码

     1.普通动画: [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:2]; frame.origin.x += 150; [img setFrame:frame]; [UIView commitAnimations]; 2.连续动画(一系列图像): NSArray *myImages = [NSArray arrayWithObjects: [UIImage imageNamed:@"myImage1.p

随机推荐