2014年12月9日火曜日

Android Material Design な TabHost + ViewPager に移行する

前回のエントリ「Android Material Design のタブの仕様まとめ」で紹介した Material Design の Fixed Tabs full-screen width を TabHost で実装してみます。

Activity 生成ウィザードの Tabbed Activity で Swipe Views(ViewPager)を選択して作成される Activity に付け足していきます。

*以下の例は minSdkVersion = 21 としています。


0. テーマに色をセット

メインカラーとアクセントカラーをセットしておきます

res/values-v21/styles.xml <?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar"> <item name="android:colorPrimaryDark">#00695C</item> <item name="android:colorPrimary">#00897B</item> <item name="android:colorAccent">#FFD54F</item> </style> </resources>


1. 普通に TabHost を実装する <?xml version="1.0" encoding="utf-8"?> <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"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/colorPrimary"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" /> </TabHost> <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost); tabHost.setup(); for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { tabHost.addTab(tabHost .newTabSpec(String.valueOf(i)) .setIndicator(mSectionsPagerAdapter.getPageTitle(i)) .setContent(android.R.id.tabcontent)); } tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { @Override public void onTabChanged(String tabId) { mViewPager.setCurrentItem(Integer.valueOf(tabId)); } }); mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){ @Override public void onPageSelected(int position) { super.onPageSelected(position); tabHost.setCurrentTab(position); } }); }

基本的なタブ機能が実装できましたが、Material Design に沿ったタブにするにはいくつか修正が必要です。
  • タブ間の区切り線を消す
  • Action Bar と タブの間の影を消す
  • タブの下に影を出す
  • タブの文字を仕様に合わせる
  • ViewPager のスクロールに応じてインディケータもスクロールさせる


2. タブ間の区切り線を消す @Override protected void onCreate(Bundle savedInstanceState) { ... TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs); // タブ間の区切り線を消す tabWidget.setStripEnabled(false); tabWidget.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE); }


3. Action Bar と タブの間の影を消す @Override protected void onCreate(Bundle savedInstanceState) { ... // Action Bar と タブの間の影を消す getActionBar().setElevation(0); }


4. タブの下に影を出す @Override protected void onCreate(Bundle savedInstanceState) { ... // タブの下に影を出す // App bar の elevation は 4dp // http://www.google.com/design/spec/what-is-material/objects-in-3d-space.html#objects-in-3d-space-elevation float elevation = 4 * getResources().getDisplayMetrics().density; tabHost.setElevation(elevation); }


5. タブの文字を仕様に合わせる

res/color/tab_widget_text.xml <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="#ffffffff" android:state_selected="true" /> <item android:color="#99ffffff" /> </selector> res/layout/tab_widget.xml <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="bottom|center_horizontal" android:paddingBottom="16dp" android:textColor="@color/tab_widget_text" android:textSize="14sp" tools:layout_width="match_parent" tools:text="group 1 ITEM TWO" /> @Override protected void onCreate(Bundle savedInstanceState) { ... LayoutInflater inflater = LayoutInflater.from(this); for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { TextView tv = (TextView) inflater.inflate(R.layout.tab_widget, tabWidget, false); tv.setText(mSectionsPagerAdapter.getPageTitle(i)); tabHost.addTab(tabHost .newTabSpec(String.valueOf(i)) .setIndicator(tv) .setContent(android.R.id.tabcontent)); } ... }


6. ViewPager のスクロールに応じてインディケータもスクロールさせる <?xml version="1.0" encoding="utf-8"?> <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"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/colorPrimary"> ... <View android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="2dp" android:layout_gravity="bottom" android:background="?android:attr/colorControlActivated" /> </TabHost> ... </LinearLayout> View indicator; TabWidget tabWidget; @Override protected void onCreate(Bundle savedInstanceState) { ... tabWidget = (TabWidget) findViewById(android.R.id.tabs); ... indicator = findViewById(R.id.indicator); mViewPager.setOnPageChangeListener(new PageChangeListener()); } private class PageChangeListener implements ViewPager.OnPageChangeListener { private int scrollingState = ViewPager.SCROLL_STATE_IDLE; @Override public void onPageSelected(int position) { // スクロール中はonPageScrolled()で描画するのでここではしない if (scrollingState == ViewPager.SCROLL_STATE_IDLE) { updateIndicatorPosition(position, 0); } tabWidget.setCurrentTab(position); } @Override public void onPageScrollStateChanged(int state) { scrollingState = state; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { updateIndicatorPosition(position, positionOffset); } private void updateIndicatorPosition(int position, float positionOffset) { View tabView = tabWidget.getChildTabViewAt(position); int indicatorWidth = tabView.getWidth(); int indicatorLeft = (int) ((position + positionOffset) * indicatorWidth); final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) indicator.getLayoutParams(); layoutParams.width = indicatorWidth; layoutParams.setMargins(indicatorLeft, 0, 0, 0); indicator.setLayoutParams(layoutParams); } }


2 件のコメント:

  1. このコメントは投稿者によって削除されました。

    返信削除
  2. SectionsPagerAdapterに、シンボルがありません。と出ます。どこで設定すればいいでしょうか?

    返信削除