热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要介绍了Android仿视频加载旋转小球动画效果的实例代码,文中给大家提到了PathMeasure的用法,介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

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

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

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

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

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

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

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仿视频加载旋转小球动画实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
author-avatar
爵士KI
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有