动画父视图而不动画子视图

Animating the parent view without animating the child view

我想做这样的事情: https://storage.googleapis.com/spec-host/mio-staging%2Fmio-design%2F1563837804615%2Fassets%2F1XlKhaQFU9aS84ACmF-EDjVKDgI4pPldv%2F02-overflowmenu.mp4

我想挤一个"ViewGroup" 就像你在视频中看到的那样。同时,我想在其原始位置淡出 ViewGroup 中的内容。即不将内容推到右边。

知道如何实现吗?

谢谢!

你可以用Transition API做这样的动画。声明两个 ViewGroups:第一个是水平的 cut/copy 按钮,第二个是垂直的 search/share 按钮。然后将这些 ViewGroup 切换为 TransitionManager。然后只需提供 Transition,它描述了第一个和第二个 ViewGroup 的视图如何出现和消失。

import androidx.appcompat.app.AppCompatActivity;
import androidx.transition.ChangeBounds;
import androidx.transition.Fade;
import androidx.transition.Scene;
import androidx.transition.Slide;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;
import androidx.transition.TransitionSet;

public class MainActivity extends AppCompatActivity {

    private ViewGroup mSceneRoot;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);
        mSceneRoot = findViewById(R.id.sceneRoot);

        showPopup1();
    }

    private void showPopup1() {
        ViewGroup popup1 = (ViewGroup) getLayoutInflater().inflate(R.layout.popup_1, mSceneRoot, false);
        popup1.findViewById(R.id.btnGo).setOnClickListener(v -> {
            showPopup2();
        });

        Scene scene = new Scene(mSceneRoot, popup1);
        TransitionManager.go(scene, getTransition(false));
    }

    private void showPopup2() {
        ViewGroup popup2 = (ViewGroup) getLayoutInflater().inflate(R.layout.popup_2, mSceneRoot, false);
        popup2.findViewById(R.id.btnBack).setOnClickListener(v -> {
            showPopup1();
        });

        Scene scene = new Scene(mSceneRoot, popup2);
        TransitionManager.go(scene, getTransition(true));
    }
}

activity_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sceneRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="bottom|right"
    android:orientation="vertical"
    android:paddingBottom="150dp"
    android:paddingRight="50dp"
    android:clipToPadding="false"
    android:clipChildren="false"/>

popup_1.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/popup1"
    android:layout_width="250dp"
    android:layout_height="wrap_content"
    android:transitionName="bg"
    app:cardElevation="10dp">

    <Button
        android:id="@+id/btnGo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:text="go"
        android:transitionName="btn_go"/>
</androidx.cardview.widget.CardView>

popup_2.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/popup2"
    android:layout_width="wrap_content"
    android:layout_height="250dp"
    android:transitionName="bg"
    app:cardElevation="10dp">

    <Button
        android:id="@+id/btnBack"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="back"
        android:transitionName="btn_back"/>
</androidx.cardview.widget.CardView>

这里的最后一件事是 getTransition 方法。您可以只使用 AutoTransition,它可以处理简单的布局更改。

private Transition getTransition(boolean open) {
      return new AutoTransition();
}

结果:

也只是为了演示,我写了更复杂的过渡,其中第一个视图组 appear/dissapear 上的按钮带有 Slide 动画。

 private Transition getTransition(boolean open) {
        Slide btnGo = new Slide(Gravity.RIGHT);
        btnGo.addTarget("btn_go");

        ChangeBounds bgBounds = new ChangeBounds();
        bgBounds.addTarget("bg");

        Fade btnBack = new Fade();
        btnBack.addTarget("btn_back");

        TransitionSet set = new TransitionSet();
        set.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
        if (open) {
            set.addTransition(btnGo);
            set.addTransition(bgBounds);
            set.addTransition(btnBack);
        } else {
            set.addTransition(btnBack);
            set.addTransition(bgBounds);
            set.addTransition(btnGo);
        }
        return set;
    }

结果:

请注意,背景的 transitionName 在两种布局中应该相同。 这里的所有转换 类 都来自 androix 包,因此代码是向后兼容的。但是,如果您需要支持 Lollipop 之前的设备,您应该通过 ViewCompat.setTransitionName 方法设置 transitionName

您可以检查 my another answer 更多 difficult/custom 转换。