在Android中为片段事务设置“z”索引或相机的翻转动画

8
我正在尝试使用以下代码对两个片段之间的交易进行动画处理: http://developer.android.com/training/animation/cardflip.html 但是结果却是这样的: http://developer.android.com/training/animation/anim_card_flip.mp4 然而,我想要的效果是这样的: https://www.youtube.com/watch?v=52mXHqX9f3Y 区别在于,尽管两者都旋转了180度,但第二个使用了不同的相机(Z轴)。
所以问题是:
  • 我可以将Z-Index应用于对象动画吗?
  • 或者,我可以提供一个动画类来代替包含动画的XML文件,以动画化片段转换吗?
谢谢。
编辑:查看差异。 enter image description here

为什么不直接使用那个YouTube片段中引用的github上的代码?http://genzeb.github.io/flip/ - Richard Le Mesurier
它正在使用一个Animation对象,据我所知,我不能将其与片段转换一起使用。 - Reinherd
很难解释。当视图翻转时,这一瞬间被捕捉在这两张图片上。左边是本帖回答中的示例。右边是iOS动画。正如您所看到的,左边的角度从未像iOS示例那样深。 - Reinherd
请在下面的答案中检查YouTube链接。 - Reinherd
啊哈 - 你应该把这样的链接放到你的问题中,这样会更容易跟进。祝你好运,我期待着阅读A的最终解决方案。 - Richard Le Mesurier
2个回答

9
为了实现您想要的效果,在您的动画师中,您需要完成以下两个步骤:
  • 使用非默认的枢轴(位于视图中间)旋转视图
  • 在旋转时平移视图
在这两种情况下,您需要知道您的视图大小,因此我建议创建自定义布局组件用作片段的根,公开一组可使用不同的 objectanimator 在xml中进行动画处理的属性。 该组件应如下所示:
public class FlippableLayout extends FrameLayout {

    private FlipEvaluator flipRightInEvaluator;
    private FlipEvaluator flipRightOutEvaluator;
    private FlipEvaluator flipLeftInEvaluator;
    private FlipEvaluator flipLeftOutEvaluator;

    public FlippableLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public FlippableLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setCameraDistance(getCameraDistance() * 10); // reduces perspective skewing
        flipRightInEvaluator = new FlipEvaluator(
                1f, .5f, // pivotX/pivotY
                -1f, 0f, // translationX start/end
                -180, 0, // rotationY start/end
                0f, 1f); // alpha start/end
        flipRightOutEvaluator = new FlipEvaluator(
                0f, .5f,
                0f, 1f,
                0, 180,
                1f, 0f);
        flipLeftInEvaluator = new FlipEvaluator(
                .0f, .5f,
                1f, 0f,
                180, 0,
                0f, 1f);
        flipLeftOutEvaluator = new FlipEvaluator(
                1f, .5f,
                0f, -1f,
                0, -180,
                1f, 0f);
    }

    public void setFlipRightIn(float value) {
        evaluateUsing(flipRightInEvaluator, value);
    }

    public void setFlipRightOut(float value) {
        evaluateUsing(flipRightOutEvaluator, value);
    }

    public void setFlipLeftIn(float value) {
        evaluateUsing(flipLeftInEvaluator, value);
    }

    public void setFlipLeftOut(float value) {
        evaluateUsing(flipLeftOutEvaluator, value);
    }

    private void evaluateUsing(FlipEvaluator evaluator, float value) {
        float cappedValue = Math.min(1f, Math.max(0f, value));
        setPivotX(getWidth() * evaluator.getPivotX());
        setPivotY(getHeight() * evaluator.getPivotY());
        setAlpha(evaluator.getAlpha(cappedValue));
        setTranslationX(getWidth() * evaluator.getTranslationX(cappedValue));
        setRotationY(evaluator.getRotationY(cappedValue));
    }

    private static class FlipEvaluator {
        private final float pivotX;
        private final float pivotY;
        private final float startTranslationX;
        private final float endTranslationY;
        private final float startRotationY;
        private final float endRotationY;
        private final float startAlpha;
        private final float endAlpha;

        /**
         * Simple evaluator holding all the start/end values for a flip animation.
         *
         * @param pivotX value between 0 and 1, where 0 is the left border and 1 is the right border of the target
         * @param pivotY value between 0 and 1, where 0 is the top border and 1 is the bottom border of the target
         * @param startTranslationX value between 0 and 1, where 1 is the width of the target
         * @param endTranslationY value between 0 and 1, where 1 is the width of the target
         * @param startRotationY value between -180 and 180
         * @param endRotationY value between -180 and 180
         * @param startAlpha initial alpha
         * @param endAlpha final alpha
         */
        private FlipEvaluator(float pivotX, float pivotY,
                              float startTranslationX, float endTranslationY,
                              float startRotationY, float endRotationY,
                              float startAlpha, float endAlpha) {
            this.pivotX = pivotX;
            this.pivotY = pivotY;
            this.startTranslationX = startTranslationX;
            this.endTranslationY = endTranslationY;
            this.startRotationY = startRotationY;
            this.endRotationY = endRotationY;
            this.startAlpha = startAlpha;
            this.endAlpha = endAlpha;
        }

        public float getPivotX() {
            return pivotX;
        }

        public float getPivotY() {
            return pivotY;
        }

        public float getTranslationX(float t) {
            return startTranslationX + (endTranslationY - startTranslationX) * t;
        }

        public float getRotationY(float t) {
            return startRotationY + (endRotationY - startRotationY) * t;
        }

        public float getAlpha(float t) {
            return t < .5f ? startAlpha : endAlpha;
        }

    }

}

您的动画文件将会看起来像这样:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:valueFrom="0"
        android:valueTo="1"
        android:propertyName="flipLeftIn"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="1000" />

当然,你可以将flipLeftIn替换为flipLeftOutflipRightInflipRightOut,以便将动画应用到不同的属性上。

在你的Activity中,你可以像往常一样在片段事务中设置自定义动画,指定之前定义的XML文件:

    ....
    getFragmentManager()
            .beginTransaction()
            .setCustomAnimations(
                    R.animator.card_flip_right_in, R.animator.card_flip_right_out,
                    R.animator.card_flip_left_in, R.animator.card_flip_left_out)
    ....

翻转示例

另一种方法是完全在XML中处理,但是使用XML定义的维度值设置枢轴/平移不如上面显示的解决方案具有可扩展性。

编辑 要减少相机距离,您可以在API>12上轻松使用View.setCameraDistance()。我更新了包括此更改的代码片段。

更少扭曲的翻转


请查看我对另一个答案的评论。 - Reinherd
在我开始回答之前,我当然会检查下面链接的视频,而我发布的解决方案提供了与 ~3:10 显示的相同效果。您可以看到视图的垂直边缘不像 Vikram 的解决方案那样沿 Z 轴“推动”,而是始终保持在视口的边缘上。也许您想更好地查看发布的 gif,并尝试自己编写代码。 - a.bertucci
你好。这已经相当不错了,但请检查一下我在问题中的编辑。感谢你的努力。 - Reinherd
1
现在我明白了,谢谢。你想要的是减少透视引起的倾斜效果。不幸的是,我之前也遇到过同样的问题,并没有找到任何改变Camera类使用深度的方法。最终我采用了自己实现倾斜的方式,使用了Matrix工具。我会尝试修改我的解决方案来应对这个问题。 - a.bertucci
1
原来有一种方法可以调整相机距离 http://developer.android.com/reference/android/view/View.html#setCameraDistance(float) 但需要API 12+。我会相应地更新我的答案。 - a.bertucci
设置相机距离就是答案。 - Reinherd

1
我尝试按照a.bertucci的回答进行操作,但动画不起作用,我不知道为什么...我是这个世界上的新手,很难理解关于自定义FlippableFrameLayout正在发生什么。
但受他在相机距离方面的回答的启发,我发现我只需要在比例属性上再加上一个动画。(即scaleX和scaleY)
因此,除了官方教程中的其他内容保持不变之外,您只需要修改动画器xml文件即可实现所需的效果:
对于left_out.xml和right_out.xml,请添加以下内容:
    <objectAnimator
    android:valueFrom="1f"
    android:valueTo="0.5f"
    android:propertyName="scaleX"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:duration="@integer/card_flip_time_half" />

<objectAnimator
    android:valueFrom="1f"
    android:valueTo="0.5f"
    android:propertyName="scaleY"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:duration="@integer/card_flip_time_half" />

对于 left_in.xmlright_in.xml添加以下内容:

    <objectAnimator
    android:valueFrom="0.5f"
    android:valueTo="1.0f"
    android:propertyName="scaleX"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:startOffset="@integer/card_flip_time_half"
    android:duration="@integer/card_flip_time_half" />

<objectAnimator
    android:valueFrom="0.5f"
    android:valueTo="1.0f"
    android:propertyName="scaleY"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:startOffset="@integer/card_flip_time_half"
    android:duration="@integer/card_flip_time_half" />

当然,你可以在这里修改valueFrom和valueTo的值(我使用了0.5f),以调整“相机距离”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接