当前项目使用的是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,限制还是比较多的。