【问题标题】:How can I animate an elevation change as part of a scene change animation/transition?作为场景更改动画/过渡的一部分,我如何为高程变化设置动画?
【发布时间】:2017-06-20 09:49:49
【问题描述】:

在我的 Android 应用中,我需要实现一个场景转换,该转换涉及改变其中一个元素的高度(z-index)。正如您在下图中左侧开始场景中看到的那样,蓝色弧线显示在狗的图像下方。在右侧显示的最终过渡中,狗的图像显示在蓝色弧线下方。我的愿望是首先开始图像的 changeBounds 过渡,然后稍晚进行圆弧的 changeBounds 过渡。在过渡的大约一半处,图像的底部应位于圆弧上方。在这个中间点,我希望图像的高程/z-index 发生变化,以便狗图像显示在蓝色弧线下方。

我目前已将我的应用主题配置为使用以下 transitionSet 进行此场景更改。

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="together"
    android:duration="2000">

    <changeBounds
        android:startDelay="0"
        android:duration="1000"
        android:resizeClip="false"
        >
        <targets>
            <target android:targetId="@id/charlie"/>
        </targets>
    </changeBounds>

    <transition class="com.motb.transitiontest.ElevationTransition">
        android:startDelay="0"
        android:duration="1000"
        <targets>
            <target android:targetId="@id/charlie"/>
            <target android:targetId="@id/arc1"/>
        </targets>
    </transition>


    <changeBounds
        android:startDelay="300"
        android:duration="1000"
        >
        <targets>
            <target android:targetId="@id/arc1"/>
            <target android:targetId="@id/helloText" />
        </targets>
    </changeBounds>

</transitionSet>

我正在尝试使用如下所示的自定义“ElevationTransition”更改海拔:

public class ElevationTransition extends Transition {

public static final String TAG = "ElevationTransition" ;

public ElevationTransition(Context context, AttributeSet attributes ) {
    super( context, attributes );
}

private static final String PROPNAME_TRANS_Z = "com.motb:transition:transz";
@Override
public void captureStartValues(TransitionValues transitionValues) {
    float translationZ = transitionValues.view.getTranslationZ() ;
    transitionValues.values.put(PROPNAME_TRANS_Z + "_start" , translationZ );
}

@Override
public void captureEndValues(TransitionValues transitionValues) {
    float translationZ = transitionValues.view.getTranslationZ() ;
    transitionValues.values.put(PROPNAME_TRANS_Z + "_end", translationZ );
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
                               TransitionValues endValues) {
    if (startValues == null || endValues == null) {
        return null;
    }
    final View view = endValues.view;
    float startElevation = (Float) startValues.values.get(PROPNAME_TRANS_Z + "_start");
    float endElevation = (Float) endValues.values.get(PROPNAME_TRANS_Z + "_end" );
    if (startElevation != endElevation) {
        view.setTranslationZ(startElevation);
        return ObjectAnimator.ofFloat(view, View.TRANSLATION_Z,
                startElevation, endElevation);
    }
    return null;
}

}

主要活动仅使用以下布局显示空白地图:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.motb.transitiontest.MainActivity"
android:id="@+id/main"
android:onClick="onClick"
android:background="@drawable/treasuremap"
>

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="250dp"
    android:id="@+id/fragmentContainer"
    android:layout_alignParentBottom="true"
    />


</RelativeLayout>

点击地图会将上面的 FrameLayout 替换为显示底部“卡片”的片段,该片段使用以下布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="250dp"
tools:context="com.motb.transitiontest.MainActivity"
android:id="@+id/main"
android:background="@android:color/transparent"
>


<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="160dp"
    android:background="@drawable/arc_bg"
    android:backgroundTint="#ff0000ff"
    android:layout_alignParentBottom="true"
    android:transitionName="rv1"
    android:id="@+id/arc1"
    android:translationZ="20dp"

    >

    <TextView
        android:id="@+id/helloText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World Transition Test"
        android:layout_marginStart="165dp"
        android:layout_marginTop="27dp"
        android:textSize="40dp"
        android:gravity="left"
        android:transitionName="tv1"
        android:textColor="#ffffffff"
        />


</RelativeLayout>

<ImageView
    android:layout_width="300px"
    android:layout_height="300px"
    android:layout_alignParentTop="true"
    android:layout_marginTop="80dp"
    android:layout_marginStart="20dp"
    android:transitionName="iv1"
    android:src="@drawable/charlie"
    android:id="@+id/charlie"
    android:translationZ="30dp"
    android:stateListAnimator="@null"
    />


</RelativeLayout>

单击片段,使用下面的 xml 启动带有狗的较大图像的第二个活动:

`
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.motb.transitiontest.Activity2"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
>


  <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.motb.transitiontest.Activity2"
    android:layout_marginTop="200dp"
    android:background="@drawable/arc_bg"
    android:layout_alignParentBottom="true"
    android:transitionName="rv1"
    android:id="@+id/arc1"
    android:backgroundTint="#ff0000ff"
    android:translationZ="40dp"
    >


    <TextView
        android:id="@+id/helloText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:text="Hello World Transition Test"
        android:textSize="40dp"
        android:textAlignment="center"
        android:transitionName="tv1"
        android:textColor="#ffffffff"
        />


</RelativeLayout>


<ImageView
    android:id="@+id/charlie"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/charlie"
    android:transitionName="iv1"
    android:scaleType="centerCrop"
    android:translationZ="0dp"
    android:stateListAnimator="@null"
    />



</RelativeLayout>`

目前,我看到的是,在过渡开始时海拔会立即发生变化,导致狗的图像被弧线遮挡。

【问题讨论】:

  • 一个更优雅和有效的解决方案是编写你的自定义类来绘制具有渐进 z 索引的图像,你可以使用 valueAnimator 为你的动画提供平滑度
  • @sumit 请提供更多详细信息,说明为什么您认为在这种情况下 valueAnimator 比 ObjectAnimator 更好。是什么让它更优雅、更高效?
  • 您也可以发布视图层次结构吗?
  • @azizbekian - 我将 xml 布局添加到问题的底部。

标签: android animation transition z-order elevation


【解决方案1】:

我查看了 github。这是一个错误。

我可以使用 Transtions 和 TransitionManager.go() 明确地运行场景之间的过渡。但是,当您在片段和活动之间运行过渡时,它总是会立即改变淡入淡出动画。 Support 26.0.0 beta-1 中修复了几个淡入淡出错误

  • 当淡入淡出过渡被中断和反转时,视图会从头开始动画。 (从 Android 框架移植的修复程序。)
  • Transition.Fade 忽略 View 的初始 alpha(AOSP 问题 221820)

但是,问问你自己,你曾经看到它做了一个真正的淡入淡出过渡吗? - 不。就像忽略所有的移动和任何东西一样,你能让它完全褪色吗?因为这就是我需要的所有建议,我无法让它按照你运行的方式运行。但是,显然元素的所有移动 确实 起作用,所以这是一个该死的错误。


你也可以完全伪造它。如果你有淡入淡出,你可以调用它并让它工作。如果将 Activity2 中的 onCreate() 更改为:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

    setContentView(R.layout.activity2);
    findViewById(R.id.arc1).setVisibility(View.VISIBLE);
    findViewById(R.id.arc2).setAlpha(0);
    findViewById(R.id.arc2).animate().alpha(1).setDuration(2000).start();
}

我只将 arc1 的可见性设置为正确的,因为你弄错了。那是始终可见的。

我想然后添加一个与另一个动画在同一时间段内的动画以逐步加入,然后繁荣。有用。这是一个愚蠢的错误。您不能在活动之间无缘无故地淡出。


我自己发现了 alpha 版本。第一个可能是正在发生的事情。或者它可能完全是另一类错误。但是,我可以向你保证。我检查并做对了一切。它仍然失败。显然,解决方法是在他们工作的地方使用场景转换。也许根本没有使用新的活动。

您显然可以想出很多解决方法。只需使用它应该具有的布局启动活动,然后设置一个场景以转换到正确的布局。在第一个活动中自己添加另一个过渡到布局。或者诸如此类。但是,它不起作用。你做从片段到活动的过渡,东西会移动,但淡入淡出总是抛出一个 conniption 并拒绝。它会立即获得另一个视图的可见性,并且如果没有 alpha 工作,也无法轻松地纠​​正它。

我的原始代码使用场景:

boolean to = true;
public void onClick(View view) {
    Scene scene;
    ViewGroup mSceneRoot = (ViewGroup) findViewById(R.id.container);
    if (to) {
        Transition mFadeTransition =
                TransitionInflater.from(this).
                        inflateTransition(R.transition.transitionset);
        scene = Scene.getSceneForLayout(mSceneRoot, R.layout.fragment_replacement, this);

        TransitionManager.go(scene, mFadeTransition);
    }
    else {
        Transition mFadeTransition =
                TransitionInflater.from(this).
                        inflateTransition(R.transition.backwardstransitionset);
        scene = Scene.getSceneForLayout(mSceneRoot, R.layout.fragment_rep, this);
        TransitionManager.go(scene, mFadeTransition);
    }
    to = !to;
}

当我认为这是他的错,因为我无法实施我关于如何进行过渡的非常出色的想法时,留下了一些旧答案。错误是他没有该死的褪色效果,这一直是问题所在。


您不能仅使用 Z 顺序对其进行动画处理。您更改顺序,它们会相互叠加,而不是相反。要执行您建议的操作,您需要有效地同时拥有两个订单,并以透明的方式为其过渡设置动画。

你画,弧,狗,另一个弧。然后 Z 顺序的变化实际上就是顶部绘制的 Arc 的透明度。如果它完全不透明,则 Arc 位于狗的顶部。如果它是完全透明的,那么狗就在弧上。但是,如果最上面的 Arc 是部分透明的,它会看起来像狗正在通过弧线融化,或者弧线正在通过狗线,这取决于我们如何调整透明度。您可以在其他地方使用相同的技巧。把东西放在两个地方就行了。如果它是不透明的,它会被覆盖,但如果它是透明的,它只会显示最下面的弧线,如果部分透明,则会显示弧线与对象之间的混合。在动画结束时,将它们按正确的顺序排列即可。

因此,在动画期间,您始终将 2 条弧线移动到相同的位置。您应该将它们视为相同的弧。并更改顶部 Arc 的不透明度,然后对那只狗做任何你正在做的事情。


视频:

https://youtu.be/zVs3qzPU2FM


创建布局。将要更改高度的位夹在另一个对象的两个副本之间。在其中一种布局中,将可见性设置为invisible。所以所有项目都以相同的顺序在两个布局中。但是,其中一个元素的第二个副本是不可见的。确保两个副本在两者中的位置相同。当您更改一个副本的边界时,请确保您也更改了另一个副本的边界。将它们作为一个单元移动。当您转换视图时,在移动它或诸如此类的同时淡出该项目。由于有第二个副本,它看起来不像淡出,它看起来像一个融化。


【讨论】:

  • 我想我明白你的建议,但仍然无法弄清楚实现。您的建议是否适用于 ActivityOptions.makeSceneTransitionAnimation?现在,我的起始片段布局和最终活动的布局中有两条弧线。我在我的transition.xml 中添加了一个。但我看到的是第二条弧线立即可见,而不是淡入。
  • 两条弧线必须相互重合移动。您应该只将它们视为 1 弧。如果弧线淡入,它应该从 0 alpha 开始,然后过渡到 1。同时始终与绘图堆底部的另一个弧线重合。
  • 我总是卷起一个自定义动画师,所以我不确定你是如何移动它们的。而且我发现您的模型令人困惑(狗图片是否取代了地图图片并变大了)?
  • 是的,狗在场景过渡动画中使用ChangeBounds进行放大和重新定位。
  • 我最终没有弄清楚如何进行您建议的两个弧形渐变。我最终只是在场景过渡中移动并调整狗图像的大小,并将弧线留在同一位置。然后在第二个活动的 onCreate 中,我在 150 毫秒后启动一个动画器,将弧向上移动到所需位置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 2016-01-03
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多