从起始位置缩放到最终位置

Scale from start rect to final position

我正在尝试将视图从起始矩形(例如,由另一个视图定义)缩放到它的最终位置。

我尝试使用以下代码来设置看起来很直接的动画:

float scaleX = 0f;
float scaleY = 0f;
Rect startRect = new Rect(10, 10, 100, 100); // taken from real view position with getLocationOnScreen
final Collection<Animator> animators = new ArrayList<>();

if (animatedView.getMeasuredHeight() != 0) {
    scaleX = (float)startRect.width() / animatedView.getMeasuredWidth();
}

if (animatedView.getMeasuredHeight() != 0) {
    scaleY = (float)startRect.height() / animatedView.getMeasuredHeight();
}

animatedView.getLocationInWindow(location);
animatedView.setPivotX(startRect.left);
animatedView.setPivotY(startRect.top);
animatedView.setScaleX(scaleX);
animatedView.setScaleY(scaleY);

animators.add(ObjectAnimator.ofFloat(animatedView, View.SCALE_X, 1.0f).setDuration(1000));
animators.add(ObjectAnimator.ofFloat(animatedView, View.SCALE_Y, 1.0f).setDuration(1000));

animatedViewRelativeLayout的child(布局参数设置在布局的某些标题视图下方)并且测量的宽度和高度以及位置是动画时的有效值设置。

根据 startRect,我观察到不同的动画 - 有时动画视图显示在 startRect 下方或上方。

似乎 RectEvaluator 是一种可能的解决方案,但它只能从 API 18.

从开始矩形位置到最终(未修改的)动画视图的正确方法是什么?

根据对该问题的评论,可以从 Android 源复制 RectEvaluator 代码,然后应用以下逻辑:

RectViewAnimator mRectAnimator;

/**
 * Creates animator which can be played. From some start position
 * to final (real position).
 * From final position to start position can be achieved using reverse interpolation.
 */
private Collection<Animator> createMoveAnimators(View targetView, Rect startRect) {
    final Collection<Animator> animators = new ArrayList<>();
    final int[] location = new int[2];

    targetView.getLocationOnScreen(location);

    final Rect finalRect = new Rect(location[0], location[1],
            location[0] + targetView.getMeasuredWidth(),
            location[1] + targetView.getMeasuredHeight());

    // Must keep this reference during animations, since Animator keeps only WeakReference to it's targets.
    mRectAnimator = appendRectEvaluatorAnimation(animators, targetView, 500, startRect, finalRect);

    return animators;
}

private RectViewAnimator appendRectEvaluatorAnimation(final Collection<Animator> animators, final View view, final int duration,
                                                      final Rect startRect, final Rect finalRect) {
    final float scaleX = (float) startRect.width() / finalRect.width();
    final float scaleY = (float) startRect.height() / finalRect.height();

    view.setTranslationY(startRect.top - (finalRect.top + (1 - scaleY) * finalRect.height() / 2));
    view.setTranslationX(startRect.left - (finalRect.left + (1 - scaleX) * finalRect.width() / 2));
    view.setScaleX(scaleX);
    view.setScaleY(scaleY);

    final RectViewAnimator rectViewAnimator = new RectViewAnimator(view, finalRect);
    final Animator animator = ObjectAnimator.ofObject(rectViewAnimator, RectViewAnimator.RECT,
            new RectEvaluator(), startRect, finalRect);

    animators.add(animator);
    return rectViewAnimator;
}

private static class RectViewAnimator {
    static final String RECT = "rect";

    private final View target;
    private final Rect finalRect;

    RectViewAnimator(final View target, final Rect finalRect) {
        this.target = target;
        this.finalRect = finalRect;
    }

    @Keep
    public void setRect(final Rect r) {
        final float scaleX = (float)r.width() / finalRect.width();
        final float scaleY = (float)r.height() / finalRect.height();

        target.setScaleX(scaleX);
        target.setScaleY(scaleY);
        target.setTranslationX(r.left - (finalRect.left + (1 - scaleX) * finalRect.width() / 2));
        target.setTranslationY(r.top - (finalRect.top + (1 - scaleY) * finalRect.height() / 2));
    }
}