【Androidアプリ開発】タブ表示
前回はメロディを流すことをやりました。
今回は、レイアウトを少し変更したいと思いました。
今、私が作ろうとしているのはTodoアプリです。「未完了」「完了」「全て」で表示を切り替えたいと思いました。そこでタブ表示で分けようとなりました。
それではやっていきます。
タブ表示を実装する
基本的には、レイアウトにタブを追加してアダプタを渡してあげるだけです。
まずは全体像を表します。
タブ表示に使う文字列です。
<resources>
(省略)
<string name="main_tab_incomplete">未完了</string>
<string name="main_tab_complete">完了</string>
<string name="main_tab_all">全て</string>
(省略)
</resources>
タブを表示するレイアウトファイルです。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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">
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabMainLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_tab_incomplete" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_tab_complete" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_tab_all" />
</com.google.android.material.tabs.TabLayout>
</androidx.viewpager.widget.ViewPager>
</androidx.constraintlayout.widget.ConstraintLayout>
タブの子ページのレイアウトです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvTodoCompleteList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvTodoIncompleteList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvTodoAllList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
タブに表示する子ページのフラグメントを作成します。
package com.example.todomanager
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.fragment.app.Fragment
class TabPageInCompleteFragment(context: Context): Fragment(){
private var _viewTabItem : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_viewTabItem = inflater.inflate(R.layout.main_tab_item_incomplete, container,false)
//Viewに対する処理
return _viewTabItem
}
}
class TabPageCompleteFragment(context: Context): Fragment(){
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.main_tab_item_complete,container,false)
}
}
class TabPageAllFragment(context: Context): Fragment(){
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.menu_tab_item_all,container,false)
}
}
子ページと親ページをつなぐアダプタを作成します。
package com.example.todomanager
import android.content.Context
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
class TabAdapter(fm: FragmentManager, private val context: Context): FragmentPagerAdapter(fm){
val _flagmentTodoInComplete = TabPageInCompleteFragment(context)
val _flagmentTodoComplete = TabPageCompleteFragment(context)
val _flagmentTodoAll = TabPageAllFragment(context)
override fun getItem(position: Int): Fragment {
when(position){
0 -> { return _flagmentTodoInComplete }
1 -> {return _flagmentTodoComplete}
else -> { return _flagmentTodoAll }
}
}
override fun getPageTitle(position: Int): CharSequence? {
when(position){
0 -> { return context.getString(R.string.main_tab_incomplete); }
1 -> { return context.getString(R.string.main_tab_complete); }
else -> { return context.getString(R.string.main_tab_all); }
}
}
override fun getCount(): Int {
return 3
}
}
呼び出すActivity(親)で子ページを取得/設定します。
(省略)
class MainActivity : AppCompatActivity(), DialogFragmentResultListener {
private var _adapterTab : TabAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
_adapterTab = TabAdapter(supportFragmentManager,this)
pager.adapter = _adapterTab
tabMainLayout.setupWithViewPager(pager)
}
(省略)
}
それでは細かく見ていきます。
タブレイアウト
最初にタブレイアウトを利用できるようにします。
メニューから「Project Structure」を選択します。
「Dependencies」を選択し、「+」ボタンで追加します。
「Library Dependency」を選択してください。
Step1.のテキストボックスに「material」と入力しSearchボタンをクリックします。
出てきたリストの中から「com.google.android.material」を選択しバージョンを選択します。
バージョンに特にこだわりはありませんがalpha版はまだ正式版ではないので「1.1.0」にしました。
追加されていることを確認します。
build.gradleに追加されていると思います。
dependencies {
(省略)
implementation 'com.google.android.material:material:1.1.0'
}
レイアウトの作成
表示するタブのレイアウトを設定します。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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">
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabMainLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_tab_incomplete" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_tab_complete" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_tab_all" />
</com.google.android.material.tabs.TabLayout>
</androidx.viewpager.widget.ViewPager>
</androidx.constraintlayout.widget.ConstraintLayout>
ViewPagerはアダプタで使用するViewの管理を行います。
この状態で実行すると以下のようになります。
各タブページのレイアウトを作成
タブページのレイアウトを作成します。ここでは例ですので単純にリストビューを表示するようにします。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvTodoCompleteList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvTodoIncompleteList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvTodoAllList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
タブページのフラグメントを作成。
アダプタよりフラグメントが呼び出されるとonCreateViewが呼び出されるのでこの時にViewをインフレイトします。取得したビューに対して初期表示の処理を行います。
class TabPageInCompleteFragment(context: Context): Fragment(){
private var _viewTabItem : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_viewTabItem = inflater.inflate(R.layout.main_tab_item_incomplete, container,false)
//Viewに対する処理
return _viewTabItem
}
}
アダプタを作成
作成したフラグメントをアダプタで取得できるようにします。
class TabAdapter(fm: FragmentManager, private val context: Context): FragmentPagerAdapter(fm){
val _flagmentTodoInComplete = TabPageInCompleteFragment(context)
val _flagmentTodoComplete = TabPageCompleteFragment(context)
val _flagmentTodoAll = TabPageAllFragment(context)
override fun getItem(position: Int): Fragment {
when(position){
0 -> { return _flagmentTodoInComplete }
1 -> {return _flagmentTodoComplete}
else -> { return _flagmentTodoAll }
}
}
override fun getPageTitle(position: Int): CharSequence? {
when(position){
0 -> { return context.getString(R.string.main_tab_incomplete); }
1 -> { return context.getString(R.string.main_tab_complete); }
else -> { return context.getString(R.string.main_tab_all); }
}
}
override fun getCount(): Int {
return 3
}
}
上記の例では、宣言で作成したフラグメントをgetItemで取得します。
またgetPageTitleでタブの名称を取得します。strings.xmlから文字列を取得するにはcontextが必要ですので引数に追加しています。
タブを設定
Activityで呼び出します。pagerにアダプタを渡して、pagerをタブに渡します。
private var _adapterTab : TabAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
_adapterTab = TabAdapter(supportFragmentManager,this)
pager.adapter = _adapterTab
tabMainLayout.setupWithViewPager(pager)
}
まとめ
タブを扱うにはまず使用できるように「com.google.android.material」を登録します。そのあとタブページをレイアウトに記述し、タブで使用するレイアウト、フラグメント、アダプタを作成します。
Visual Studioだとツールボックスで選んでフォームに置くだけで終わりなのですが、結構面倒に感じました。
ここまで読んで頂いてありがとうございます。