社会人1年目エンジニアのブログ

if you can't explain it simply, you don't understand it well enough.

TabLayout + ViewPager + FontAwesomeでタブ遷移を実装してみた

前回の記事の続き

[Android]フラグメントについてまとめ - 社会人1年目文系エンジニアのブログ

TabLayout + ViewPager + FontAwesomeを使って以下のようなタブ遷移を実装しました

f:id:chonesu:20161210230257g:plain

アクティビティ

public class MainActivity extends AppCompatActivity {

    // タブメニューのインデックス
    private static final int INDEX_HOME = 0;
    private static final int INDEX_DIALOG = 1;
    private static final int INDEX_WEBVIEW = 2;

    // タブレイアウト
    private TabLayout mTabLayout;

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

        // TabLayoutにTabを追加
        mTabLayout = (TabLayout) findViewById(R.id.tablayout);
        mTabLayout.addTab(mTabLayout.newTab());
        mTabLayout.addTab(mTabLayout.newTab());
        mTabLayout.addTab(mTabLayout.newTab());

        // ViewPager
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);

        // 各コンテンツとなるフラグメントをセットするアダプターをViewPagerにセット
        // Fragmentを操作するためにコンストラクタの引数にFragmentManagerを渡しスーパークラスにセットします。
        MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

        // アダプターに各ページ要素となるフラグメントを追加
        pagerAdapter.addFragment(SimpleTextFragment.newInstance(INDEX_HOME, "ホーム画面"));
        pagerAdapter.addFragment(SimpleTextFragment.newInstance(INDEX_DIALOG, "ダイアログフラグメント画面"));
        pagerAdapter.addFragment(SimpleTextFragment.newInstance(INDEX_WEBVIEW, "WEBVIEW画面"));

        // ViewPagerにアダプタをセット
        viewPager.setAdapter(pagerAdapter);

        // TabLayoutとViewPagerをバインド
        mTabLayout.setupWithViewPager(viewPager);

        // fontawesome使いたいのでタブレイアウトをカスタム
        setUpCustomTab();
    }

    /**
    * カスタムタブレイアウトをセット
    */
    private void setUpCustomTab() {
        for (int i = 0; i < mTabLayout.getTabCount(); i++) {
            TabLayout.Tab tab = mTabLayout.getTabAt(i);
            if (tab == null) {
                continue;
            }
            if (i == INDEX_HOME) { // ホーム
                tab.setCustomView(createCustomTabView(getString(R.string.fa_home)));
            } else if (i == INDEX_DIALOG) { // ダイアログフラグメント
                tab.setCustomView(createCustomTabView(getString(R.string.fa_commenting)));
            } else if (i == INDEX_WEBVIEW) { // WebView
                tab.setCustomView(createCustomTabView(getString(R.string.fa_globe)));
            }
        }
    }

    /**
    * タブごとにレイアウトをインフレートしてmenuアイコンをセットしたビューを返します。
    * 
    * @param icon fontawesomeのUnicode
    * @return View カスタムビュー
    */
    private View createCustomTabView(String icon) {
        // カスタムタブビューのレイアウトをインフレートします。
        LayoutInflater inflater = LayoutInflater.from(this);
        View customView = inflater.inflate(R.layout.tab_item_layout, null);
        // アセットから読み込んだfontawesomeをビューにセットします。
        TextView tabIcon = (TextView) customView.findViewById(R.id.tab_icon);
        Typeface font = Typeface.createFromAsset(getAssets(), "fontawesome-webfont.ttf");
        tabIcon.setTypeface(font);
        tabIcon.setText(icon);
        return customView;
    }
}

fontawesomeを使う方法は以下参照

Android アプリで Font Awesome を利用する – アカベコマイリ

アダプタ

public class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

    private List<Fragment> mFragments = new ArrayList<>();

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

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

    public void addFragment(Fragment ft) {
        mFragments.add(ft);
    }

}

onCreateで開発者がaddFragmentを書くことが強制されてないのであんまりよくない気もする。
コンストラクタで渡したほうがいいのかもしれない

各ページのフラグメント

public class SimpleTextFragment extends Fragment {


    /**
     * staticファクトリーメソッド
     * Bundleで値をセットしたこのクラスのインスタンスを返します。
     *
     * @param index ページ番号
     * @param title 画面のタイトル
     * @return SimpleTextFragment フラグメント
     */
    public static SimpleTextFragment newInstance(int index,String title) {
        Bundle args = new Bundle();
        args.putString("title",title);
        args.putInt("index",index);
        SimpleTextFragment fragment = new SimpleTextFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.simple_fragment_layout,container,false);
        Bundle args = getArguments();
        String title = args.getString("title");
        ((TextView) view.findViewById(R.id.title)).setText(title);
        return view;
    }
}

今回は遷移が実装できればよかったので同じクラスの別インスタンスをアダプタにセットしてるだけです

メインレイアウト

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:background="@color/bg_gray"
    tools:context="jp.recruit.practicefragment.MainActivity">
    <android.support.v4.view.ViewPager
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/viewpager">
        <android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabBackground="@color/white"
            android:id="@+id/tablayout"
            app:tabIndicatorHeight="3dp"
            android:layout_gravity="top"/>
    </android.support.v4.view.ViewPager>
</LinearLayout>

公式参照

TabLayout | Android Developers

タブレイアウト

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textSize="25dp"
        android:textColor="@drawable/icon_selector"
        android:id="@+id/tab_icon" />
</LinearLayout>

タブ一個のレイアウト。
このテキストビューにfontawesome指定でtextをセットする
drawableじゃないから変なメモリリークとかおきないし、解像度別に画像用意しなくていい。

セレクタ

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true"
          android:color="@color/colorAccent" />
    <item android:state_selected="false"
          android:color="@color/icon_gray" />
</selector>

タブが選択されていたら色を変えるシンプルなセレクタ

感想

ただタブ遷移するだけならすぐ実装できてすごいなぁと思いました。 公式のデザインガイド的にはタブはページ上部に設置するようにとのことです。
多分キーボードとの兼ね合いだと思います。

次回

1個のタブの中でさらに遷移したい場合viewpagerではどうやって実装するのがベストプラクティスなんだろう。。
というのを割とずっと考えているので次回はそのあたりまとめたいと思います