Android Tabhost with FragmentActivity

(2013-01-30 更新) 接續 Android TabHost中切換Activity 記錄了使用 ActivityGroup 達到在 TabHost 中切換 Activity 的方法,也在 Android Screen Orientation Event螢幕方向處理+Acitivity Liftcycle 記錄了當螢幕方向改變時的處理,這篇小蛙繼續記錄用 FragmentActivity 取代 ActivityGroup,透過 FragmentActivity 內建的 BackStack 來管理倒退歷程。
這個程式碼小蛙很早之前就實作完成,但礙於螢幕方向改變時的處理一直還找不到解決辦法,所以一直拖到現在,不囉嗦直接進程式碼。
MainTabActivity.java : 主要的 Tabhost Activity。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MainTabActivity extends Activity {
    private TabHost mHost;
    // 在Activity中使用Tabhost必須要有LocalActivityManager物件來設定
    LocalActivityManager lam;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // main layout採用預設的Tabhost
        mHost = (TabHost) findViewById(android.R.id.tabhost);
        lam = new LocalActivityManager(MainTabActivity.this, false);
        lam.dispatchCreate(savedInstanceState);
        mHost.setup(lam);
        mHost.addTab(mHost.newTabSpec("Tab1").setIndicator("Tab1").setContent(new Intent(MainTabActivity.this, FragmentActivity1.class)));
        mHost.addTab(mHost.newTabSpec("Tab2").setIndicator("Tab2").setContent(new Intent(MainTabActivity.this, FragmentActivity2.class)));
    }
    @Override
    protected void onPause() {
        // 漏掉這行一定出錯
        lam.dispatchPause(isFinishing());
        super.onPause();
    }
    @Override
    protected void onResume() {
        // 漏掉這行一定出錯
        lam.dispatchResume();
        super.onResume();
    }
}

FragmentActivity1.java : 第一個 Tab 中用來管理 Fragment 的 FragmentActivity。(2012-05-07更新) 經由 Jay 留言後,小蛙詳細測了一下,發現 FragmentActivity 主畫面中的 Button 是沒辦法消失的 (因為 FragmentActivity 的目的關係),因此改成這樣,讓 FragmentActivity 純粹當成容器,主要的內容還是以 Fragment 為主。(這個方法不是唯一,但是一個可行的方法,應該也有更好的做法!)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class FragmentActivity1 extends FragmentActivity {
    public static FragmentManager fm;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_activity_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();
    }
}

Fragment1.java : 真正使用到的 Fragment。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_1, 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;
    }
}

fragment_acitivity_1.xml : FragmentActivity layout。(2012-05-07修改) FragmentActivity 只用來當容器,而不真正呈現內容 (僅把 Fragment 內容載入)。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/simple_fragment">
</LinearLayout>

fragment1.xml:Fragment layout,FragmentActivity 載入的真正內容。

01
02
03
04
05
06
07
08
09
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />
</LinearLayout>

最後別忘了在 AndroidManifest.xml 中加入android:configChanges=”orientation” 設定。這樣就成功的使用在 Activity 中使用 Tabhost並且透過 FragmentActivity 來管理 Fragment 囉!

2013-01-30 許多網友反應無法下載 MediaFire 的檔案,補上新連結。範例檔下載

2012-10-22 範例檔下載

    21 個回應

    1. zhujinshan3表示:

      多谢lz,tab实现的方式茫茫多,得每种都研究研究找到最适合自己需求的实现方式

    2. zhujinshan3表示:

      LocalActivityManager现在是个过期的类,不太想使用它,而TabHost直接用setup()方法会报错,看了网上其他的解决办法没能很好的解决,不知道版主有没有考虑过这个问题

    3. dean表示:

      hi 非常感谢LZ共享这个方法,很实用。不过有个问题,一直没解决,请LZ帮忙看下,
      我在其中一个FRAGMENT中的 onCreateView 里调用了 startActivityForResult
      Intent securityIntent = new Intent(layoutView.getContext(),
      SecurityCheckActivity.class)
      startActivityForResult(securityIntent, SecurityCheckRequestCode);
      可是怎么都接收不到 onActivityResult事件,请问要怎么处理?

      • Hans表示:

        Dear dean:
        在 Fragment 下是沒有辦法接收到 startActivityForResult 結果的喔!
        把 startActivityForResult(securityIntent, SecurityCheckRequestCode);
        改成
        getActivity().getParent().startActivityForResult(securityIntent, SecurityCheckRequestCode);
        透過 MainTabActivity 來呼叫 startActivityForResult,
        同時也要使用 MainTabActivity 的 onActivityResult 來處理這個事件!
        Good Luck !!

    4. wasai表示:

      求Demo,谢谢~~~~~~
      [email protected]

    5. wanghao表示:

      楼主真是太厉害了,解决了我的一个大问题,不知能不能和您深入的交流下,对于这个问题我还有点疑惑。我的qq是22340991 期待您的回复。 祝新年快乐

      • Hans表示:

        Dear wanghao:
        不好意思喔,小蛙沒有用 QQ,網站上方有 email,可以直接寫 mail 給小蛙!^__^

    6. me表示:

      麻烦发一份demo到我邮箱 [email protected]谢谢了

    7. me表示:

      你好,我在找一个Tab的解决方案,要求如下:
      1. 多Tab,Tab页在下方,效果可以自定义.
      2. 每个Tab内可能会有多个子Activity.即在Tab也内新建的子Activity可以在Tab内部,也可以是是独立的Activity.
      3. 在每个Tab内子Actvitiy保证回退历史
      4. 能检测到已经到最后一个子Activity,并能提示用户退出
      你的代码我这里无法下载,无法知道最后的效果是否能满足我的需求.请问你是否方便把代码放到其他地方或者发一个给我[email protected]吗?谢谢.

    8. dean表示:

      hi 你好,我看了DEMO示例,也成功实现了基本的跳转。
      想请教下,怎么实现页面之前的传参呢?
      之前两个ACTIVITY之前可以通过Intent 用Buddle进行传参,
      可是fragment我还是摸不到头脑~~~

      • Hans表示:

        Dear dean:
        小蛙最近比較沒有時間去試看看,而之前開發的時候也沒有遇到這個需求,
        (寫這個範例只是想先知道怎麼用,然後記錄起來)
        不過小蛙問了一下同事,同事倒是提到可以試試看用fragment建構子的方式來傳遞,
        (至於可不可行小蛙就不清楚了,理論上聽起來應該是可以,實際上要試試看)
        Good Luck!!! ^_____^

        • dean表示:

          thx,我最近也是遇到这种需求才想起来研究的。
          主要就是想做一个一直存在的tabhost。继续研究,感谢

    9. jay表示:

      蛙大你好,
      我也是正要從TabHost中切換Activity的實作方式,改成Fragment Activity
      想請問有此一範例的程式碼嗎?因為我有點搞迷糊了
      是不是改成Fragment Activity後
      原先在TabHost切換Activity的裡的child activiy換到fragment activity就變成了fragment
      然後透過fragment activity的FragmentManager來切換各個fragment,並將其內容在id名稱為simple_fragmen的fragment layout當中做切換?

      • Hans表示:

        Dear Jay:
        不好意思造成您的困擾,小蛙今天看了一下Code,才發現問題 ~ >_< 現在文章已經更新囉!先下載範例看看效果是不是自己要的, 不好意思!>____< 您說的沒錯原本的 TabHost 中變成啟動的是 FragmentActivity, 而新的作法中FragmentActivity當成容器,實際呈現以Fragment為主! 範例檔

    發佈留言

    發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

    這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料