简介
自KitKat起,Android加入了 Android Transition框架,可以帮助我们做一些Activity级别或View级别的动画效果,今天的内容就是如何在View上使用Transition。
有别于传统的Animation, Transition需要对动画前后设置不同布局,通过相应的API实现两个布局的切换动画。而传统Animation只会对应一个布局文件,动画结束时,即使设置了fillAfter(true)让画面停留在动画的最后一帧也不会对布局产生影响。现在,对于这种动画前后需要改变布局的场景,我们可以使用Transition。
第一步:
使用Android Studio创建一个新的工程。建一个空的Activity。由于本例使用了一些5.0加入的API,SDK版本请使用21.
第二步:
创建一些Demo中需要用到的Drawable。我们将使用Shape创建4个圆形,每一个都有不同的颜色与渐进效果。首先在drawable目录下新建一个文件:shape1.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:shape="oval">
<!--gradientRadius 很难理解,**%的形式在三星note机型上表现非常差!
使用float值在5.0上的表现非常差! -->
<!-- %意识是相对于自己的尺寸,%p是相对于自己Parent的-->
<gradient
android:endColor="#66ff0000"
android:gradientRadius="75%"
android:centerColor="#ffffcc00"
android:startColor="#ffffcc00"
android:type="radial"
android:useLevel="false"/>
<size
android:height="100dp"
android:width="100dp"/>
</shape>
以上代码构建出的是一个由渐变色填充而成的圆形图案。而四个图形在大小与样式方面完全相同,仅仅在色彩上有所区别。当然,大家可能需要为不同像素密度的设备分别准备多种不同版本的图形。利用以下代码创建shape2.xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:shape="oval" >
<gradient
android:endColor="#66ffcc00"
android:gradientRadius="75%"
android:startColor="#ff00ff00"
android:type="radial"
android:useLevel="false" />
<size
android:height="100dp"
android:width="100dp" />
</shape>
接着创建shape3.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:shape="oval" >
<gradient
android:endColor="#6600ff00"
android:gradientRadius="75%"
android:startColor="#ff0000ff"
android:type="radial"
android:useLevel="false" />
<size
android:height="100dp"
android:width="100dp" />
</shape>
最后创建shape4.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:shape="oval" >
<gradient
android:endColor="#660000ff"
android:gradientRadius="75%"
android:startColor="#ffff0000"
android:type="radial"
android:useLevel="false" />
<size
android:height="100dp"
android:width="100dp" />
</shape>
我们将把这些图形作为ImageButtons应用在两种布局场景之内。
第一步
我们需要为每个动画创建两个布局,分别对应动画前的状态和动画后的状态。在Transition中,这两个状态被定义成Scene,Transition就是不同Scene之间的切换效果。不同Scene的布局文件可以有重名控件,并且往往看起来差不多,只是同一个控件的位置大小图片等属性不一样。
OK,现在我们创建布局文件,在res/layout目录下新建scene_1.xml。
首先我们添加一个RelativeLayout用来容纳我们后面的ImageButton:
<pre name="code" class="html"><RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
tools:context="xzy.demo.androidtestapi.transition.TestTransition">
</RelativeLayout>
接着向这个RelativeLayout中添加4个ImageButton,每个ImageButton会只用1个我们之前创建的shape.
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn1"
android:src="@drawable/shape1"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="centerInside"
/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn2"
android:src="@drawable/shape2"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:scaleType="centerInside"
/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn3"
android:src="@drawable/shape3"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn4"
android:src="@drawable/shape4"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
/>
注意,需要对scene中的每个控件单独设置ID和事件,即使不同scene中的控件ID相同,也许要单独设置事件。
在IDE中,我们的布局预览效果如下:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
tools:context="xzy.demo.androidtestapi.transition.TestTransition">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn1"
android:src="@drawable/shape1"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn2"
android:src="@drawable/shape2"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn3"
android:src="@drawable/shape3"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn4"
android:src="@drawable/shape4"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
</RelativeLayout>
最后是scene_3.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
tools:context="xzy.demo.androidtestapi.transition.TestTransition">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn1"
android:src="@drawable/shape1"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn2"
android:src="@drawable/shape2"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn3"
android:src="@drawable/shape3"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn4"
android:src="@drawable/shape4"
android:background="#00000000"
android:contentDescription="shape"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="centerInside"
android:onClick="changeScene"/>
<TextView
android:id="@+id/info"
android:text="序列动画,会在球动完后出来"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
现在我们回头看一下三个scene,除了ImageButton的位置,这三个布局基本相同。
第一步:
打开创建的主Activity,在onCreate()之前做如下申明:
private Scene mScene1, mScene2, mScene3;
private RelativeLayout mBaseLayout;
private TransitionManager mCustomTransitionManager;
第二步:
编写一些初始化代码,在onCreate()中自动创建的代码后面添加以下代码:
setContentView(R.layout.base_transition_layout);
//得到容器ViewGroup
mBaseLayout = (RelativeLayout) findViewById(R.id.base);
//创建三个场景
mScene1 = new Scene(mBaseLayout, mBaseLayout.findViewById(R.id.container));
mScene2 = Scene.getSceneForLayout(mBaseLayout, R.layout.scene_2, this);
mScene3 = Scene.getSceneForLayout(mBaseLayout, R.layout.scene_3, this);
//create transition, set properties
TransitionInflater inflater = TransitionInflater.from(this);
mCustomTransitionManager = inflater.inflateTransitionManager(R.transition.transition_mgr,
mBaseLayout);
首先,我们要对容器ViewGroup进行定义,后面的每个scene都是添加到这个容器中的。然后我们初始化不同的scene,每个Transition会对应两个Scene,通过这种方式,Android将能够根据我们的需要在两个场景之间进行过渡、并将不同场景下具备相同ID的任意视图元素作为同一对象加以处理,这样场景切换时就能显示出动画式的变化效果。
第三步:
设置点击事件,触发不同的Transition动画:
public void changeScene(View v) {
if (v.getId() == R.id.anim_1) {
//简单的写法,直接用TransitionManager,使用默认动画效果
TransitionManager.go(mScene1);
} else if (v.getId() == R.id.anim_2) {
TransitionManager.go(mScene2);
} else if (v.getId() == R.id.anim_3) {
mCustomTransitionManager.transitionTo(mScene3);
} else if (v.getId() == R.id.anim_4) {
//在下一帧进行动画,可以继续设置属性等
TransitionManager.beginDelayedTransition(mBaseLayout);
//可以继续设置View的属性,不会影响动画
ImageView btn4 = (ImageView) mBaseLayout.findViewById(R.id.btn4);
ViewGroup.LayoutParams params = btn4.getLayoutParams();
if (params.width > dip2px(this, 51)) {
params.width = dip2px(this, 51);
params.height = dip2px(this, 50);
} else {
params.width = dip2px(this, 100);
params.height = dip2px(this, 100);
}
btn4.setLayoutParams(params);
}
}
Android提供了一系列过渡类型可供选择,大家可以根据自己需要的场景变化方式采用其中的不同动画效果。在今天的Demo中,我们使用
TransitionManager.go(mScene2);
来触发默认的动画效果,采用:
mCustomTransitionManager.transitionTo(mScene3);
来触发我们自定义的动画效果。
Android会计算如何根据我们设置的Transition属性在两个Scene间实现基过渡。感兴趣的朋友也可以 点击此处 查看更多与Transition引用相关的选项。
程序主Activity代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff000000"
tools:context="xzy.demo.androidtestapi.transition.TestTransition">
<Button
android:id="@+id/anim_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="changeScene"
android:text="演示1"
/>
<Button
android:id="@+id/anim_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/anim_1"
android:onClick="changeScene"
android:text="演示2"
/>
<Button
android:id="@+id/anim_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/anim_2"
android:onClick="changeScene"
android:text="演示3"
/>
<Button
android:id="@+id/anim_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/anim_3"
android:onClick="changeScene"
android:text="演示4"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/base"
android:layout_below="@id/anim_1">
<include layout="@layout/scene_1"
/>
</RelativeLayout>
</RelativeLayout>
现在大家可以运行自己的程序,点击不同的Button查看不同Transition的动画效果。
总结:
在今天的文章中,我们事实上还只是初步了解了自己能够利用Android Transition框架实现怎样的设计方案与过渡效果。要在自己的应用程序中引入更多过渡机制,大家可以 点击此处 查看TransitionManager类当中的其它方法,其中包括beginDelayedTransition与transitionTo。此外,大家也不妨尝试利用 TransitionSet 将多种过渡机制结合在一起,例如同时使用来自不同过渡机制的渐变与移动效果。根据过渡机制复杂程度的不同,大家可能还需要用到 TransitionValues 类,它能够提供与对应过渡相关的数据值引用能力。如果各位还想了解更多与场景处理相关的技术手段,也可以 点击此处 查看 Scene 类的相关说明。