How to animate a View around a circle?

前端 未结 3 1863
生来不讨喜
生来不讨喜 2020-12-29 15:11

I want to set an infinite move animation around a circle to a View like picture below.How can i achieve this?

This View may be a TextView o

3条回答
  •  灰色年华
    2020-12-29 15:38

    I am not aware of anything to natively do this, so I think your best approach here would be to determine the path of your animation (the xy coordinates) and create a Handler to repeatedly postDelayed. Each iteration of your handler firing will just translate the view to its next point in the path.

    Edit: I've create a solution and tested it with both moving right and left, and moving in a circle.

    ViewPathAnimator.java:

    import android.graphics.Path;
    import android.graphics.PathMeasure;
    import android.os.Handler;
    import android.util.Pair;
    import android.view.View;
    
    import java.lang.ref.WeakReference;
    import java.util.HashMap;
    
    public class ViewPathAnimator
    {
        public static final int DEFAULT_DELAY     = 1000 / 10;
        public static final int DEFAULT_FRAMESKIP = 3;
    
        private static Handler                        handler;
        private static HashMap animatedViews;
    
        public static void animate(View view, Path path)
        {
            animate(view, path, DEFAULT_DELAY, DEFAULT_FRAMESKIP);
        }
    
        public static void animate(View view, Path path, int delay)
        {
            animate(view, path, delay, DEFAULT_FRAMESKIP);
        }
    
        public static void animate(View view, Path path, int delay, int frameSkip)
        {
            if (animatedViews == null)
            {
                animatedViews = new HashMap<>();
            }
    
            if (handler == null)
            {
                handler = new Handler();
            }
    
            if (animatedViews.containsKey(view.hashCode()))
            {
                cancel(view);
            }
    
            PathRunnable runnable = new PathRunnable(view, path, delay, frameSkip);
            animatedViews.put(view.hashCode(), runnable);
            handler.postDelayed(runnable, delay);
        }
    
        public static void cancel(View view)
        {
            if (animatedViews != null && handler != null)
            {
                PathRunnable task = animatedViews.get(view.hashCode());
                if (task != null)
                {
                    handler.removeCallbacks(task);
                    animatedViews.remove(view.hashCode());
                }
            }
        }
    
        private static class PathRunnable implements Runnable
        {
            private WeakReference view;
            Pair[] points;
            private int delay;
            private int frameSkip;
            private int frame;
    
            PathRunnable(View view, Path path, int delay, int frameSkip)
            {
                this.view = new WeakReference<>(view);
                this.points = getPoints(path);
                this.delay = delay;
                this.frameSkip = Math.max(frameSkip, 0);
                this.frame = 0;
            }
    
            @Override
            public void run()
            {
                frame = (frame + frameSkip + 1) % points.length;
                Pair pair = points[frame];
    
                View v = view.get();
                if (v != null)
                {
                    v.setTranslationX(pair.first);
                    v.setTranslationY(pair.second);
    
                    handler.postDelayed(this, delay);
                }
            }
    
            // https://stackoverflow.com/questions/7972780/how-do-i-find-all-the-points-in-a-path-in-android
            private Pair[] getPoints(Path path)
            {
                PathMeasure pathMeasure = new PathMeasure(path, true);
                int         frames      = (int) pathMeasure.getLength();
    
                Pair[] pointArray   = new Pair[frames];
                float                length       = pathMeasure.getLength();
                float                distance     = 0f;
                float                speed        = length / pointArray.length;
                int                  counter      = 0;
                float[]              aCoordinates = new float[2];
    
                while ((distance < length) && (counter < pointArray.length))
                {
                    // get point from the path
                    pathMeasure.getPosTan(distance, aCoordinates, null);
                    pointArray[counter] = new Pair<>(aCoordinates[0], aCoordinates[1]);
                    counter++;
                    distance = distance + speed;
                }
    
                return pointArray;
            }
        }
    }
    

    This can now be given a Graphics path to animate along, like this

    View view = findViewById(R.id.text);
    Path path = new Path();
    path.addCircle(0, 0, 100, Path.Direction.CW);
    
    ViewPathAnimator.animate(view, path, 1000/ 30, 2);
    

提交回复
热议问题