天天看點

使用Android.Transition架構建立動畫(1)

在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"

/>

...

這是兩個布局的螢幕截圖:

使用Android.Transition架構建立動畫(1)
使用Android.Transition架構建立動畫(1)

為了看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的效果:

使用Android.Transition架構建立動畫(1)

為什麼會産生這樣的效果呢?因為如果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/

繼續閱讀