Я не думаю, что с ObjectAnimator можно что-либо сделать, потому что, как представляется, нет функции, которая может быть вызвана для утверждения относительной продолжительности определенного фрагмента анимации.
Я разработал нечто похожее на то, что вам нужно некоторое время назад, но оно работает несколько иначе - оно наследуется от анимации.
Я изменил все, чтобы работать с вашими изгибающими потребностями, и с классом PathPoint.
Вот обзор:
Я поставляю список точек для анимации в конструкторе.
Я вычислил длину между всеми точками, используя простой калькулятор расстояния. Затем я суммирую все, чтобы получить общую длину пути, и сохраните длины сегментов в карте для будущего использования (это для повышения эффективности во время выполнения).
При анимации я использую текущее интерполяционное время, чтобы выяснить, какие 2 очка я оживляю между ними, учитывая отношение времени & отношение пройденного расстояния.
Я подсчитываю время, необходимое для анимации между этими двумя точками в соответствии с относительным расстоянием между ними по сравнению с общим расстоянием.
Затем я интерполирую отдельно между этими двумя точками, используя вычисления в классе PathAnimator.
Вот код:
CurveAnimation.java:
public class CurveAnimation extends Animation
{
private static final float BEZIER_LENGTH_ACCURACY = 0.001f; // Must be divisible by one. Make smaller to improve accuracy, but will increase runtime at start of animation.
private List<PathPoint> mPathPoints;
private float mOverallLength;
private Map<PathPoint, Double> mSegmentLengths = new HashMap<PathPoint, Double>(); // map between the end point and the length of the path to it.
public CurveAnimation(List<PathPoint> pathPoints)
{
mPathPoints = pathPoints;
if (mPathPoints == null || mPathPoints.size() < 2)
{
Log.e("CurveAnimation", "There must be at least 2 points on the path. There will be an exception soon!");
}
calculateOverallLength();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
PathPoint[] startEndPart = getStartEndForTime(interpolatedTime);
PathPoint startPoint = startEndPart[0];
PathPoint endPoint = startEndPart[1];
float startTime = getStartTimeOfPoint(startPoint);
float endTime = getStartTimeOfPoint(endPoint);
float progress = (interpolatedTime - startTime)/(endTime - startTime);
float x, y;
float[] xy;
if (endPoint.mOperation == PathPoint.CURVE)
{
xy = getBezierXY(startPoint, endPoint, progress);
x = xy[0];
y = xy[1];
}
else if (endPoint.mOperation == PathPoint.LINE)
{
x = startPoint.mX + progress * (endPoint.mX - startPoint.mX);
y = startPoint.mY + progress * (endPoint.mY - startPoint.mY);
}
else
{
x = endPoint.mX;
y = endPoint.mY;
}
t.getMatrix().setTranslate(x, y);
super.applyTransformation(interpolatedTime, t);
}
private PathPoint[] getStartEndForTime(float time)
{
double length = 0;
if (time == 1)
{
return new PathPoint[] { mPathPoints.get(mPathPoints.size() - 2), mPathPoints.get(mPathPoints.size() - 1) };
}
PathPoint[] result = new PathPoint[2];
for (int i = 0; i < mPathPoints.size() - 1; i++)
{
length += calculateLengthFromIndex(i);
if (length/mOverallLength >= time)
{
result[0] = mPathPoints.get(i);
result[1] = mPathPoints.get(i + 1);
break;
}
}
return result;
}
private float getStartTimeOfPoint(PathPoint point)
{
float result = 0;
int index = 0;
while (mPathPoints.get(index) != point && index < mPathPoints.size() - 1)
{
result += (calculateLengthFromIndex(index)/mOverallLength);
index++;
}
return result;
}
private void calculateOverallLength()
{
mOverallLength = 0;
mSegmentLengths.clear();
double segmentLength;
for (int i = 0; i < mPathPoints.size() - 1; i++)
{
segmentLength = calculateLengthFromIndex(i);
mSegmentLengths.put(mPathPoints.get(i + 1), segmentLength);
mOverallLength += segmentLength;
}
}
private double calculateLengthFromIndex(int index)
{
PathPoint start = mPathPoints.get(index);
PathPoint end = mPathPoints.get(index + 1);
return calculateLength(start, end);
}
private double calculateLength(PathPoint start, PathPoint end)
{
if (mSegmentLengths.containsKey(end))
{
return mSegmentLengths.get(end);
}
else if (end.mOperation == PathPoint.LINE)
{
return calculateLength(start.mX, end.mX, start.mY, end.mY);
}
else if (end.mOperation == PathPoint.CURVE)
{
return calculateBezeirLength(start, end);
}
else
{
return 0;
}
}
private double calculateLength(float x0, float x1, float y0, float y1)
{
return Math.sqrt(((x0 - x1) * (x0 - x1)) + ((y0 - y1) * (y0 - y1)));
}
private double calculateBezeirLength(PathPoint start, PathPoint end)
{
double result = 0;
float x, y, x0, y0;
float[] xy;
x0 = start.mX;
y0 = start.mY;
for (float progress = BEZIER_LENGTH_ACCURACY; progress <= 1; progress += BEZIER_LENGTH_ACCURACY)
{
xy = getBezierXY(start, end, progress);
x = xy[0];
y = xy[1];
result += calculateLength(x, x0, y, y0);
x0 = x;
y0 = y;
}
return result;
}
private float[] getBezierXY(PathPoint start, PathPoint end, float progress)
{
float[] result = new float[2];
float oneMinusT, x, y;
oneMinusT = 1 - progress;
x = oneMinusT * oneMinusT * oneMinusT * start.mX +
3 * oneMinusT * oneMinusT * progress * end.mControl0X +
3 * oneMinusT * progress * progress * end.mControl1X +
progress * progress * progress * end.mX;
y = oneMinusT * oneMinusT * oneMinusT * start.mY +
3 * oneMinusT * oneMinusT * progress * end.mControl0Y +
3 * oneMinusT * progress * progress * end.mControl1Y +
progress * progress * progress * end.mY;
result[0] = x;
result[1] = y;
return result;
}
}
Вот пример, который показывает, как активировать анимацию:
private void animate()
{
AnimatorPath path = new AnimatorPath();
path.moveTo(0, 0);
path.lineTo(0, 300);
path.curveTo(100, 0, 300, 900, 400, 500);
CurveAnimation animation = new CurveAnimation(path.mPoints);
animation.setDuration(5000);
animation.setInterpolator(new LinearInterpolator());
btn.startAnimation(animation);
}
Теперь, имейте в виду, что В настоящее время я вычисляю длину кривой в соответствии с приближением.Это, очевидно, вызовет некоторые неточности в скорости. Если вы считаете, что это недостаточно точно, не стесняйтесь изменять код. Кроме того, если вы хотите увеличить точность длины кривой, попробуйте уменьшить значение BEZIER_LENGTH_ACCURACY. Он должен быть делимым на 1, поэтому принятые значения могут быть 0,001, 0,000025 и т. Д.
Хотя вы можете заметить небольшие колебания скорости при использовании кривых, я уверен, что это намного лучше, чем просто делить время поровну между всеми пути.
Я надеюсь, что это помогает :)
Это отличная идея, я буду смотреть в Безье длины кривой известково и попытаться и улучшить его , Логика была тем, что я хотел, но код не соответствовал моим требованиям, так как я не анимировал представление, но использовал ObjectAnimator.ofObject. Я вознагражу вас щедростью и добавлю код, который я написал, который основан на вашем ответе в качестве принятого ответа для других пользователей. Спасибо огромное! – marmor
приветствует человека! рад, что я мог бы помочь :) –