使用Transition FrameWork实现有意义的转场动画

使用Transition FrameWork实现有意义的转场动画


Android Transition Framework可以实现三种效果:

  1. 不同Activity之间切换时,Activityc的内容(contentView)转场动画
  2. 不同Activity之间切换时,如果使用了Shared Element动画,也可以使用Transition FrameWork来实现不同的过渡动画效果
  3. 同一个Activity内View变化的过渡动画(Scene)

1. Activity之间切换的过渡动画

通过这种方法可以使activity切换时,他们的布局内容有过度动画

当从 Activity A 切换到 Activity B 的时候,Activity布局的内容会按照预先定义好的动画来执行过渡动画。在 android.transition 包中,已经有三种现成的动画可以用: Explode,Slide和Fade。 所有这些过渡都会跟踪activity布局中可见的目标Views,驱动这些Views按照过渡的规则产生响应的动画效果。

Explode Explode Explode

<– more –>

你可以在xml中创建这些过渡效果,也可以通过代码来创建。对于Fade过渡效果来说,它看起来是这样子的:

xml中创建

过渡效果定义在xml中,目录是 res/transition

res/transition/activity_fade.xml

<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/"
    android:duration="1000"/>

res/transition/activity_slide.xml

<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/"
    android:duration="1000"/>

要使用这些xml’中定义的过渡动画,你需要使用 TransitionInflater 来实例化它们。

MainActivity.java

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_transition);
       setupWindowAnimations();
   }

   private void setupWindowAnimations() {
       Slide slide = TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
       getWindow().setExitTransition(slide);
   }

TransitionActivity.java

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_transition);
       setupWindowAnimations();
   }

   private void setupWindowAnimations() {
       Fade fade = TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
       getWindow().setEnterTransition(fade);
   }

在代码中创建

MainActivity.java

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_transition);
       setupWindowAnimations();
   }

   private void setupWindowAnimations() {
       Slide slide = new Slide();
       slide.setDuration(1000);
       getWindow().setExitTransition(slide);
   }

TransitionActivity.java

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_transition);
       setupWindowAnimations();
   }

   private void setupWindowAnimations() {
       Fade fade = new Fade();
       fade.setDuration(1000);
       getWindow().setEnterTransition(fade);
   }
不管哪种创建方法都会产生如下的效果:

那么这里面一步一步的到底发生了什么:

  1. Activity A 启动 Activity B

  2. Transition Framework 发现 A 中定义了Exit Transition (slide) 然后就会对它的所有可见的View使用这个过渡动画.

  3. Transition Framework 发现 B 中定义了Enter Transition (fade) 然后机会对它所有可见的Views使用这个过渡动画.
  4. On Back Pressed(按返回键) Transition Framework 会执行把 Enter and Exit过渡动画反过来执行(但是如果定义了 returnTransition 和 reenterTransition ,那么就会执行这些定义的动画)
译注:
  • Exit Transition:可以理解为activity进入后台的过渡动画
  • Enter Transition:可以理解为创建activity并显示时的过渡动画
  • Return Transition:可以理解为销毁activity时的过渡动画
  • Reenter Transition:可以理解为activity从后台进入前台时的过渡动画
  • 要使这些过渡动画生效,我们需要调用 startActivity(intent,bundle) 方法来启动activity。 bundle 需要通过 ActivityOptionsCompat.makeSceneTransitionAnimation().toBundle() 的方式来生成

ReturnTransition & ReenterTransition

Return and Reenter Transitions是与进入和退出动画相对应的.

  • EnterTransition <–> ReturnTransition
  • ExitTransition <–> ReenterTransition

如果Return or Reenter没有创建, Android 会把Enter and Exit Transitions反过来执行. 但是如果你创建了Return or Reenter,那Android就会执行你创建的动画,并且这些动画可以不同.

我们可以修改下前面Fade的例子,给 TransitionActivity 创建 ReturnTransition 。这里我们就拿 Slide 过渡效果来举例子。这样,如果我们从B返回到A的时候,B就会执行一个Slide的过渡效果。

TransitionActivity.java

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_transition);
       setupWindowAnimations();
   }

   private void setupWindowAnimations() {
       Fade fade = new Fade();
       fade.setDuration(1000);
       getWindow().setEnterTransition(fade);
       
       Slide slide = new Slide();
       slide.setDuration(1000);
       getWindow().setReturnTransition(slide);        
   }

可以看到,如果没有创建Return Transition,退出的时候会执行Enter Transtion相反的动画。如果创建了Return Transition,那么就会执行这个创建的动画效果。

没有Return Transition 有Return Transition
Enter: Fade In Enter: Fade In
Exit: Fade Out Exit: Slide out

2. Activity之间共享元素(Share Elements)

这里的思想就是通过动画的形式将两个不同布局中的两个不同的View联系起来。

然后Transition framework就会在用户从一个View切换到另一个View的时候给用户展现一些必要的动画。

但你要记住:发生动画的View并不是从一个布局中移动到另一个布局。他们是两个独立的View。

a) 设置Window Content Transition属性

你需要在app的 styles.xml 中进行设置.[译]我没有设置也没问题

values/styles.xml

<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="android:windowContentTransitions">true</item
    ...
</style>

如果你愿意的话,你也可以给整个app设置一个默认的转场动画和共享元素动画。

<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
    ...
    <!-- specify enter and exit transitions -->
    <item name="android:windowEnterTransition">@transition/explode</item>
    <item name="android:windowExitTransition">@transition/explode</item>

    <!-- specify shared element transitions -->
    <item name="android:windowSharedElementEnterTransition">@transition/changebounds</item>
    <item name="android:windowSharedElementExitTransition">@transition/changebounds</item>
    ...
</style>

b)设置相同的transition name

为了使共享元素动画生效,你需要给共享元素的两个View设置相同的 android:transitionName 属性值。不过他们的id和其他属性可以不同。

layout/activity_a.xml

<ImageView
        android:id="@+id/small_blue_icon"
        style="@style/MaterialAnimations.Icon.Small"
        android:src="@drawable/circle"
        android:transitionName="@string/blue_name" />

layout/activity_b.xml

<ImageView
        android:id="@+id/big_blue_icon"
        style="@style/MaterialAnimations.Icon.Big"
        android:src="@drawable/circle"
        android:transitionName="@string/blue_name" />

c) 用共享元素来启动activity

使用 ActivityOptions.makeSceneTransitionAnimation() 方法指定要共享元素的View和 android:transitionName 属性的值

MainActivity.java

blueIconImageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, SharedElementActivity.class);

        View sharedView = blueIconImageView;
        String transitionName = getString(R.string.blue_name);

        ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
        startActivity(i, transitionActivityOptions.toBundle());
    }
});

这样就可以有下面漂亮的过渡效果了:

可以看到, Transition framework 创建并执行了一个动画。动画的视觉效果就是一个View从一个activity移动到另一个activity中并伴随着形状的变化。

在fragment之间实现Shared elements过渡动画

这个activity中的做法几乎是一样的。

a)和 b) 完全一样, 只有 c) 有点区别

a) 设置Window Content Transition属性

values/styles.xml

<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="android:windowContentTransitions">true</item>
    ...
</style>

b) 设置相同的transition name

layout/fragment_a.xml

<ImageView
        android:id="@+id/small_blue_icon"
        style="@style/MaterialAnimations.Icon.Small"
        android:src="@drawable/circle"
        android:transitionName="@string/blue_name" />

layout/fragment_b.xml

<ImageView
        android:id="@+id/big_blue_icon"
        style="@style/MaterialAnimations.Icon.Big"
        android:src="@drawable/circle"
        android:transitionName="@string/blue_name" />

c) 启动带有共享元素的fragment

在你使用 FragmentTransaction 启动fragment的时候,你需要同时带上共享元素过渡动画的先关信息。

FragmentB fragmentB = FragmentB.newInstance(sample);

// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);

// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);

getFragmentManager().beginTransaction()
        .replace(R.id.content, fragmentB)
        .addSharedElement(blueView, getString(R.string.blue_name))
        .commit();

最终的效果就是这样的:

允许过渡效果之间的重叠

You can define if enter and exit transitions can overlap each other.你可以设置一个activity的退出效果和另一个activity的进入效果产生重叠部分。

Android文档 )这么说的:

当设置为 true ,enter transition会立马执行>

当设置为 false ,enter transition会等到退出exit transition结束后再执行.

这对Fragments和Activities的共享元素过渡效果都是有用的。

FragmentB fragmentB = FragmentB.newInstance(sample);

// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);

// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);

// Prevent transitions for overlapping
fragmentB.setAllowEnterTransitionOverlap(overlap);
fragmentB.setAllowReturnTransitionOverlap(overlap);

getFragmentManager().beginTransaction()
        .replace(R.id.content, fragmentB)
        .addSharedElement(blueView, getString(R.string.blue_name))
        .commit();

可以看到,效果还是挺明显的:

Overlap True Overlap False
Fragment_2 出现在Fragment_1的上面 Fragment_2 等到Fragment_1消失后才出现

3. 布局元素动画

Scenes

也可以用来驱动一个activity中的布局发生变化时,让其中的View产生过渡动画。

过渡效果发生在场景之间。场景就是一个定义了静态UI的普通布局。Transition Framework可以在两个场景之间添加切换过渡动画。

scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this);
scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene2, this);
scene3 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene3, this);
scene4 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene4, this);

(...)

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.button1:
            TransitionManager.go(scene1, new ChangeBounds());
            break;
        case R.id.button2:
            TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
            break;
        case R.id.button3:
            TransitionManager.go(scene3, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
            break;
        case R.id.button4:
            TransitionManager.go(scene4, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
            break;  
    }
}

上面的代码会在同一个activity中的4个场景切换时产生过渡动画。每一次切花的动画都是不一样的。

Transition Framework会考虑当前场景内所有可见的View并计算需出需要的动画来安排两个场景之间的view的位置。

布局的改变

也可以用来给布局属性的变化加上过渡效果。你只需要做你想要的改变然后其他的就交给Transition Framework,它会为你添加动画。

a) 开启演示过渡效果

下面这行代码告诉framework我们将要对UI进行一些改变,请你给我加上过渡效果。

TransitionManager.beginDelayedTransition(sceneRoot);
b) 改变布局参数
ViewGroup.LayoutParams params = greenIconView.getLayoutParams();
params.width = 200;
greenIconView.setLayoutParams(params);

改变View的宽度属性让他变小,这会触发 layoutMeasure 。这个点上,Transition framework会记录开始和结束时的相关值,并给这个变化加上过渡效果。

4. (彩蛋) 共享元素(share elements)+圆形展现(Circular Reveal)

圆形展现仅仅一个现实和隐藏一组view的动画而已。API21+可以通过 ViewAnimationUtils 来使用它。

Circular Reveal动画可以结合共享元素过渡效果来创建一些有意义的动画来告诉用户app在发生中什么。

这又是怎么回事呢:

  • 橙色的圆是发生在 MainActivity 和 RevealActivity 之间的共享元素动画。
  • 在 RevealActivity 中有一个共享元素动画结束的监听器。当动画结束时它做了两件事:
    • 给Toolbar执行一个Circular Reveal动画
    • 使用 ViewPropertyAnimator 给 RevealActivity 的其他View执行一个放大的动画

监听共享元素动画的结束

Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
    @Override
    public void onTransitionEnd(Transition transition) {
        animateRevealShow(toolbar);
        animateButtonsIn();
    }
    
    (...)

});

显示Toolbar

private void animateRevealShow(View viewRoot) {
    int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
    int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
    int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

    Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
    viewRoot.setVisibility(View.VISIBLE);
    anim.setDuration(1000);
    anim.setInterpolator(new AccelerateInterpolator());
    anim.start();
}
```  

> 放大activity的内部view  

```java
private void animateButtonsIn() {
    for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
        View child = bgViewGroup.getChildAt(i);
        child.animate()
                .setStartDelay(100 + i * DELAY)
                .setInterpolator(interpolator)
                .alpha(1)
                .scaleX(1)
                .scaleY(1);
    }
}

更多circular reveal动画

有很多种方式来实现一个view的显示动画,但重要的是要让这些动画有意义,它可以告诉用户app中正在发生着什么。

从目标View的中间产生一个Circular Reveal动画

int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = viewRoot.getTop();
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.start();
```        

#### 从目标View的顶部产生一个Circular Reveal动画并且结合了其他的动画

![reveal_blue]

```java
int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        animateButtonsIn();
    }
});
anim.start();
``` 


#### 从触摸点产生一个Circular Reveal动画

![reveal_yellow]

```java
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
        if (view.getId() == R.id.square_yellow) {
            revealFromCoordinates(motionEvent.getRawX(), motionEvent.getRawY());
        }
    }
    return false;
}
private Animator animateRevealColorFromCoordinates(int x, int y) {
    float finalRadius = (float) Math.hypot(viewRoot.getWidth(), viewRoot.getHeight());

    Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, x, y, 0, finalRadius);
    viewRoot.setBackgroundColor(color);
    anim.start();
}
动画和展现

Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
transition.addListener(new Transition.TransitionListener() {
    @Override
    public void onTransitionEnd(Transition transition) {
        animateRevealColor(bgViewGroup, R.color.red);
    }
    (...)

});
TransitionManager.beginDelayedTransition(bgViewGroup, transition);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
btnRed.setLayoutParams(layoutParams);

Sample source code

https://github.com/lgvalle/Material-Animations

More information


转载自:http://damonzh.github.io/2016/04/04/transition-framework/?utm_source=tuicool&utm_medium=referral

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据中的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像中的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值