Androidのアプリ開発をはじめようと考えている方、もしくははじめたばかりの方向けにお届けしている 【はじめてのAndroidアプリ開発】 シリーズ。 今回は 『タブ機能の使い方』をご紹介。
RSS系や SNS系など多くのアプリに欠かせないタブデザイン、ストレスフリーで使いこなしたいですよね。
★ご紹介するタブの内容★
たくさんのタブを設置
タブにアイコンを設定
タブ毎にレイアウトや機能を設定
★本稿はこんな方に役立ちます★
Android アプリ開発初心者
RSS系のアプリを考えておられる方
Javaをもっと知りたい方
★開発環境★
・ Android Studio 3.4
・ Java
・ エミュレーター Android 9 (Pie)
・ Android Studio の標準テンプレート(Tabbed Activity)を使用
【はじめてのAndroidアプリ開発】タブ機能を使う タブ機能を準備する様子
VIDEO
Android Studio の Tabbed Activity ではじめてプロジェクトを起動した時、Java のクラス間の関係やレイアウトファイルの使用用途が分かりにくいと思います。少し時間をかけて読み解いていったり、分からないところで Logcat の出力を行うなどして各コードの働きを理解していきましょう。
タブ機能にご興味ある方は、この後のチュートリアルを参考にしてみて下さい。
タブデザインの採用方法
Android Studio でタブデザインを採用する場合は、新規プロジェクトの作成時に Tabbed Activity を選べば簡単にタブデザインを利用可能。 Tabbed Activity のプレーン状態からタブへのアイコンセット、それからタブ毎にレイアウトや機能制御できる方法を紹介していきます。
まずはいつも通り新規プロジェクトの作成から始めましょう。
Tabbed Activity を選択。
プロジェクト名のセット。
Tabbed Activity の新規プロジェクトが立ち上がったら、一度エミュレータを起動して、タブの様子を確認してみましょう。Tabbed Activity では、二つのタブが用意されていて、タブによって
Hello world from section: ○
の ○ の数字が変わります。 表示されている文字の内容は一定で、番号だけが変化。一体どういう風にプログラム制御されているのか、一度 Tabbed Activity で作成されたプログラム内容を確認してみましょう。
画層クリックで拡大
まず画面のレイアウトについては、
全体レイアウトを activity_main.xml
タブ内のレイアウトを fragment_main.xml
で制御されています。
そして表示されている文字の制御については、 Java プログラムで実行。
全体管理を MainActivity
タブ全体の管理を SectionsPagerAdapter
タブとタブ内容の制御を PlaceholderFragment
タブの中身を PageViewModel
次はファイル構成ではなく、プログラム・フローから Tabbed Activity の動きを確認してみたいと思います。
画層クリックで拡大
まずアプリ起動時に読み込まれる MainActivity から見ていきます。 コードを上から順に辿っていくと、画面レイアウトを構成する activity_main.xml が読み込まれ、その activity_main.xml には「タイトル」「タブ」「タブの中身」がセット。この時「タブの中身」はテキストではなく、表示枠だけ設定されていることが確認できます。
画層クリックで拡大
activity_main.xml が読み込まれた後は、
sectionsPagerAdapterクラスがインスタンス化
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
タブの中身を設定
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
タブとタブの中身をリンク
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
という流れで、タブを制御。
ここで少し分かりにくいのが、タブの中身を制御する sectionsPagerAdapterクラスのインスタンス化 でしょう。上図を参考に sectionsPagerAdapter の設定プログラムを追っていきましょう。
Android アプリ開発の基礎学習では登場してこないような
@NonNull
Bundle() メソッド
setArguments()メソッド
Transformations.map()
などたくさんのキーワードがプログラム内に盛り込まれています。 また savedInstanceState など公式ドキュメントでは紹介されていないようなキーワードも登場。Tabbed Activity を詳しく理解しようと思うと、焦らずに時間をかけて一つずつ紐解いていく根気が必要となるでしょう。
タブ内に表示される文字の変更
Android Studio で Tabbed Activity からタブ機能を採用した際、タブに表示される文字は PageViewModel.java でセットされています。デフォルトの Tabbed Activity の場合、 定型文にタブの番号だけが表示。タブ機能を理解する上でも、タブ毎に表示される文字を変えてみたいと思います。
PageViewModel
package com.oshimamasara.tabsample001.ui.main;
import android.arch.core.util.Function;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Transformations;
import android.arch.lifecycle.ViewModel;
import android.util.Log;
public class PageViewModel extends ViewModel {
private static final String TAG = "MainActivity";
private MutableLiveData mIndex = new MutableLiveData<>();
private LiveData mText = Transformations.map(mIndex, new Function() {
@Override
public String apply(Integer input) {
Log.i(TAG,"PageViewのinput::"+input);
if(input==1){
return "TAB1の内容: " ;
} else{
return "TAB2の内容: " ;
}
}
});
public void setIndex(int index) {
mIndex.setValue(index);
}
public LiveData getText() {
return mText;
}
}
タブ毎にテキストの内容を変更したい場合は、タブの現在地を示す input の値に応じて内容制御すれば OK。例えば 2つタブがある場合は、
if(input==1){
return "TAB1の内容: " ;
} else{
return "TAB2の内容: " ;
}
と if 文を用いればタブに応じたテキストを設定できます。 またテキストのレイアウトについても、 fragment_ main.xml を編集すれば文字の大きさや色も変更可能。
画像クリックで拡大
このように Tabbed Activity を利用すると簡単にタブ機能を使うことができます。ただしタブ毎に細やかなレイアウトや画像、地図などのアプリケーション機能を設けようと思うと、別途専用の Java プログラムを用意する方が使いやすいと思います。このタブ毎の機能やレイアウトの設定については後述させていただきますね。
タブ数を増やす
デフォルトの Tabbed Activity では、タブが 2つしかありません。 タブの数を増やそうと思うとタブ機能全般を管理する SectionsPagerAdapter を編集すれば OK。 上図のように 配列 TAB_TITLES へ値を追加し、 getCount() の数値を変更すれば OK。
SectionsPagerAdapter
package com.oshimamasara.tabsample001.ui.main;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import com.oshimamasara.tabsample001.Frag01;
import com.oshimamasara.tabsample001.Frag02;
import com.oshimamasara.tabsample001.Frag03;
import com.oshimamasara.tabsample001.R;
/**
* A [FragmentPagerAdapter] that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
@StringRes
private static final int[] TAB_TITLES = new int[]{
R.string.tab_text_1,
R.string.tab_text_2,
R.string.tab_text_3,
R.string.tab_text_4,
R.string.tab_text_5,
R.string.tab_text_6,
R.string.tab_text_7,
R.string.tab_text_8};
private final Context mContext;
public SectionsPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
@Override
public Fragment getItem(int position) {
// 指定されたページのフラグメントをインスタンス化するためにgetItemが呼び出し
return PlaceholderFragment.newInstance(position + 1);
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
@Override
public int getCount() {
// Show 2 total pages.
return 8;
}
}
R.string.tab_text_3 などタブに応じた新しい文字列が必要となりますので、下記のように strings.xml を編集しました。
strings.xml
<resources>
<string name="app_name">私のサンプルアプリ</string>
<string name="tab_text_1">タブ1</string>
<string name="tab_text_2">タブ2</string>
<string name="tab_text_3">タブ3</string>
<string name="tab_text_4">タブ4</string>
<string name="tab_text_5">タブ5</string>
<string name="tab_text_6">タブ6</string>
<string name="tab_text_7">タブ7</string>
<string name="tab_text_8">タブ8</string>
</resources>
以上でタブの数を増やすことはできますが、上図にも示す通りタブのレイアウトが窮屈です。次はタブのレイアウトを少し編集していきましょう。
タブの幅を広げる方法
画像クリックで拡大
タブの数を増やして、タブレイアウトが窮屈になった際の「タブ表示幅」の調整方法をご紹介していきます。まずタブのレイアウトは activity_main.xml の
<android.support.design.widget.TabLayout
内で制御されています。ここの
android:layout_width="match_parent"
を
android:layout_width="600dp"
などに変更すれば簡単にタブ幅を変更できます。しかしこれでは、すべてのタブを表示することができません。この問題を解決する方法としては、タブにスクロール機能を設けると8番目のタブの内容も確認することが可能となります。
【layout_width】
・match_parent 画面幅に合わせて表示
・wrap_content 文字数に応じた幅で表示
・600dp 指定の幅で表示
タブにスクロール機能を追加
画像クリックで拡大
タブにスクロール機能を追加する方法としては activity_main.xml 内の
<android.support.design.widget.TabLayout
を
<HorizontalScrollView
と
<LinearLayout
で囲めば OK。
activity_main.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="?actionBarSize"
android:padding="@dimen/appbar_padding"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="600dp"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary" />
</LinearLayout>
</HorizontalScrollView>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
タブにスクロール機能を設けることで、たくさんのタブ情報を表示できますが、 あまりたくさんのタブを設けることは ❌ かもしれません。それはタブの場合、タブのページをタップした際にタブ内の情報が読み込まれるのではなく、表示されたタブすべてのタブ内情報が一度に読み込まれます。今回の場合であれば タブ 1から タブ 8まで全てのタブ内情報が一度に処理。タブ内の処理内容によっては、なかなか画面が表示されないという不具合原因につながってくるので要注意です。
タブにアイコンをセット
ニュース系のアプリでは、タブにアイコンがよくセットされていますよね。同じように、タブにアイコンを表示できるように設定方法をご紹介していきます。
まずは表示するアイコン画像から用意する必要があります。今回は Android Studio に標準で用意されているアイコン画像を利用して、タブ内にアイコンをセット。
上図のように res フォルダ下の drawable フォルダで右クリックし、 New → Image Asset を選択。
画像の設定画面が開いたら、 Icon Type を左クリックし、 Action Bar and Icons を選択。そして Name は、任意の値をセット。今回は ic_tab_home としました。あとは Clip Art でお好みのアイコン画像を選択し、 Next。
アイコン画像の保存先等が表示されます。概ねデフォルトのままで大丈夫と思いますので、そのまま Finish ボタンを左クリック。
すると drawable のフォルダ内に、新しくアイコン画像のフォルダが作成。 後は用意したアイコン画像をタブ内に表示できるよう、 MainActivity と activity_main.xml を適切に編集します。
この時のポイントとしては、レイアウトに toolbar 機能を利用することです。
activity_main.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="?actionBarSize"
android:padding="@dimen/appbar_padding"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary" />
</LinearLayout>
</HorizontalScrollView>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
MainActivity
package com.oshimamasara.tabsample001;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.oshimamasara.tabsample001.ui.main.SectionsPagerAdapter;
public class MainActivity extends AppCompatActivity {
private Toolbar toolbar;
private TabLayout tabLayout;
private int[] tabIcons = {
R.drawable.ic_tab_home,
R.drawable.ic_tab_news,
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (getSupportActionBar() != null) {
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
}
getSupportActionBar().setTitle(null);
//TabLayout tabs = findViewById(R.id.tabs);
//tabs.setupWithViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
setUpTabIcon();
}
private void setUpTabIcon() {
tabLayout.getTabAt(0).setIcon(tabIcons[0]);
tabLayout.getTabAt(1).setIcon(tabIcons[1]);
}
}
【実行結果】
画像クリックで拡大
概要としては、スクロール表示可能なタブ・レイアウトを toolbar内に盛り込んだ形。 ただ toolbar を利用した場合、もれなく矢印やタイトルが表示されます。今回はそうした矢印やタイトルは不要となるため、 MainActivity 39 〜 43行目で「矢印」と「タイトル」を非表示処理。
タブの数が 3つ 4つと増える場合は、 配列 tabIcons にアイコン画像の情報を追加すると対応できます。
タブ毎にレイアウトや処理内容を制御
デフォルトの Tabbed Activity では、タブ内の情報を PlaceholderFragmentクラス と PageViewModelクラス で制御していましたが、ちょっと使いにくいです。。。 そこでタブ毎のレイアウトと処理内容を個別設定できるようなプログラムを作成してみました。
画像クリックで拡大
まずタブ毎にレイアウトと機能を設定しようと思うと、各タブに対応したクラスファイルとレイアウトファイルが必要に。今回は上図のように、各タブに対応したファイルを作成しました。
タブ1を制御するファイル/ Frag01.java、 frag01_layout.xml
タブ2を制御するファイル/ Frag02.java、 frag02_layout.xml
タブ3を制御するファイル/ Frag03.java、 frag03_layout.xml
Frag01.java や frag01_layout.xml などの新規ファイル作成方法は、以下の手順となります。
プロジェクト名のフォルダのところで右クリック(今回は tubsample001)。そして New → Java Class を選択。
大文字から始まる任意のファイル名を入力。今回は、 Fragment断片部分 の Frag にセット。
最初のタブを Frag01 で制御し、2番目のタブを Frag02、 3番目のタブを Frag03 で制御したいと思います。Frag01 を作成した手順で Frag02、 Frag03 を作成しておきましょう。
次は最初のタブのレイアウトを制御する専用ファイル frag01_layout.xml の作成。レイアウトフォルダーの上で右クリックをし、 New → Layout resource file を選択。
そしてファイル名を入力し、OK。
同じ要領で2番目のレイアウトを制御する専用ファイル frag02_layout.xml、 3番目のレイアウトを制御する専用ファイル frag03_layout.xml を作成しましょう。
タブ毎にレイアウトがきちんと制御されているか確認するために、背景色を変えたり、文字を入力してタブの雰囲気を出します。
frag01_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_dark">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/tab_text_1"
android:textSize="100sp"
android:textColor="@android:color/white"/>
</android.support.constraint.ConstraintLayout>
2番目のタブや 3番目のタブも同じようにレイアウトを変更しておきましょう。
画像クリックで拡大
次はタブ毎の振る舞いを制御する Frag01.java や Frag02.java のプログラム。 下記のコードを Frag01.java にペーストしましょう。
Frag01.java
package com.oshimamasara.tabsample001;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Frag01 extends Fragment {
@Nullable
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag01_layout,container,false);
}
}
他の Frag02 や Frag03 もレイアウト部分のコードを変えて貼り付け。
基本的にはレイアウトの XMLファイルを Viewオブジェクトにインスタンス化できるクラス LayoutInflater を使って処理。
画像クリックで拡大
そして最後にタブ全般を管理する SectionsPagerAdapter.java 内の getItem()メソッドを編集。今まではタブの内容を制御する PageViewModel を読み込むための PlaceholderFragment が getItem() の対象となっていましたが、 PlaceholderFragment を各タブ毎の制御文である Frag01.java や Frag02.java に変更。 これでタブ毎に慣れ親しんだ方法で、機能やレイアウトを調整することができます。
SectionsPagerAdapter.java
package com.oshimamasara.tabsample001.ui.main;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import com.oshimamasara.tabsample001.Frag01;
import com.oshimamasara.tabsample001.Frag02;
import com.oshimamasara.tabsample001.Frag03;
import com.oshimamasara.tabsample001.R;
/**
* A [FragmentPagerAdapter] that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
@StringRes
private static final int[] TAB_TITLES = new int[]{
R.string.tab_text_1,
R.string.tab_text_2,
R.string.tab_text_3,
R.string.tab_text_4,
R.string.tab_text_5,
R.string.tab_text_6,
R.string.tab_text_7,
R.string.tab_text_8};
private final Context mContext;
public SectionsPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
@Override
public Fragment getItem(int position) {
// 指定されたページのフラグメントをインスタンス化するためにgetItemが呼び出し
//return PlaceholderFragment.newInstance(position + 1);
Fragment fragment = null;
switch (position){
case 0:
fragment = new Frag01();
break;
case 1:
fragment = new Frag02();
break;
case 2:
fragment = new Frag03();
}
return fragment;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
@Override
public int getCount() {
// Show 2 total pages.
return 3;
}
}
// タブ数 3のプログラムとなっています。
以上の結果、タブ毎に機能とレイアウトを調整できる アプリのテンプレートが作成できました。
アプリや Web サービスでよく目にするタブ機能。 シームレスにページ移動できる感覚は、
多くの場合でユーザーの滞在時間やアプリ利用時間増加につながることでしょう。そしてその結果、広告収入やアプリ内課金収入の増加にも期待。
Android Studio に標準で用意されているタブテンプレートの Tabbed Activity でしたが、思い通りに使おうと思うと Java や Android Studio の知識が欠かせません。「 Android Studio、 参考書を買ったものの全く前に進まない」「Java、ちょっと難しいんだよな....」という方、一度学習環境を見直してみるというのはどうでしょうか?
オンライン形式のプログラミングスクール CodeCamp では、 現役エンジニアによる Android Studio の基本レッスンや Java のプログラミング・レッスンを提供中。一時的には学習コストが発生しますが、 日本語や英語のようにきっと一生もののスキルになることは間違いないでしょう。
「オンライン形式?」「マンツーマン?」「現役エンジニア?」と疑問を持たれた方、一度「無料体験」を利用されてみてはどうでしょうか?
無料体験は随時おこなっていますので、 詳しくは 公式ホームページ より確認してみてください。