電脳戯言記

プログラムのこととか日々のどうでもいいこととかをつらつらと記します。 Twitter ID: @nagakenjs

ActionBarSherlockでActionBarとViewPagerを連携する

ActionBarSherlockのFragmentデモにあるTabs and Pagerは、OSネイティブのタブUIを使っているため2.3以前の端末で動かしたときと4.0以降で動かしたときで表示が異なります。

そこで、ActionBarのタブとViewPagerを連携することでどの端末でも見た目を統一できます。

ソースコード全体は以下に置きました。

ダミーFragmentの作成

各タブに表示するためのダミーFragmentを作ります。

public class DummyFragment extends SherlockFragment {
    private int mNum;

    public static DummyFragment getInstance(int num) {
        DummyFragment fragment = new DummyFragment();

        Bundle args = new Bundle();
        args.putInt("num", num);
        fragment.setArguments(args);

        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle args = this.getArguments();
        mNum = (args != null) ? args.getInt("num") : 0;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        TextView view = (TextView) inflater.inflate(R.layout.hello_world, container, false);
        view.setText("Tab" + mNum);

        return view;
    }
}

ダミーFragmentのXMLファイル(hello_world.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/hello_text"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical|center_horizontal"
    android:text="@string/hello_world"
    android:textAppearance="?android:attr/textAppearanceMedium" />

FragmentPagerAdapterの作成

ViewPagerを管理するためにFragmentPagerAdapterのサブクラス(TabAdapterクラス)を作ります。

ActionBarでタブを使えるようにするためにActionBar.NAVIGATION_MODE_TABSを設定します。
mViewPager.setOnPageChangeListener()で設定するイベントについては後述します。

public TabAdapter(SherlockFragmentActivity activity, ViewPager pager) {
    super(activity.getSupportFragmentManager());

    mActionBar = activity.getSupportActionBar();
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    mViewPager = pager;
    mViewPager.setOnPageChangeListener(mOnPageChangeListener);
}

@Override
public Fragment getItem(int position) {
    TabInfo info = mTabs.get(position);
    if (info != null) {
        if (info.fragment == null) {
            info.fragment = DummyFragment.getInstance(position);
        }
        return info.fragment;
    } else {
        return new Fragment();
    }
}

@Override
public int getCount() {
    return this.mTabs.size();
}

タブの追加処理

タブを追加できるよう、TabAdapterクラスにメソッドを追加します。
setTabListener()で設定するイベントについては後述します。

private final SparseArray<TabInfo> mTabs = new SparseArray<TabInfo>();

public void addTab(String tag, Class<?> clazz, Bundle args) {
    ActionBar.Tab tab = this.mActionBar.newTab();
    tab.setText(tag);
    tab.setTabListener(mTabListener);
    this.mActionBar.addTab(tab);

    TabInfo info = new TabInfo(tag, clazz, args);
    mTabs.put(tab.getPosition(), info);
}

タブ情報の管理クラス(TabInfoクラス)以下のように定義しました。

static final class TabInfo {
    public final String tag;
    public final Class<?> clss;
    public final Bundle args;
    public Fragment fragment;

    TabInfo(String _tag, Class<?> _class, Bundle _args) {
        tag = _tag;
        clss = _class;
        args = _args;
    }
}

タブ制御イベントの追加

ActionBarのタブをタップしたときの制御イベントを定義します。
ActionBarとViewPagerは別制御なので、タブを選択したときにViewPagerを選択するようonTabSelected()メソッドに処理を追加します。

private final ActionBar.TabListener mTabListener = new ActionBar.TabListener() {
    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
};

次にViewPagerからActionBarを制御するための制御イベントを定義します。
ViewPagerのページが切り替わったらActionBarのタブも切り替わるようにonPageSelected()メソッドに処理を追加します。

private final ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageSelected(int position) {
        mActionBar.selectTab(mActionBar.getTabAt(position));
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }
};

Activityからの呼び出し

SherlockFragmentActivityのonCreateでタブの管理処理を呼び出します。

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

    ViewPager pager = (ViewPager) findViewById(R.id.pager);
    TabAdapter tabAdapter = new TabAdapter(this, pager);
    pager.setAdapter(tabAdapter);

    tabAdapter.addTab("Tab1", DummyFragment.class, null);
    tabAdapter.addTab("Tab2", DummyFragment.class, null);
    tabAdapter.addTab("Tab3", DummyFragment.class, null);
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <FrameLayout
        android:id="@android:id/tabcontent"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_weight="0" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

これでAndroid 4.0より前の端末でも4.0のようなTab+ViewPagerが使えるようになります。
f:id:xion:20121223175005p:plain