{"id":13641016,"url":"https://github.com/linglongxin24/BaiduProgressBar","last_synced_at":"2025-04-20T07:31:40.554Z","repository":{"id":90164170,"uuid":"75543168","full_name":"linglongxin24/BaiduProgressBar","owner":"linglongxin24","description":"仿百度加载动画，一种优雅的Loading方式","archived":false,"fork":false,"pushed_at":"2016-12-05T14:08:16.000Z","size":4119,"stargazers_count":67,"open_issues_count":0,"forks_count":20,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-08-03T01:19:12.135Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/linglongxin24.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-12-04T14:41:43.000Z","updated_at":"2024-05-24T10:49:41.000Z","dependencies_parsed_at":"2023-07-05T14:47:33.581Z","dependency_job_id":null,"html_url":"https://github.com/linglongxin24/BaiduProgressBar","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linglongxin24%2FBaiduProgressBar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linglongxin24%2FBaiduProgressBar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linglongxin24%2FBaiduProgressBar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linglongxin24%2FBaiduProgressBar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linglongxin24","download_url":"https://codeload.github.com/linglongxin24/BaiduProgressBar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223821995,"owners_count":17208782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:01:16.877Z","updated_at":"2024-11-09T11:30:37.941Z","avatar_url":"https://github.com/linglongxin24.png","language":"Java","funding_links":[],"categories":["进度条"],"sub_categories":[],"readme":"#【Android自定义View实战】之仿百度加载动画，一种优雅的Loading方式\n\n\u003e无意中看到了百度的加载动画，看起来非常优雅，打算亲手造一个。\n\n ![效果图](https://github.com/linglongxin24/BaiduProgressBar/blob/master/screenshots/baiduloading.gif?raw=true)\n\n\n\n\u003e仿百度加载动画的重要思路：当第一遍执行完毕后就让第一个停下来在中间位置，换原来中间位置的第三个开始执行动画，\n 以此类推，当第二遍执行完毕后第二个停下来，中间位置的开始执行动画。\n \n ![效果图](https://github.com/linglongxin24/BaiduProgressBar/blob/master/screenshots/effect.gif?raw=true)\n\n#第一个：仿百度加载动画，用ObjectAnimator属性动画操作ImageView的属性方法实现：\n\n * 1.布局文件\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"500px\"\n    android:layout_height=\"500px\"\n    android:orientation=\"vertical\"\u003e\n\n    \u003cImageView\n        android:id=\"@+id/iv_blue\"\n        android:layout_width=\"wrap_content\"\n        android:scaleType=\"matrix\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@mipmap/dot_blue\"\n        android:layout_gravity=\"center\" /\u003e\n    \u003cImageView\n        android:id=\"@+id/iv_yellow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:scaleType=\"matrix\"\n        android:src=\"@mipmap/dot_yellow\"\n        android:layout_gravity=\"center\" /\u003e\n    \u003cImageView\n        android:id=\"@+id/iv_red\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:scaleType=\"matrix\"\n        android:src=\"@mipmap/dot_red\"\n        android:layout_gravity=\"center\" /\u003e\n\u003c/FrameLayout\u003e\n```\n\n * 2.代码\n\n```java\npackage cn.bluemobi.dylan.baiduprogressbar;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 仿百度优雅的加载动画\n * Created by dylan on 2016-12-04.\n */\n\npublic class BaiduProgressBar extends FrameLayout {\n    /**\n     * 开始执行的第一个动画的索引，\n     * 由于第一个和第二个同时当执行，\n     * 当第一遍执行完毕后就让第一个停下来在中间位置，换原来中间位置的第三个开始执行动画，\n     * 以此类推，当第二遍执行完毕后第二个停下来，中间位置的开始执行动画。\n     */\n    private int startIndex = 0;\n    /**\n     * 交换执行动画的源图片数组\n     */\n    private int[] src = new int[]{R.mipmap.dot_yellow, R.mipmap.dot_red, R.mipmap.dot_blue};\n    /**\n     * 存放三个ImageView的的集合\n     */\n    private List\u003cImageView\u003e views = new ArrayList\u003c\u003e();\n    /**\n     * 让左边和右边动画同时执行的AnimatorSet对象\n     */\n    private AnimatorSet animatorSet;\n\n    /**\n     * 动画所执行的最大半径（即中间点和最左边的距离）\n     */\n    private int maxRadius = 200;\n\n    public BaiduProgressBar(Context context) {\n        super(context);\n        init();\n    }\n\n    public BaiduProgressBar(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public BaiduProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    /**\n     * 查找布局控件\n     */\n    private void assignViews() {\n        ImageView iv_blue = (ImageView) findViewById(R.id.iv_blue);\n        ImageView iv_yellow = (ImageView) findViewById(R.id.iv_yellow);\n        ImageView iv_red = (ImageView) findViewById(R.id.iv_red);\n        views.add(iv_yellow);\n        views.add(iv_red);\n        views.add(iv_blue);\n    }\n\n    /**\n     * 初始化\n     */\n    private void init() {\n        LayoutInflater.from(getContext()).inflate(R.layout.baidu_progress_bar, this, true);\n        assignViews();\n        startAnimator();\n    }\n\n    /**\n     * 开始执行动画\n     */\n    private void startAnimator() {\n        /**向左来回移动的X位移动画**/\n        ObjectAnimator objectAnimatorLeft = ObjectAnimator.ofFloat(views.get(0), \"translationX\", 0, -maxRadius, 0);\n        objectAnimatorLeft.setRepeatCount(-1);\n        objectAnimatorLeft.setDuration(1000);\n\n        /**向右来回移动的X位移动画**/\n        ObjectAnimator  objectAnimatorRight = ObjectAnimator.ofFloat(views.get(1), \"translationX\", 0, maxRadius, 0);\n        objectAnimatorRight.setRepeatCount(-1);\n        objectAnimatorRight.setDuration(1000);\n\n        /**动画组合-\u003e让左右同时执行**/\n        animatorSet = new AnimatorSet();\n        animatorSet.play(objectAnimatorRight).with(objectAnimatorLeft);\n        animatorSet.setInterpolator(new LinearInterpolator());\n        animatorSet.start();\n\n        /**动画监听**/\n        objectAnimatorLeft.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n                /**每次记录一下下次应该停止在中间的Image索引，然后和中间的交换**/\n                if (startIndex == 0) {\n                    sweep(0, 2);\n                    startIndex = 1;\n                } else {\n                    sweep(1, 2);\n                    startIndex = 0;\n                }\n            }\n        });\n\n    }\n\n    /**\n     * 每次让先执行动画的目标和中间停止的动画目标交换\n     *\n     * @param a 最先执行的动画的索引\n     * @param b 在中间动画的索引\n     */\n    private void sweep(int a, int b) {\n        views.get(a).setImageResource(src[b]);\n        views.get(b).setImageResource(src[a]);\n        int temp = src[b];\n        src[b] = src[a];\n        src[a] = temp;\n    }\n\n    /**\n     * 在View销毁时停止动画\n     */\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        animatorSet.cancel();\n    }\n}\n\n```\n#第二个：仿百度加载动画第二种实现方式，用ValueAnimator+原生的ondraw()方法实现：\n\n```java\npackage cn.bluemobi.dylan.baiduprogressbar;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 仿百度优雅的加载动画\n * Created by dylan on 2016-12-04.\n */\n\npublic class BaiduProgressBar2 extends View {\n    /**\n     * 开始执行的第一个动画的索引，\n     * 由于第一个和第二个同时当执行，\n     * 当第一遍执行完毕后就让第一个停下来在中间位置，换原来中间位置的第三个开始执行动画，\n     * 以此类推，当第二遍执行完毕后第二个停下来，中间位置的开始执行动画。\n     */\n    private int sweepIndex = 0;\n    /**\n     * 交换执行动画的颜色数组\n     */\n    private int[] colors = new int[]{getResources().getColor(R.color.colorYellow),\n            getResources().getColor(R.color.colorRed),\n            getResources().getColor(R.color.colorBlue)};\n\n    /**\n     * 动画所执行的最大偏移量（即中间点和最左边的距离）\n     */\n    private Float maxWidth = 200f;\n\n    /**\n     * 三个圆的半径\n     */\n    private Float radius = 30f;\n\n    /**\n     * 当前偏移的X坐标\n     */\n    private Float currentX = 0f;\n    /**\n     * 画笔\n     */\n    private Paint paint;\n    /**\n     * 属性动画\n     */\n    private ValueAnimator valueAnimator;\n\n    public BaiduProgressBar2(Context context) {\n        super(context);\n        startAnimator();\n    }\n\n    public BaiduProgressBar2(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        startAnimator();\n    }\n\n    public BaiduProgressBar2(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        startAnimator();\n    }\n\n    /**\n     * 用属性动画实现位移动画\n     */\n    private void startAnimator() {\n        valueAnimator = ValueAnimator.ofFloat(0f, maxWidth, 0);\n        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                currentX = (Float) animation.getAnimatedValue();\n                invalidate();\n            }\n        });\n        valueAnimator.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n                sweep(sweepIndex);\n            }\n        });\n        valueAnimator.setInterpolator(new LinearInterpolator());\n        paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        valueAnimator.setRepeatCount(-1);\n        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);\n        valueAnimator.setDuration(1000);\n        valueAnimator.start();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        int centerX = getWidth() / 2;\n        int centerY = getHeight() / 2;\n\n        /**画左边的圆**/\n        paint.setColor(colors[0]);\n        canvas.drawCircle(centerX - currentX, centerY, radius, paint);\n\n        /**画右边的圆**/\n        paint.setColor(colors[1]);\n        canvas.drawCircle(centerX + currentX, centerY, radius, paint);\n\n        /**画中间的圆**/\n        paint.setColor(colors[2]);\n        canvas.drawCircle(centerX, centerY, radius, paint);\n\n    }\n\n    /**\n     * 每次让先执行动画的目标和中间停止的动画目标交换\n     *\n     * @param a 最先执行的动画的索引\n     */\n    private void sweep(int a) {\n        int temp = colors[2];\n        colors[2] = colors[a];\n        colors[a] = temp;\n\n        if (a == 0) {\n            sweepIndex = 1;\n        } else {\n            sweepIndex = 0;\n        }\n    }\n\n    /**\n     * 在View销毁时停止动画\n     */\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        valueAnimator.cancel();\n    }\n}\n\n```\n\n\u003e在经过以上的动画之后，突然在[Loading设计思路分享](http://www.ui.cn/detail/73226.html)中看到了两个比较酷炫的动画\n主要思路图如下\n\n ![效果图](https://github.com/linglongxin24/BaiduProgressBar/blob/master/screenshots/demo.gif?raw=true)\n ![效果图](https://github.com/linglongxin24/BaiduProgressBar/blob/master/screenshots/sl1.jpg?raw=true)\n ![效果图](https://github.com/linglongxin24/BaiduProgressBar/blob/master/screenshots/sl2.gif?raw=true)\n \n#第三个：扔球动画-\u003e水平旋转动画\n\n```java\npackage cn.bluemobi.dylan.baiduprogressbar;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.animation.PropertyValuesHolder;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by dylan on 2016-12-04.\n */\n\npublic class BaiduLoadingView extends FrameLayout {\n    /**\n     * 存放三个小球的集合\n     */\n    private List\u003cImageView\u003e views = new ArrayList\u003c\u003e();\n    /**\n     * 同时播放动画的对象\n     */\n    private AnimatorSet animatorSet;\n\n    public BaiduLoadingView(Context context) {\n        super(context);\n        init();\n    }\n\n    public BaiduLoadingView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public BaiduLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void assignViews() {\n        ImageView iv_blue = (ImageView) findViewById(R.id.iv_blue);\n        ImageView iv_yellow = (ImageView) findViewById(R.id.iv_yellow);\n        ImageView iv_red = (ImageView) findViewById(R.id.iv_red);\n        views.add(iv_yellow);\n        views.add(iv_red);\n        views.add(iv_blue);\n    }\n\n    /**\n     * 初始化\n     */\n    private void init() {\n        LayoutInflater.from(getContext()).inflate(R.layout.baidu_progress_bar, this, true);\n        assignViews();\n        startAnimator();\n    }\n\n    private void startAnimator() {\n        /**动画组合-\u003e让左右同时执行**/\n        animatorSet = new AnimatorSet();\n        animatorSet.play(startAnimator1()).with(startAnimator2()).with(startAnimator3());\n        animatorSet.setInterpolator(new LinearInterpolator());\n        animatorSet.start();\n    }\n\n    private ObjectAnimator startAnimator1() {\n        /**对象的不同属性组合**/\n        PropertyValuesHolder objectAnimatorTranslation = PropertyValuesHolder.ofFloat(\"translationX\", -100, -200, -100, 0, 100, 200, 100, 0, -100);\n        PropertyValuesHolder objectAnimatorScale = PropertyValuesHolder.ofFloat(\"scaleX\", 0.5f, 1, 1.5f, 1, 0.5f, 1, 1.5f, 1, 0.5f);\n        PropertyValuesHolder objectAnimatorScaleY = PropertyValuesHolder.ofFloat(\"scaleY\", 0.5f, 1, 1.5f, 1, 0.5f, 1, 1.5f, 1, 0.5f);\n        /**同时操作对象的两个属性动画**/\n        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(views.get(0), objectAnimatorTranslation, objectAnimatorScale, objectAnimatorScaleY);\n        objectAnimator.setRepeatCount(-1);\n        objectAnimator.setInterpolator(new LinearInterpolator());\n        objectAnimator.setDuration(2000);\n        objectAnimator.start();\n        return objectAnimator;\n\n    }\n\n    private ObjectAnimator startAnimator2() {\n        /**对象的不同属性组合**/\n        PropertyValuesHolder objectAnimatorTranslation = PropertyValuesHolder.ofFloat(\"translationX\", 0, 100, 200, 100, 0, -100, -200, -100, 0);\n        PropertyValuesHolder objectAnimatorScale = PropertyValuesHolder.ofFloat(\"scaleX\", 1, 0.5f, 1, 1.5f, 1, 0.5f, 1, 1.5f, 1);\n        PropertyValuesHolder objectAnimatorScaleY = PropertyValuesHolder.ofFloat(\"scaleY\", 1, 0.5f, 1, 1.5f, 1, 0.5f, 1, 1.5f, 1);\n        /**同时操作对象的两个属性动画**/\n        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(views.get(1), objectAnimatorTranslation, objectAnimatorScale, objectAnimatorScaleY);\n        objectAnimator.setRepeatCount(-1);\n        objectAnimator.setInterpolator(new LinearInterpolator());\n        objectAnimator.setDuration(2000);\n        objectAnimator.start();\n        return objectAnimator;\n\n    }\n\n    private ObjectAnimator startAnimator3() {\n        /**对象的不同属性组合**/\n        PropertyValuesHolder objectAnimatorTranslation = PropertyValuesHolder.ofFloat(\"translationX\", 100, 0, -100, -200, -100, 0, 100, 200, 100);\n        PropertyValuesHolder objectAnimatorScale = PropertyValuesHolder.ofFloat(\"scaleX\", 1.5f, 1f, 0.5f, 1, 1.5f, 1, 0.5f, 1, 1.5f);\n        PropertyValuesHolder objectAnimatorScaleY = PropertyValuesHolder.ofFloat(\"scaleY\", 1.5f, 1f, 0.5f, 1, 1.5f, 1, 0.5f, 1, 1.5f);\n        /**同时操作对象的两个属性动画**/\n        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(views.get(2), objectAnimatorTranslation, objectAnimatorScale, objectAnimatorScaleY);\n        objectAnimator.setRepeatCount(-1);\n        objectAnimator.setInterpolator(new LinearInterpolator());\n        objectAnimator.setDuration(2000);\n        objectAnimator.start();\n        return objectAnimator;\n    }\n\n    /**\n     * 在View销毁时停止动画\n     */\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        animatorSet.cancel();\n    }\n}\n\n```\n\n\n#第四个：扔球动画-\u003e垂直旋转动画\n\n```java\npackage cn.bluemobi.dylan.baiduprogressbar;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.animation.PropertyValuesHolder;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.PointF;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by dylan on 2016-12-04.\n */\n\npublic class BaiduProgressLoading extends FrameLayout {\n\n    /**\n     * 存放三个小球的集合\n     */\n    private List\u003cImageView\u003e views = new ArrayList\u003c\u003e();\n\n    public BaiduProgressLoading(Context context) {\n        super(context);\n        init();\n    }\n\n    public BaiduProgressLoading(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public BaiduProgressLoading(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void assignViews() {\n        ImageView iv_blue = (ImageView) findViewById(R.id.iv_blue);\n        ImageView iv_yellow = (ImageView) findViewById(R.id.iv_yellow);\n        ImageView iv_red = (ImageView) findViewById(R.id.iv_red);\n        views.add(iv_yellow);\n        views.add(iv_red);\n        views.add(iv_blue);\n    }\n\n    private void init() {\n        LayoutInflater.from(getContext()).inflate(R.layout.baidu_progress_bar, this, true);\n        assignViews();\n        startAnimator1();\n        startAnimator2();\n        startAnimator3();\n    }\n\n    PointF point = new PointF();\n\n    private void startAnimator1() {\n        ValueAnimator valueAnimator = ValueAnimator.ofFloat(90, 360);\n        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 - 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(0).setTranslationX(point.x);\n                views.get(0).setTranslationY(point.y);\n            }\n        });\n        valueAnimator.setInterpolator(new LinearInterpolator());\n        valueAnimator.setDuration(750);\n        valueAnimator.start();\n\n        ValueAnimator valueAnimator2 = ValueAnimator.ofFloat(180, 0, -180);\n        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 + 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(0).setTranslationX(point.x);\n                views.get(0).setTranslationY(point.y);\n            }\n        });\n        valueAnimator2.setInterpolator(new LinearInterpolator());\n        valueAnimator2.setDuration(1000);\n        valueAnimator2.setStartDelay(750);\n        valueAnimator2.start();\n\n\n        ValueAnimator valueAnimator3 = ValueAnimator.ofFloat(0, 90);\n        valueAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 - 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(0).setTranslationX(point.x);\n                views.get(0).setTranslationY(point.y);\n            }\n        });\n        valueAnimator3.setInterpolator(new LinearInterpolator());\n        valueAnimator3.setDuration(250);\n        valueAnimator3.setStartDelay(1750);\n        valueAnimator3.start();\n        valueAnimator3.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                startAnimator1();\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n\n            }\n        });\n    }\n\n    private void startAnimator2() {\n\n        ValueAnimator valueAnimator2 = ValueAnimator.ofFloat(180, 0, -180);\n        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 + 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(1).setTranslationX(point.x);\n                views.get(1).setTranslationY(point.y);\n            }\n        });\n        valueAnimator2.setInterpolator(new LinearInterpolator());\n        valueAnimator2.setDuration(1000);\n        valueAnimator2.start();\n\n        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 360);\n        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 - 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(1).setTranslationX(point.x);\n                views.get(1).setTranslationY(point.y);\n            }\n        });\n        valueAnimator.setInterpolator(new LinearInterpolator());\n        valueAnimator.setDuration(1000);\n        valueAnimator.setStartDelay(1000);\n        valueAnimator.start();\n        valueAnimator.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                startAnimator2();\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n\n            }\n        });\n    }\n\n    private void startAnimator3() {\n\n        ValueAnimator valueAnimator = ValueAnimator.ofFloat(270, 180);\n        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 + 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(2).setTranslationX(point.x);\n                views.get(2).setTranslationY(point.y);\n            }\n        });\n        valueAnimator.setInterpolator(new LinearInterpolator());\n        valueAnimator.setDuration(250);\n        valueAnimator.start();\n\n        ValueAnimator valueAnimator2 = ValueAnimator.ofFloat(0, 360);\n        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 - 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(2).setTranslationX(point.x);\n                views.get(2).setTranslationY(point.y);\n            }\n        });\n        valueAnimator2.setInterpolator(new LinearInterpolator());\n        valueAnimator2.setDuration(1000);\n        valueAnimator2.setStartDelay(250);\n        valueAnimator2.start();\n\n\n        ValueAnimator valueAnimator3 = ValueAnimator.ofFloat(180, -90);\n        valueAnimator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                float value = (Float) animation.getAnimatedValue();\n                /**\n                 * ﻿﻿\n                 * 圆点坐标：(x0,y0)\n                 * 半径：r\n                 * 角度：a0\n                 * 则圆上任一点为：（x1,y1）\n                 * x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )\n                 * y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )\n                 */\n                /**第四步，根据每个菜单真实角度计算其坐标值**/\n                point.x = (float) Math.cos(value * (Math.PI / 180)) * 100 + 100;\n                point.y = (float) -Math.sin(value * (Math.PI / 180)) * 100;\n                views.get(2).setTranslationX(point.x);\n                views.get(2).setTranslationY(point.y);\n            }\n        });\n        valueAnimator3.setInterpolator(new LinearInterpolator());\n        valueAnimator3.setDuration(750);\n        valueAnimator3.setStartDelay(1250);\n        valueAnimator3.start();\n        valueAnimator3.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                startAnimator3();\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n\n            }\n        });\n    }\n}\n\n```\n\n#[GitHub](https://github.com/linglongxin24/BaiduProgressBar)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinglongxin24%2FBaiduProgressBar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinglongxin24%2FBaiduProgressBar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinglongxin24%2FBaiduProgressBar/lists"}