當前專案使用的是TabHost+Activity進行分頁,目前要做個報表功能,需要在一個Tab頁內進行Activity的切換。比方說我有4個Tab頁分別為Tab1,Tab2,Tab3,Tab4,現在的需求是需要將Tab1內的Activity動態切換。找了很多資料最終使用了ActivityGroup解決了問題,在這過程中順便嘗試了一下使用Fragment+FragmentActivity+TabHost和Fragment+FragmentActivity+ActionBar試圖淘汰掉舊版的ActivityGroup和TabHost,但是發現如果要使用Fragment,我需要修改已有的各個Tab頁內的Activity,工作量較大,而使用ActionBar還有著許多版本相容性問題,並且ActionBar不支援多層巢狀,最終放棄。現在寫一下我個人實驗用的TabHost+ActivityGroup、Fragment+FragmentActivity+TabHost和Fragment+FragmentActivity+ActionBar的Demo,供參考。

首先是TabHost+ActivityGroup。在最初的想法中,我是使用通過清空TabHost現有內容,再新增新內容來實現動態載入Tab頁效果,具體程式碼不說了,舉個例子:我有4個Tab頁分別為Tab1、Tab2、Tab3和Tab4,存放4個Activity分別為Tab1Activity、Tab2Activity、Tab3Activity和Tab4Activity,現在我需要將Tab1位置的Tab1Activity變成Tab5Activity,我的做法是先全部清空,然後重新新增Tab1、Tab2、Tab3和Tab4,在新增的時候,將Tab1指向的Activity變成Tab5Activity。這種方法無疑是非常蠢得,而且在這一切換之後,會發現Ta5Activity的生命週期出現異常,每次在Tab1和其他Tab之間切換時,Ta5Activity都會被destroy掉重新create。這個問題我至今沒找到原因。

然後找到了ActivityGroup。ActivityGroup也是繼承至Activity類,可以看成是一個Actvity的容器類。 以上面的例子為例,當我們把ActivityGroup放入Tab1內時,我們就可以通過管理ActivityGroup來達到動態切換Activity的效果,貼上ActivityGroup的程式碼:

public class ActivityGroup1 extends ActivityGroup {
private ScrollView container = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
//存放Activity的容器
container = (ScrollView) findViewById(R.id.containerBody);
// 模組1
ImageView btnModule1 = (ImageView) findViewById(R.id.btnModule1);
btnModule1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//清空容器內現有內容
container.removeAllViews();
//載入activity
container.addView(getLocalActivityManager().startActivity(
"Module1",
new Intent(ActivityGroup1.this, Tab1Activity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
.getDecorView());
}
}); // 模組2
ImageView btnModule2 = (ImageView) findViewById(R.id.btnModule2);
btnModule2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
container.removeAllViews();
container.addView(getLocalActivityManager().startActivity(
"Module2",
new Intent(ActivityGroup1.this, Tab2Activity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
.getDecorView());
}
}); // 模組3
ImageView btnModule3 = (ImageView) findViewById(R.id.btnModule3);
btnModule3.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
container.removeAllViews();
container.addView(getLocalActivityManager().startActivity(
"Module3",
new Intent(ActivityGroup1.this, Tab3Activity.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
.getDecorView());
}
});
}
}

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:orientation="vertical"
android:layout_height="fill_parent">
<LinearLayout android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/cust_title" android:textColor="@android:color/white"
android:textSize="28sp" android:text="模組1" android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
</LinearLayout>
<!-- 中間動態載入View -->
<ScrollView android:measureAllChildren="true" android:id="@+id/containerBody"
android:layout_weight="1" android:layout_height="fill_parent"
android:layout_width="fill_parent">
</ScrollView>
<LinearLayout android:background="@android:color/black"
android:layout_gravity="bottom" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<!-- 功能模組按鈕1 -->
<ImageView android:id="@+id/btnModule1" android:src="@android:drawable/ic_dialog_dialer"
android:layout_marginLeft="7dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- 功能模組按鈕2 -->
<ImageView android:id="@+id/btnModule2" android:src="@android:drawable/ic_dialog_info"
android:layout_marginLeft="7dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- 功能模組按鈕3 -->
<ImageView android:id="@+id/btnModule3" android:src="@android:drawable/ic_dialog_alert"
android:layout_marginLeft="7dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

在TabHost中的呼叫方式和普通的activity一樣

TabSpec tabSpec=mtabHost.newTabSpec("1").setIndicator("TAB1").setContent(
new Intent(this, FragmentActivity1.class));
mtabHost.addTab(tabSpec);

這樣通過點選三個按鈕就能達到動態切換Activity的效果了。

順便說下,當ActivityGroup內巢狀Activity時,Activity的生命週期跟TabHost內巢狀Activity一樣。具體的百度。

較新的版本中TabHost和ActivityGroup都已經被淘汰了,取而代之的是FragmentActivity+Fragment。但我很疑惑Fragment是否真能完全取代ActivityGroup的效果。就拿上述功能來說,我要想使用FragmentActivity替換ActivityGroup,那麼我現有的Activity就必須重寫,改裝成Fragment。這種情況下,我在其他地方要使用這些Activity該怎麼辦呢?我沒找到如何使用Fragment裝載Activity的辦法,所以暫時就沒法用FragmentActivity替代ActivityGroup了。貼下使用FragmentActivity+Fragment+TabHost的實現程式碼:

Fragment1Activity:

public class FragmentActivity1 extends FragmentActivity{
public static FragmentManager fm;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_acitivity_1);
fm = getSupportFragmentManager();
// 只當容器,主要內容已Fragment呈現
initFragment(new Fragment1());
}
/**
*
*/ // 切換Fragment
public static void changeFragment(Fragment f){
changeFragment(f, false);
}
// 初始化Fragment(FragmentActivity中呼叫)
public static void initFragment(Fragment f){
changeFragment(f, true);
}
private static void changeFragment(Fragment f, boolean init){
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.simple_fragment, f);
if(!init)
ft.addToBackStack(null);
ft.commit();
}
}

這裡的FragmentActivity1並不顯示具體內容,只做Fragment得容器使用,具體顯示內容通過呼叫changeFragment方法動態載入。

Fragment1和Fragment2的程式碼如下,點選按鈕兩者互相切換:

public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment1, container, false);
Button tv = (Button)v.findViewById(R.id.button2);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 直接呼叫FragmentActivity1的靜態方法來做切換
FragmentActivity1.changeFragment(new Fragment2());
}
});
return v;
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment2, container, false);
Button tv = (Button)v.findViewById(R.id.button2);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 直接呼叫FragmentActivity1的靜態方法來做切換
FragmentActivity1.changeFragment(new Fragment1());
}
});
return v;
}
}

同樣,在TabHost新增中跟普通的Activity一樣:

TabSpec tabSpec=mtabHost.newTabSpec("1").setIndicator("TAB1").setContent(
new Intent(this, FragmentActivity1.class));
mtabHost.addTab(tabSpec);

這三者的layout都很簡單,就不寫了。對比ActivityGroup方法發現,其實兩者實現思路是一樣的,都是先用一個“容器”(ActivityGroup和FragmentActivity)佔住Tab1的位置,然後每次相應時,通過改變“容器”內的元素來達到動態改變的效果,區別就是ActivityGroup存放的是Activity,而FragmentActivity存放的是Fragment。(本人目前已經知道可以將Fragment巢狀到Activity中,是否有方法可以將Activity巢狀到Fragment中呢?那麼就可以達到兩個方法的完全相容了,而不用像我我擔心的,需要改造目前已有的Activity成Fragment來達到使用FragmentActivity替換ActivityGroup的效果)

最後說下ActionBar+FragmentActivity+Fragment來替換掉TabHost。在使用過程中發現這個ActionBar限制有點多,於是捨棄了,貼下實現程式碼。

public class FragmentActivity1 extends FragmentActivity implements ActionBar.TabListener{
public static FragmentManager fm;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_acitivity_1);
fm = getSupportFragmentManager();
ActionBar actionBar=this.getActionBar();
this.getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
this.getActionBar().setDisplayShowTitleEnabled(false);
this.getActionBar().setDisplayShowHomeEnabled(false);
setBar("Tab 1");
setBar("Tab 2");
setBar("Tab 3");
setBar("Tab 4");
// 只當容器,主要內容已Fragment呈現
initFragment(new Fragment1());
}
/**
*
*/
private void setBar(String s) {
Tab tab = this.getActionBar().newTab();
tab.setContentDescription(s);
tab.setText(s);
tab.setTabListener(this);
getActionBar().addTab(tab);
}
// 切換Fragment
public static void changeFragment(Fragment f){
changeFragment(f, false);
}
// 初始化Fragment(FragmentActivity中呼叫)
public static void initFragment(Fragment f){
changeFragment(f, true);
}
private static void changeFragment(Fragment f, boolean init){
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.simple_fragment, f);
if(!init)
ft.addToBackStack(null);
ft.commit();
}
@Override
public void onTabReselected(Tab arg0, android.app.FragmentTransaction arg1) {
// TODO Auto-generated method stub }
@Override
public void onTabSelected(Tab arg0, android.app.FragmentTransaction arg1) {
// TODO Auto-generated method stub
if(arg0.getText().equals("Tab 1"))
{
changeFragment(new Fragment1());
}
else {
changeFragment(new Fragment2());
}
}
@Override
public void onTabUnselected(Tab arg0, android.app.FragmentTransaction arg1) {
// TODO Auto-generated method stub }
}

在minSdkVersion設定為11後就能呼叫getActionBar()方法了,可以給它新增tab,然後重點是為ActionBar.TabListener寫點選Tab時要實現的功能,這裡我進行了Fragment的切換,以達到TabHost相同的效果。要注意的是如果該FragmentActivity是巢狀在其他Activity中(比方說TabHost)時,getAction將會返回null,即無法進行分頁,其他情況下也有可能返回null,限制還是比較多的。