参考:https://developer.android.google.cn/reference/android/graphics/drawable/RippleDrawable.html
https://developer.android.google.cn/training/material/animations.html#Reveal
在material主题当中为按钮和activity transition提供了很多默认的动画,并且在API21及以上允许我们自定义这些动画,包括以下几个部分:
1、触摸反馈动画
2、循环揭露动画
3、Activity transitions动画
4、曲线运动
5、视图状态改变
接下来对这些效果一一进行讲解:
1、触摸反馈
触摸反馈就是我们和UI进行交互的时候呈现出来的动画效果,最典型的就是我们点击按钮的时候呈现出来的水波纹动画效果。
实现这个效果最简单的方式就是设置View的背景为:
?android:attr/selectableItemBackground,这个代表有界的波纹
?android:attr/selectableItemBackgroundBorderless 这个代表无界的波纹,这个波纹将会扩展到其父类
注意:selectableItemBackgroundBorderless是API21中新加的attribute
以上两种波纹效果的波纹颜色是固定的,那么要想改变波纹的颜色,就得使用RippleDrawable这个类了,可以在xml文件当中定义RippleDrawable。
通过类的层次结构可以发现RippleDrawable是继承自LayerDrawable的。
在xml当中定义RippleDrawable:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ffff0000" >
<item android:id="@android:id/mask"
android:drawable="@android:color/black" />
</ripple>
还可以不定义mask层,这样就会出现背景,这个背景的drawable还可以是别的drawable:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ffff0000" >
<item android:drawable="@android:color/darker_gray"/>
<!--
<item android:drawable="@drawable/icon"/>
-->
</ripple>
还可以既不定义mask又不定义子层,此时的波纹效果将会超出View的边界:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#ffff0000" >
</ripple>
我们只需要设置在xml文件当中设置View的backgroud为以上定义的 RippleDrawable即可。
下面来看下效果,首先是布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:id="@+id/content_material_design_animatio"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.easyliu.test.animationdemo.MaterialDesignAnimation.MaterialDesignAnimatioActivity"
tools:showIn="@layout/activity_material_design_animatio"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:text="textview bounded ripple"
android:textSize="20sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:text="textview Borderless ripple"
android:textSize="20sp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="@drawable/ripple_drawable_normal"
android:text="Customize Touch Feedback normal"
android:textSize="20sp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="@drawable/ripple_drawable_no_mask"
android:text="Customize Touch Feedback no mask"
android:textSize="20sp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:background="@drawable/ripple_drawable_no_mask_and_childlayer"
android:text="Customize Touch Feedback no mask and childlayer"
android:textSize="20sp"
/>
</LinearLayout>
效果如下:
2、循环揭露动画
当隐藏或者显示View的时候,揭露动画提供用户一种视觉的连续性,使用方式如下所示,主要是使用ViewAnimationUtils.createCircularReveal() 方法。不过这里需要注意:确保getWidth()以及getHeight()能够获取到有效的值,不然没有动画效果。
private void initView() {
final View revealView = findViewById(R.id.btn_reveal);
findViewById(R.id.btn_toggle_view).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
if (revealView.getVisibility() == View.VISIBLE) {
// get the center for the clipping circle
int cx = revealView.getWidth() / 2;
int cy = revealView.getHeight() / 2;
// get the final radius for the clipping circle
float initialRadius= (float) Math.hypot(cx, cy);
// create the animation (the final radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(revealView, cx, cy, initialRadius, 0);
anim.setDuration(1000);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
// make the view invisible when the animation is done
anim.addListener(new AnimatorListenerAdapter() {
@Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
revealView.setVisibility(View.INVISIBLE);
}
});
anim.start();
} else {
// get the center for the clipping circle
int cx = revealView.getWidth() / 2;
int cy = revealView.getHeight() / 2;
// get the final radius for the clipping circle
float finalRadius= (float) Math.hypot(cx, cy);
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(revealView, cx, cy, 0, finalRadius);
anim.setDuration(1000);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
// make the view visible and start the animation
revealView.setVisibility(View.VISIBLE);
anim.start();
}
}
});
}
效果如下:
3、Activity Transition
material design当中可以为activity指定enter和exit动画以及activity之间共享元素的动画。
enter动画代表activity当中的view怎么进入界面
exit动画代表activity当中的view怎么退出界面
shared elements动画代表两个activity之间共享的view表现的动画,比如两个activity有相同的图片,只是位置和大小不相同,此时就可以把这张图片指定为共享元素,为其指定动画。
Android5.0支持以下的enter和exit动画:
explode:view从界面的中心进入或退出
slide:view从界面的边缘进入或退出
fade:view通过改变自身的大小来进入或退出
Android5.0支持以下的shared elements动画:
changeBounds - Animates the changes in layout bounds of target views.
changeClipBounds - Animates the changes in clip bounds of target views.
changeTransform - Animates the changes in scale and rotation of target views.
changeImageTransform - Animates changes in size and scale of target images.
如下所示即为拥有共享元素的activity之间的transition
为activity指定transition的步骤:
1、设置activity的主题,包括enter和exit的activity都得设置主题,如下所示。
首先需要 enable window content transitions,也就是设置name=“android:windowActivityTransitions”为true
然后就是设置enter和exit动画,最后设置共享元素动画,如ActivityTransitionTheme所示。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="ActivityTransitionTheme" parent="AppTheme.NoActionBar">
<!-- enable window content transitions -->
<item name="android:windowActivityTransitions">true</item>
<!-- 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/move_image
</item>
<item name="android:windowSharedElementExitTransition">@transition/move_image
</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowAllowEnterTransitionOverlap">false</item>
</style>
需要在res目录下新建transition目录,然后定义explode.xml以及move_image.xml这两种动画效果:
explode.xml:
<?xml version="1.0" encoding="utf-8"?>
<explode/>
move_image.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
<changeBounds/>
<changeImageTransform/>
</transitionSet>
当然也可以在代码中设置这些动画,如果不在主题中指定的话。
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
// set an exit transition
getWindow().setExitTransition(new Explode());
主要有以下几个函数:
Window.setEnterTransition()
Window.setExitTransition()
Window.setSharedElementEnterTransition()
Window.setSharedElementExitTransition()
Window.setAllowEnterTransitionOverlap()
2、接下来就是执行activity的跳转了:
Intent intent = new Intent(TransitionExitActivity.this, TransitionEnterActivity.class);
startActivity(intent,
ActivityOptions.makeSceneTransitionAnimation(TransitionExitActivity.this).toBundle());
在进入的activity当中,当点击按钮finish自己返回的时候,为了呈现相同的动画效果,需要调用finishAfterTransition()而不是finish()函数。
最终效果如下所示:
4、共享元素的ActivityTransition
以上只是简单的activity transition,并没有使用共享元素,接下来讲解具有共享元素的activityTransition,首先看效果。这张图片就是共享元素。
实现步骤如下:
1、给共享元素设置android:transitionName 属性,两个Activity当中的ImageView都得设置这个属性,且名字得相同
2、点击ExitActivity当中的imageView执行跳转:
indViewById(R.id.iv_share_element).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
Intent intent = new Intent(TransitionExitActivity.this, TransitionEnterActivity.class);
intent.putExtra(KEY_ID,R.drawable.ball);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(TransitionExitActivity.this, view,
TRANSITION_NAME).toBundle());
}
});
需要把imageView和TransitionName都传入,同时把imageView的资源id作为参数传过去,这样接收的Activity就能得到这个资源id,从而设置imageView,如下所示:
int imageResourceId=getIntent().getIntExtra(KEY_ID,0);
ImageView imageView= (ImageView) findViewById(R.id.iv_share_element);
imageView.setImageResource(imageResourceId);
imageView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
finishAfterTransition();
}
});
通过这种方式就达到了共享元素transition的效果。