タブ内でのフラグメント(Fragment)の切り替え

複数のタブでフラグメントの切り替えを行う場合、あるタブのフラグメントを置き換えると別のタブのフラグメントの情報が表示されなくなってしまいます。

そこでタブ内でのフラグメントの切り替えを行うときは次の手順で1つ1つのフラグメントを保存できます。

  1. 親フラグメントの作成
  2. アクティビティからフラグメントの切り替え

これを画像で表現すると次のようになります。

親フラグメントのイメージ図

1つのフラグメントを親にしてその中に複数の子フラグメントが入るようなイメージです。

親フラグメントの作成

例えば親フラグメントのレイアウトをroot_fragment.xmlとすると次のようなファイルになります。

<?xml version="1.0" encoding="utf-8" ?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/fragment" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" />

idを"fragment"にしています。

また、親フラグメントのコードは次のようになります。

public class RootFragment extends Fragment
{
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.root_fragment, container, false);
        return view;
    }
    
    public void onActivityCreated(Bundle savedInstanceState)
    {
        super.onActivityCreated(savedInstanceState);

        Bundle args = getArguments();
        String rootClass = args.getString("root");

        initFragment(rootClass);
    }
    
    /**バックスタックを取り出す。ある場合はtrue、ない場合はfalseを返す。*/
    public boolean popBackStack()
    {
        return getChildFragmentManager().popBackStackImmediate();
    }
    
    /**クラス名からフラグメントを設置する。すでに設置済みなら何もしない。*/
    private void initFragment(String fragmentClassName)
    {
        FragmentManager fm = getChildFragmentManager();

        if(fm.findFragmentById(R.id.fragment) != null){
            return;
        }
        
        FragmentTransaction ft = fm.beginTransaction();
        Fragment fragment = Fragment.instantiate(getActivity(), 
                                                fragmentClassName);
        ft.replace(R.id.fragment, fragment);
        ft.commit();

    }
}

簡単に説明するとこのフラグメントの起動時に渡される子フラグメントのクラス名に応じて中のフラグメントを切り替えています。

ただし、すでに同じフラグメントが設置済みなら何もしないように設定しています。

アクティビティ側の設定

アクティビティではFragmentTabHostを使った次のようなレイアウトを使います。

<android.support.v4.app.FragmentTabHost
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/tab_host" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" >
    <LinearLayout
        android:orientation="vertical" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" >
        <TabWidget             
            android:id="@android:id/tabs" 
            android:orientation="horizontal" 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:layout_weight="0" /> 
        <FrameLayout             
            android:id="@android:id/tabcontent" 
            android:layout_width="0dp" 
            android:layout_height="0dp" 
            android:layout_weight="0" /> 
            <FrameLayout
                android:id="@+id/real_content" 
                android:layout_width="match_parent" 
                android:layout_height="0dp" 
                android:layout_weight="1" />    
    </LinearLayout> 
</android.support.v4.app.FragmentTabHost>

アクティビティのコード側では次のようにFragmentTabHostを使ってタブを切り替えます。

public class MainActivity extends FragmentActivity 
{

    public FragmentTabHost tabHost;
    final static String tabIds[] = {"TAB1", "TAB2"};

    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        tabHost = (FragmentTabHost) findViewById(R.id.tab_host);
        tabHost.setup(this, getSupportFragmentManager(), R.id.real_content);
        
        addTab(tabIds[0], "Tab1", Fragment1.class);
        addTab(tabIds[1], "Tab2", Fragment2.class);
    }
    
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        super.onKeyDown(keyCode, event);

        switch(keyCode){
        case KeyEvent.KEYCODE_BACK:
            //スタックを戻る。
            if(!getCurrentRootFragment().popBackStack()){
                return true;
            }else{
                return false;
            }
        }
        return false;
    }
    
    /**タブを追加する。*/
    public void addTab(String tag, String indector, Class<? extends Fragment> cls)
    {
        TabSpec tabSpec = tabHost.newTabSpec(tag).setIndicator(indector);
        Bundle args = new Bundle();
        args.putString("root", cls.getName());
        tabHost.addTab(tabSpec, RootFragment.class, args);
    }
    
    /**現在タブに設定されているフラグメントを取得する。*/
    private RootFragment getCurrentRootFragment()
    {
        return (RootFragment) getSupportFragmentManager().findFragmentById(R.id.real_content);
    }
}

バックボタンが押されたときに親フラグメントにスタックがあれば1つフラグメントを戻り、なければアプリを終了するようにしています。

addTabメソッドで親フラグメントに追加する初期フラグメントのクラス名を渡しています。

子フラグメントの切り替え例

フラグメントの切り替え方は通常のフラグメントの切り替え方と変わりません。

例えば次のような感じです。

FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.addToBackStack(null);
MyFragment fragment = new MyFragment();
ft.replace(R.id.fragment, fragment);
ft.commit();

親フラグメントと違いここではgetFragmentManagerメソッドを呼び出しているので1つのタブ内のフラグメントのみを管理できます。

あとは新しいフラグメントに切り替えるだけです。

関連項目
プライバシーポリシー