2012年10月16日火曜日

Android ActionBarとFragmentを使用してTab画面を表示する(Android 4.0以上)

以前Android TabActivityとTabHostを使用してTab画面を表示するで作成したTab画面はTabActivityを継承し作成していましたが
Android3.xよりTabActivityは非推奨になったようです。

TabActivityを使用しないでTab画面を作成する方法です。
Android3.0より導入されたActionBarとFragmentを使用します。

対象:
Build SDK:Android 4.0 (API 14) / Min SDK:Android 4.0 (API 14)

Android 2.x でActionBarとFragmentを使用してTab画面を表示する方法はコチラ
Android ActionBarとFragmentを使用してTab画面を表示する(Android 2.x)

まずメインとなるアクティビティを作成します。
android.app.Activityを継承して作成します。
import android.os.Bundle;
import android.view.Menu;
import android.app.Activity;

public class MainActivity extends Activity {

     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
     }

     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
          getMenuInflater().inflate(R.menu.activity_main, menu);
          return true;
     }

}

次に各タブのコンテンツとなるFragmentを作成します。
こちらはandroid.app.Fragmentを継承して作成します。
※android.support.v4.app.FragmentはAndroid2.xでFragmentを使用できるようにサポートされているパッケージです。
今回はandroid4.0以上が対象なのでandroid.app.Fragmentを使用します。
レイアウトはそれぞれの違いがわかるように適当に変更しておいてください。
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab1Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        return inflater.inflate(R.layout.fragment_tab1, container, false);
    }
}
今回は面倒なので2タブにします。
同じように2タブめに表示するFragmentを作成します。
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab2Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        return inflater.inflate(R.layout.fragment_tab2, container, false);
    }
}

次にタブを変更したときのリスナーを作成します。
ActionBar.TabListenerインターフェースを実装します。
このクラスの各メソッドの引数「FragmentTransaction」はバグっており常にnullになっているため使用できません。
2012/12/11追記
縦横を切り替えたときFragment#OnCreateが2回走る不具合があり、
コンストラクタでFragmentを探すように変更し
onTabSelectedでもdetachされていないときだけattachするよう変更しました。
詳しくは・・・
Android ActionBarとFragmentを利用したTab画面でFragment#OnCreateが2回走る
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ActionBar.Tab;


public class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;
    
    /**
     * コンストラクタ
     * @param activity
     * @param tag
     * @param clz
     */
    public TabListener(Activity activity, String tag, Class clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        //FragmentManagerからFragmentを探す。  2012/12/11 追記    
    mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);      
    }

    /**
     * @brief タブが選択されたときの処理
     */
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        //ftはnullなので使用できない        
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            FragmentManager fm = mActivity.getFragmentManager();
            fm.beginTransaction().add(R.id.container, mFragment, mTag).commit();
        } else {
             //detachされていないときだけattachするよう変更   2012/12/11 変更   
            //FragmentManager fm = mActivity.getFragmentManager();   
            //fm.beginTransaction().attach(mFragment).commit();   
            if (mFragment.isDetached()) {      
                FragmentManager fm = mActivity.getFragmentManager();      
                fm.beginTransaction().attach(mFragment).commit();      
             }    

        }
    }
    /**
     * @brief  タブの選択が解除されたときの処理
     */
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            FragmentManager fm = mActivity.getFragmentManager();
            fm.beginTransaction().detach(mFragment).commit();
       }    
    }
    /**
   * @brief タブが2度目以降に選択されたときの処理
     */
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}

それではメインアクティビティに戻ります。
メインアクティビティではonCreateメソッドでActionBarを取得し、NavigationModeをタブにします。
後はActionBarにタブのコンテンツとなるFragmentを追加していくだけです。
import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        actionBar.addTab(actionBar.newTab() 
                .setText("ページ1") 
                .setTabListener(new TabListener( 
                        this, "tag1", Tab1Fragment.class))); 
        actionBar.addTab(actionBar.newTab() 
                .setText("ページ2") 
                .setTabListener(new TabListener( 
                        this, "tag2", Tab2Fragment.class))); 
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.activity_main, menu);
         return true;
    }

}

0 件のコメント: