Collapsing Toolbar 内の TabLayout
2015年5月にリリースされた Android Design Support Library では、 マテリアルデザインなアプリケーションを実装するために便利なコンポーネントが多数サポートされています。
ここでは、タブ (tabs) の実装をサポートする、TabLayout の使い方について説明します。
タブはご存知のとおり、マテリアルデザインを意識せずとも、情報のカテゴライズに非常に役に立つユーザーインターフェイス要素ですよね。
特にマテリアルデザインの指針では次のように記載されています。
「タブを使うと、アプリケーション内の異なるビュー間あるいは機能的な様々な側面を探索し切り替えたり、 カテゴリー分けされたデータの集合をブラウズすることが容易になります」
ここでは折りたたみツールバー内でタブを利用する方法を紹介します。
Collapsing Toolbar 内のタブ
ここでは次のような UI を作成します。
一つ目のタブはリストになっていて上下にスクロールできます。それに伴って、ツールバーも伸縮します。 ツールバーが縮みきるとタブだけが画面上部に残ります。
Collapsing Toolbar については 「Collapsing Toolbar (折りたたみツールバー) の典型的な実装方法」 で説明していますのでそちらをみてください。
リソース
レイアウト
メインアクティビティのレイアウトリソースは次の通り。
res/layout/main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layout_gravity="bottom"
app:layout_anchor="@+id/appBarLayout"
app:layout_anchorGravity="bottom"
app:tabGravity="fill"
app:tabMode="fixed"
style="@style/MyTabLayoutStyle"/>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="256dp"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|enterAlways"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
android:fitsSystemWindows="true"
app:title="Hello, title!">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/flower"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="parallax"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
TabLayout に設定しているスタイル (MyTabLayoutStyle) は下記に示します。タブ内の文字の色を変えるために設定しています。
さて、一つ目のタブには次のように RecyclerView を利用して、リストを表示しています。
res/layout/tab1.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</RelativeLayout>
RecyclerView の要素に当たるレイアウトは次の通り。
res/layout/my_row1.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_margin="16dp"
android:textSize="30sp"/>
</RelativeLayout>
タブ2とタブ3では特に何もしていなくて、単に TextView で文字を表示しているだけです。
res/layout/tab2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 2"/>
</RelativeLayout>
res/layout/tab3.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 3"/>
</RelativeLayout>
色
res/values/colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#009688</color>
<color name="colorPrimaryDark">#00796B</color>
<color name="colorAccent">#FFEB3B</color>
</resources>
タブの選択を示すインジケータ・ラインはアクセント色が利用されます。
スタイル
ここでは選択されているタブの文字を白色、選択されていないタブの文字を灰色にするために、 カスタムのスタイルを作成しています。
res/values/styles.xml
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="MyTabLayoutStyle" parent="Base.Widget.Design.TabLayout">
<item name="tabSelectedTextColor">#ffffff</item>
<item name="tabTextAppearance">@style/MyTabText</item>
</style>
<style name="MyTabText" parent="TextAppearance.AppCompat.Button">
<item name="android:textColor">#eeeeee</item>
</style>
</resources>
Java ソースコード
メインアクティビティ
ViewPager と TabLayout を利用します。
MainActivity.java
package com.keicode.android.test2.collapsingtoolbar1;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView.Adapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
final PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(pagerAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.options, menu);
return true;
}
}
PagerAdapter を次のように実装し、タブ 1, 2, 3 それぞれで Fragment1, 2, 3 がロードされるようにします。
PagerAdapter.java
package com.keicode.android.test2.collapsingtoolbar1;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
public class PagerAdapter extends FragmentStatePagerAdapter {
int numberOfTabs;
public PagerAdapter(FragmentManager fm, int numberOfTabs){
super(fm);
this.numberOfTabs = numberOfTabs;
}
@Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new Fragment1();
case 1:
return new Fragment2();
case 2:
return new Fragment3();
}
return null;
}
@Override
public int getCount() {
return numberOfTabs;
}
}
タブそれぞれのフラグメント
今回はタブ毎に異なるフラグメントを設定しています。タブ1には Fragment1 をロードしています。Fragment1 では RecyclerView でリストを表示しています。他の二つは単に文字を表示しているだけでスクロールもできません。
Fragment1.java
package com.keicode.android.test2.collapsingtoolbar1;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class Fragment1 extends Fragment {
RecyclerView recyclerView;
private RecyclerView.Adapter mAdapter;
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle saveInstanceState){
return inflater.inflate(R.layout.tab1, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Recycle View
RecyclerView recyclerView = (RecyclerView) getView().findViewById(R.id.recyclerView);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
ArrayList<String> dataList = new ArrayList<>();
for(char ch='A'; ch<='Z'; ch++) {
dataList.add(Character.toString(ch));
}
mAdapter = new MyAdapter(dataList);
recyclerView.setAdapter(mAdapter);
}
}
RecyclerView のアダプターは次の通り。
MyAdapter.javapackage com.keicode.android.test2.collapsingtoolbar1;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ItemViewHolder> {
ArrayList<String> mArrayList;
public static class ItemViewHolder extends RecyclerView.ViewHolder{
public TextView mTextView;
public ItemViewHolder(View v){
super(v);
mTextView = (TextView) v.findViewById(R.id.textView1);
}
}
public MyAdapter(ArrayList<String> arrayList){
this.mArrayList = arrayList;
}
@Override
public ItemViewHolder onCreateViewHolder( ViewGroup parent, int viewType ){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_row1, parent, false);
return new ItemViewHolder(v);
}
@Override
public void onBindViewHolder(ItemViewHolder holder ,int position){
final String data;
data = mArrayList.get(position);
holder.mTextView.setText(data);
}
@Override
public int getItemCount(){
return mArrayList.size();
}
}
残りのフラグメントは次の通り。特に何もしてません。
Fragment2.java
package com.keicode.android.test2.collapsingtoolbar1;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment2 extends Fragment {
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle saveInstanceState){
return inflater.inflate(R.layout.tab2, container, false);
}
}
Fragment3.java
package com.keicode.android.test2.collapsingtoolbar1;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment3 extends Fragment {
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle saveInstanceState){
return inflater.inflate(R.layout.tab3, container, false);
}
}
以上で、上記のスクリーンショット (動画) のような動きが実現できるはずです。