よくあるこういったUIのやつ
コンテンツ部分を横スワイプでタブが左右に切り替わる
使用するコンポーネント
ViewPager
スワイプに応じてページングされたFragmentを生成し、自動でアタッチする。
TabLayout
TabのUI表現及び、リスナーを管理する。また、生成したViewPagerをセットすることでViewPagerの挙動に応じてタブを切り替えることが可能。
導入
app/buile.gradle
implementation 'com.android.support:design:28.0.0'
implementation 'com.google.android.material:material:1.0.0'
こちらを入れてしまうとAndroidXのコンポーネントと競合しビルドが通らなかった。
Manifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
TabLayoutとViewPagerの配置
TabLayoutとViewPagerを配置するのみでよい。また、TabItemは後述するViewPagerで生成されるので、ここで記載の必要はない
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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" tools:context=".Posts.MainActivity"> <android.support.design.widget.TabLayout android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:id="@+id/tabLayout"> </android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@+id/tabLayout" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </android.support.constraint.ConstraintLayout>
Fragmentの定義
Fragmentはタブ、スワイプで切り替わるコンテンツ部分の定義をすればよい。R.layout.fragment_personal_posts
にレイアウトを記述しているものとする。
class Tab1PostsFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_personal_posts, container, false) } companion object { @JvmStatic fun newInstance() = PersonalPostsFragment().apply { arguments = Bundle().apply { } } } }
Tab1PostsFragment
と同様にTab2PostsFragment
も定義しておく。
Tab生成、制御の実装
Activityまたは上記2つのFragmentの親となるFragmentに記述する。
Tabの生成、制御はViewPagerのAdapterが管理する
viewPager.adapter = object : FragmentPagerAdapter(supportFragmentManager){ override fun getCount(): Int { ... } override fun getItem(position: Int): Fragment { ... } override fun getPageTitle(position: Int): CharSequence? { ... } }
FragmentPagerAdapter
は一度作成されたFragmentはViewPagerが保持し、FragmentStatePagerdAapter
、切り替わりの度にSaveInstance、破棄、生成される。
実装必須となるメソッドは2つだが、タブのタイトルを返すメソッドも実装できる。
getCount()
はViewPagerのページ数を返す
val fragmentList = listOf<Fragment>( Tab1PostsFragment.newInstance(), Tab2PostsFragment.newInstance() ) override fun getCount(): Int { return fragmentList.size }
getItem()
はpositionに応じたFragmentインスタンスを返す
override fun getItem(position: Int): Fragment { val fragment = fragmentList.get(position) return fragment }
getPageTitle()
はpositionに応じたタイトルを返す
override fun getPageTitle(position: Int): CharSequence? { val tabTitles = listOf<String>( "まとめブログ", "個人ブログ" ) return resources.getString( tabTitles[position] ) }
最後にAdapterをセットしたViewPagerをTabLayoutに渡せばViewPagerに連動したタブの制御が可能となる。
viewPager.adapter = object : FragmentPagerAdapter(supportFragmentManager){ ... }
tabLayout.setupWithViewPager(viewPager)
タブのレイアウトにデザインを入れる場合
setupWithViewPager()
後に
tabLayout.setupWithViewPager(viewPager) Tab tab1 = tabLayout.getTabAt(0); tab1.setText("Home"); tab1.setIcon(R.drawable.tab1); val view = layoutInflater.inflate(R.layout.activity_main, null) tab1.setCustomView(view);
のようにすれば、各タブの属性にアクセスできる。