最近手機升級到Android 4.0.3版,想說來試試看寫個有 Fragment 程式,整理了一下學習心得。
隨著平板電腦及多螢幕系統的出現, Fragment 的設計是為了讓大尺寸的螢幕有更動態更彈性的UI設計。
下圖是官網中Design Philosophy所提供的一個範例,用這張圖舉個例子,如果要設計動態新聞資訊時,在手機平台 Activity 1 設計使用 Fragment 1 (新聞列表) ,Fragment 2 (新聞內容);在平板則設計左邊為 Fragment 1 (新聞列表) ,右邊畫面為 Fragment 2 (新聞內容) 。
Fragment 有兩種,一種是具有UI介面作為將一個 ViewGroup 增加到 Activity 中(見實作1),另一種是沒有UI介面直接動態加入 Activity 中(見實作2),另外如果作為背景工作使用也採用此種方式(見實作3)。
Fragment (碎片)不能獨立存在必須依附在Activity內,Fragment擁有自己的生命周期,但會受到Activity生命週期的約束。所以使用時要考慮設計成模組化及可重複使用性,通常是作為更新頻繁的列表之類的功能,
每個 Fragment 定義自己的佈局(layout)、生命週期行為,並配合不同螢幕解析度或佈局配置下優化螢幕空間。這樣的作法最主要的目的是當使用者操作該介面時只要更新Fragment所負責的介面,而不用更新到整個Activity;
這樣可以避免掉Activity在做換頁時進行多次的onDestroy()等工作,常使用在 Fragment 在 Activityt提供了一個Back Stack來儲存操作資訊,然後透過選單或是按鈕之類的方式對Fragment進行回復或退回,這樣避免掉Activity與Activity切換時所浪費的時間,大大提高程式的執行效率。
在Android 系統中,透過 Stack方式來管理 Activity ,一般來說由於Android由於設計在手持裝置上所以不能像Windows一樣可以將多個應用程式介面並排或重疊在一個畫面上,當開啟一個 Activity 時系統會按照使用時間先後自動將該Activity放置在一個Stack中,並將前景使用的Activity放置在Stack最前面。
同樣,管理Fragment也是Stack方式差別在於這個Stack是由Activity來管理,當呼叫 addToBackStack方法時Fragment才會被加入Stack內。因此,Fragment不是個應用及的元件,所以不需要再 AndroidManifest.xml 中去定義Fragment相關參數。
另外,需要注意的地方還有每個Fragment需要一個唯一的標識,這樣能夠在Activity被重啟時系統使用這個ID來恢復Fragment。
Fragment提供ID的方法有三種方式:
A. Supply the android:id attribute with a unique ID. (使用android:id屬性來設定唯一ID;)
B. Supply the android:tag attribute with a unique string.(使用android:tag屬性來設定唯一的字串;)
C. you provide neither of the previous two, the system uses the ID of the container view.(如果沒有設定前面兩個屬性,系統會使用Fragment容器View ID。)
實作1: 第一個 Fragment
1.建立Fragment (MyFirstFragment.java、firstfragment.xml)
firstfragment.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/Fragment1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:text="My first Fragment !!"
android:textAppearance="?android:attr/textAppearanceLarge" />
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyFirstFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.firstfragment, container, false);
return v;
}
}
2.MainActivity
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#FFFFFF"
android:text="Main Activity"
android:textColor="#0000FF" />
<fragment
android:id="@+id/first"
android:name="com.demo.fragment.MyFirstFragment"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="2" />
</LinearLayout>
MainActivity.java
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
3.結果:
實作2: 動態增加 Fragment
1.建立Fragment (MyFirstFragment.java、firstfragment.xml)
firstfragment.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/Fragment1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:text="My first Fragment !!"
android:textAppearance="?android:attr/textAppearanceLarge" />
MyFirstFragment.java
package com.demo.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyFirstFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.firstfragment, container, false);
return v;
}
}
2.MainActivity
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/MainActivityUI"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:text="Main Activity"
android:textColor="#0000FF" />
</LinearLayout>
MainActivity.java
package com.demo.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addFragment();
}
void addFragment() {
// Instantiate a new fragment.
Fragment newFragment = new MyFirstFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.MainActivityUI, newFragment, "first");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
}
}
增加一個 Fragment 部分的程式碼在addFragment()中實現,首先建立一個 MyFirstFragment 的實例(Instantiate),
在使用getFragmentManager()獲得FragmentTransaction物件,並呼叫 beginTransaction() 開始執行Transaction;
過程中使用FragmentTransaction物件add()的方法將Fragment增加到Activity中。
add()有三個參數,第一個是Fragment的ViewGroup;第二個是Fragment 的實例(Instantiate);第三個是Fragment 的Tag。
一旦FragmentTransaction出現變化,必須要呼叫commit()使之生效。
3.結果:
實作3: 動態增加沒有UI的 Fragment
1.這部分程式在Android ApiDemos 中有範例請參考:
SDK根目錄\samples\android-15\ApiDemos\src\com\example\android\apis\app\FragmentRetainInstance.java
或 http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.html
3.結果:
這部分主要是透過 FragmentTransaction 中的 add(Fragment fragment, String tag) 在Activity中來增加Fragment,與上述兩個實例不一樣地方在於不用呼叫onCreateView()。
如果Activity 取得 Fragment,就要利用Tag方式在程式中可以使用 findFragmentByTag()方法達成。
至於其他相關資料例如 Fragment生命週期、FragmentManager 、在Android 2.x ~ 1.6版設備使用Fragment 等,有空時再整理分享。
參考:
1.The Android 3.0 Fragments API
http://android-developers.blogspot.com/2011/02/android-30-fragments-api.html
2.Fragments For All
http://android-developers.blogspot.com/2011/03/fragments-for-all.html
3.Fragments
http://developer.android.com/guide/topics/fundamentals/fragments.html (英文版)
http://leybreeze.com/blog/?p=902 (簡體中文版)
4.Android在平版電腦上的Activity設計和智慧型手機上有些不同
http://cheng-min-i-taiwan.blogspot.com/2011/03/androidactivity.html
==============延伸閱讀=====================
1.第一支Android Fragment程式--HelloFragment
http://cheng-min-i-taiwan.blogspot.tw/2012/04/android-fragment-hellofragment.html
2.Android Fragments 的生命週期
http://cheng-min-i-taiwan.blogspot.tw/2012/12/android-fragments.html
3.Fragment間的資料傳遞
http://cheng-min-i-taiwan.blogspot.tw/2013/01/fragment.html
4.Fragment與Activity間的資料傳遞
http://cheng-min-i-taiwan.blogspot.tw/2013/02/fragmentactivity.html
5.Fragment子類別之ListFragment
http://cheng-min-i-taiwan.blogspot.tw/2013/02/fragmentlistfragment.html
6.Fragment子類別之DialogFragment
http://cheng-min-i-taiwan.blogspot.tw/2013/02/fragmentdialogfragment.html
7.Fragment子類別之PreferenceFragment
http://www.cheng-min-i-taiwan.blogspot.tw/2013/02/fragmentpreferencefragment.html
8.Fragment子類別之WebViewFragment
http://cheng-min-i-taiwan.blogspot.tw/2013/02/fragmentwebviewfragment.html
版主妳好:
回覆刪除近期正在摸索android這個領域
遇到fragment這部分的問題
是否方便請教版主呢?
謝謝
可以的,但我不一定可以回覆您的正確答案ㄡ...
刪除老師:
回覆刪除可不可以寫一篇 GOOGLE TV模擬器教學
網路上好像也沒有類似的教學文章~~