【发布时间】:2015-11-11 16:09:36
【问题描述】:
我想将一个无限移动动画围绕一个圆圈设置为View,如下图所示。我该如何实现?
此视图可能是TextView 或任何简单或复杂的视图。
我知道我可以将视图放在ViewGroup 中并设置旋转动画,但我不想旋转视图,因为视图的方向很重要。
【问题讨论】:
我想将一个无限移动动画围绕一个圆圈设置为View,如下图所示。我该如何实现?
此视图可能是TextView 或任何简单或复杂的视图。
我知道我可以将视图放在ViewGroup 中并设置旋转动画,但我不想旋转视图,因为视图的方向很重要。
【问题讨论】:
我不知道有什么本机可以做到这一点,所以我认为你最好的方法是确定你的动画路径(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);
【讨论】:
我提出的这个解决方案解决了这个问题,顺时针旋转视图容器,逆时针旋转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 的解决方案(感谢他)。
【讨论】:
可能是ValueAnimator可以帮忙吗?根据它动画的进度,你可以计算你的路径坐标并将它设置到你的视图吗?
【讨论】: