【问题标题】:How to animate a View around a circle?如何围绕圆圈为视图设置动画?
【发布时间】:2015-11-11 16:09:36
【问题描述】:

我想将一个无限移动动画围绕一个圆圈设置为View,如下图所示。我该如何实现?

此视图可能是TextView 或任何简单或复杂的视图。

我知道我可以将视图放在ViewGroup 中并设置旋转动画,但我不想旋转视图,因为视图的方向很重要。

【问题讨论】:

    标签: android animation


    【解决方案1】:

    我不知道有什么本机可以做到这一点,所以我认为你最好的方法是确定你的动画路径(xy 坐标)并创建一个Handler 到重复postDelayed。您的处理程序触发的每次迭代都只会translate 视图到路径中的下一个点。

    编辑:我已经创建了一个解决方案,并通过左右移动和绕圈移动对其进行了测试。

    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<Integer, PathRunnable> 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> view;
            Pair<Float, Float>[] 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<Float, Float> 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<Float, Float>[] getPoints(Path path)
            {
                PathMeasure pathMeasure = new PathMeasure(path, true);
                int         frames      = (int) pathMeasure.getLength();
    
                Pair<Float, Float>[] 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;
            }
        }
    }
    

    现在可以给它一个 Graphics 路径来制作动画,就像这样

    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);
    

    【讨论】:

    • 我认为应该有一种更简单的方法来实现这一点。我的意思是,如果路径比圆更复杂怎么办?
    • 没错,但对于更复杂的路径,您可能会指定路径上的点。使用圆圈,您至少可以以编程方式计算路径。
    • 这听起来像是一个很好的解决方案,但我无法让它发挥作用。它像随机摇晃一样在其位置随机动画。我增加了圆形收音机,但没有运气
    • 奇怪,我刚刚在 Marshmallow 模拟器和 Jellybean 设备上再次尝试过。两者都在工作。您是否正在进行任何翻译、调整大小、重新布局?
    • 让我在一个新项目上尝试一下以避免其他冲突。
    【解决方案2】:

    我提出的这个解决方案解决了这个问题,顺时针旋转视图容器,逆时针旋转TextView :)

    布局:

    <RelativeLayout
        android:id="@+id/cont"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="150dp"
        android:animateLayoutChanges="true"
        android:clipChildren="false">
    
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="M"
            android:textSize="60sp"/>
    </RelativeLayout>
    

    动画:

        View v = findViewById(R.id.cont);
        View text = findViewById(R.id.text);
    
        int durationMillis = 10000;
    
        RotateAnimation r = new RotateAnimation(0, 360,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        r.setDuration(durationMillis);
        r.setInterpolator(new LinearInterpolator());
        r.setRepeatMode(Animation.RESTART);
        r.setRepeatCount(Animation.INFINITE);
        text.startAnimation(r);
    
        RotateAnimation rC = new RotateAnimation(360, 0,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rC.setDuration(durationMillis);
        rC.setInterpolator(new LinearInterpolator());
        rC.setRepeatMode(Animation.RESTART);
        rC.setRepeatCount(Animation.INFINITE);
        v.startAnimation(rC);
    

    但是如果您想在其他路径中移动对象并寻找通用解决方案,请查看@Kevin 的解决方案(感谢他)。

    【讨论】:

      【解决方案3】:

      可能是ValueAnimator可以帮忙吗?根据它动画的进度,你可以计算你的路径坐标并将它设置到你的视图吗?

      【讨论】:

      • 您能在此处粘贴样本吗?我对动画不熟悉。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-19
      • 2013-06-19
      • 1970-01-01
      • 2021-06-24
      • 2019-09-04
      相关资源
      最近更新 更多