在Android 4.4(KitKat)中,谷歌添加了很多不错的东西。现在我们来看看android.transition框架。
多年来,android不断改进现有的动画工具供开发者使用。在HoneyComb版本中,提供了很多不错的API用于创建丰富、复杂的动画。在此基础上,KitKat的android.transition让我们可以通过一种更直观的方式定义动画效果。
Scene和Transition
先从Scene和Transition概念说起。Scene定义了界面的当前状态信息,而Transition定义了界面之间的切换。
可以从布局文件中载入Scene定义,示例如下:
1
|
scene = Scene.getSceneForLayout(container, R.layout.example, context);
|
其中container
在Scene中是一个包含了所有view的ViewGroup。如果是在fragment中,Scene就是传入onCreateView()
方法的参数。使用Transition的最简单方式就是使用TransitionManager
处理,示例如下:
1
|
TransitionManager.go(scene);
|
如果在TransitionManager
中不明确需要指定哪个Transition,就会默认使用AutoTransition
,这个我们会后面介绍。也可以用inflater
载入现有的view来创建Scene,示例如下:
1
2
|
View view = inflater.inflate(R.layout.example, container,
false
);
Scene scene =
new
Scene(container, (ViewGroup)view);
|
Andorid.Transition实践
我们来看一个更详细的示例,首先从项目主页下载示例代码AndroidTransitionExample。这已经是一个已完成的项目了,所以也可以用git checkout
检出代码(以下是详细解释)。
首先新建只包含一个Fragment的项目,这样可以更容易记录一些信息。我们为TransitionFragment
新建一个xml布局文件,叫做fragment_transition_scene_1.xml。接着往里面添加一个TextView
,然后在TextView
下面再添加一个Button,如下:
fragment_transition_scene_1.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:id
=
"@+id/scene"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<
TextView
android:id
=
"@+id/textView"
android:text
=
"@string/hello_world"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
<
Button
android:id
=
"@+id/goButton"
android:text
=
"@string/button_go"
android:layout_below
=
"@id/textView"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
</
RelativeLayout
>
|
你一定猜得到,我们接下来还要新建另一个xml布局文件,fragment_transition_scene_2.xml。它和上一个布局文件基本一样,只是把Button移到布局底部。示例如下:
1
2
3
4
5
6
7
8
9
|
...
<
Button
android:id
=
"@+id/goButton"
android:text
=
"@string/button_go"
android:layout_below
=
"@id/textView"
android:layout_alignParentBottom
=
"true"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
...
|
这是两个布局的屏幕截图:
为了看Transition的效果,我们从第二个布局文件中创建Scene。点击goButton的时候展示Transition的效果。我们先修改一下TransitionFragment.onCreateView()
方法的代码,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Override
public
View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_transition_scene_1,
container,
false
);
final
Scene scene = Scene.getSceneForLayout(container,
R.layout.fragment_transition_scene_2, getActivity());
Button goButton = (Button)rootView.findViewById(R.id.goButton);
goButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
TransitionManager.go(scene);
}
});
return
rootView;
}
|
这是点击goButton的效果:
为什么会产生这样的效果呢?因为如果view有相同的ID,就会被当做是同一个view,然后也会被改变(通过改变bounds),也就是说,在Scene切换时,view的位置和大小也会改变,需要注意的是两个布局文件的RelativeLayout
也要有相同的ID。
使用GIT实战
如果你对整个例子代码感兴趣的话,也可以利用GIT检出代码。从AndroidTransitionExample复制一份即可,你可以用GUI git客户端列出项目的历史版本,也可以定位到某个具体的提交点检出。
当然,你也可以使用命令行。使用cd
命令切换到项目文件夹,然后执行以下命令:
1
|
git log --oneline
|
此时,你会得到项目的提交列表,像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
cc40873 load the transition manager from the XML
file
c2a25d4 inflate the transition from the XML
file
9871bfa add the
return
transition
1de57f0 use AnticipateOvershootInterpolator
fbcc465 slow motion transitions
6ea37f7 extract method goToScene
34e0f8f restore transition by adding LinearLayout with
id
2b000b3 example of change bounds not working when changing hierarchy
6b4629c example of transitioning from Button to ImageView
092ebe0 added layout_weight to button
1e7c5be modified layout
for
scene 2
d94b907 Android Studio updated IML files
24f9a74 Create README.md
0667c36 simple transition
4265f50 factor out TransitionFragment
280f123 initial commit
|
以上贴出的对应于simple transition这个提交点,执行以下代码可以把项目设置成这个状态:
1
|
git checkout 0667c36
|
执行 git checkout
,你可以跳到任意一个提交点上,下面的插入文字会给出指示操作。
修改布局文件
我们来修改一下第二个布局文件,首先把RelativeLayout
换成LinearLayout
,然后我们来介绍一个在第一个布局文件中没有出现的view,最后我们重新排布一下这些view,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:id
=
"@+id/scene"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:orientation
=
"vertical"
android:gravity
=
"center_horizontal"
>
<
TextView
android:id
=
"@+id/textView"
android:text
=
"@string/hello_world"
android:layout_weight
=
"1"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
<
TextView
android:text
=
"@string/hello_world"
android:layout_weight
=
"1"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
<
Button
android:id
=
"@+id/goButton"
android:text
=
"@string/button_go"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
</
LinearLayout
>
|
我们目前处于“1e7c5be modified layout for scene 2”提交点,在命令行执行git checkout 1e7c5be
就可以切换上去。可以看到,Transition仍然起作用,在第一个Scene里面,不存在的view竟然出现在屏幕上,然后随着Button和TextView的移动而逐渐消失。
我们详细看看AutoTransition,事实证明,它只是TransitionSet
的子类,只是它给自己定义了一个执行序列,分别是fading out、changing bounds、fading in。
我们注意到,在第二个Transition中,AutoTransition会改变bounds,目前我们只看到了Button和TextView改变了位置,如果我们改变view的大小会出现什么情况呢?在Button中添加layout_weight属性来看看效果:
1
2
3
4
5
6
|
<
Button
android:id
=
"@+id/goButton"
android:text
=
"@string/button_go"
android:layout_weight
=
"1"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
|
现在Button在转变的过程中既改变了位置也改变了大小。
现在切换到“added layout_weight to button”这个点,命令:git checkout 092ebe0
。现在再做一些变化效果,在Scene中把Button转变成ImageView
:
1
2
3
4
5
6
7
|
<
ImageView
android:id
=
"@+id/goButton"
android:text
=
"@string/button_go"
android:layout_weight
=
"1"
android:src
=
"@drawable/bnr_hat"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
|
此时切换到“example of transitioning from Button to ImageView”这个点,命令: git checkout 6b4629c
。看看从Button变成ImageView
的效果:
仔细看你会发现,第一个Button首先被一张切割过的图片替换了,然后逐渐移动到最终的位置,逐渐改变大小。
如果我们改变所有views的嵌套结构,比如在Button外面套一层LinearLayout
,那么Bounds就不会改变了。transition manager要求在Scene的布局文件中,同一层级的view要有和之前相同的ID:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<
LinearLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
>
<
ImageView
android:src
=
"@drawable/bnr_hat"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
<
Button
android:id
=
"@+id/goButton"
android:text
=
"@string/button_go"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
</
LinearLayout
>
|
此时切换到“example of change bounds not working when changing hierarchy”这个点,命令:git checkout 2b000b3
。
如果我们在第一个Scene中继续用LinearLayout
包含Button,那么还是不行。想让它起作用的话,可以给LinearLayout
一个相同的ID。这会得到两种不同的效果。
切换到“restore transition by adding LinearLayout with id”,命令:git checkout 34e0f8f
下一篇我们我们继续研究怎么控制Transition,以及如何从xml文件中载入Transition。
原文地址 http://blog.jobbole.com/62601/