■ はじめに
https://dk521123.hatenablog.com/entry/2019/09/30/020307
で スワイプ処理でページ遷移できる ViewPage2 を扱ったが TabLayoutと組み合わせて、タブ表示ができるようなので、メモ。
■ 基本的な構成
1)Activity ... 表示する画面 2)Adapter ... Adapter 3)Fragment ... ViewPage2 が表示するフラグメント
■ 使用しているクラス
* TabLayoutMediator ⇒ 入力値としてTabLaoutとViewPager2を渡すと、 タグの位置とタブ表示を管理できる
https://developer.android.com/reference/com/google/android/material/tabs/TabLayoutMediator
■ サンプル
例1:英単語帳
* 以下、構成。 1)MainActivity.kt 2)ExamAdapter.kt 3)ExamFragment.kt
MainActivity.kt
import android.os.Bundle import androidx.viewpager2.widget.ViewPager2 import com.dk.englishcards.BaseActivity import com.dk.englishcards.R import com.google.android.material.tabs.TabLayoutMediator import kotlinx.android.synthetic.main.activity_main.* class MainActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val exams = arrayOf( Exam("streamline", "(仕事を)合理化する"), Exam("attribute", "…に原因があると考える"), Exam("mandatory", "義務的な"), Exam("apprenticeship", "見習い期間"), Exam("applause", "拍手/賞賛"), Exam("overwhelming", "圧倒的な") ) examViewPager.adapter = ExamAdapter(this, exams) examViewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL // 入力値としてTabLaoutとViewPager2を渡すと、 // タグの位置とタブ表示を管理できる TabLayoutMediator(examTabLayout, examViewPager) { tab, position -> val number = position / 2 tab.text = if((position % 2) == 0) "Q$number" else "A$number" }.attach() } }
activity_main.xml
<?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:id="@+id/examFragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".exam.MainActivity"> <com.google.android.material.tabs.TabLayout android:id="@+id/examTabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/examViewPager" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> </androidx.viewpager2.widget.ViewPager2> </androidx.constraintlayout.widget.ConstraintLayout>
Exam.kt
data class Exam ( val question: String, val answer: String )
ExamAdapter.kt
import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter class ExamAdapter(fragmentActivity: FragmentActivity, exams: Array<Exam>) : FragmentStateAdapter(fragmentActivity) { private val exams = exams override fun createFragment(position: Int): Fragment { val index = (position / 2) val exam = exams[index] val questionOrAnswer = if ((position % 2) == 0) exam.question else exam.answer return ExamFragment.newInstance(position, questionOrAnswer) } override fun getItemCount(): Int { return exams.size * 2 } }
ExamFragment.kt
mport android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.fragment.app.Fragment import com.dk.englishcards.R private const val ARG_PARAM_POSITION = "position" private const val ARG_PARAM_LABEL = "label" class ExamFragment : Fragment() { private var position: Int? = null private var label: String? = null companion object { fun newInstance(position: Int, label: String) = ExamFragment().apply { arguments = Bundle().apply { putInt(ARG_PARAM_POSITION, position) putString(ARG_PARAM_LABEL, label) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { position = it.getInt(ARG_PARAM_POSITION) label = it.getString(ARG_PARAM_LABEL) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_exam, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val textView = view.findViewById<TextView>( R.id.examTextViewFragment) val qOrA = if ((position!! % 2) == 0) "Q" else "A" val no = (position!! / 2) textView.text = "$qOrA-$no. $label" } }
fragment_exam.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context=".exam.ExamFragment"> <TextView android:id="@+id/examTextViewFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="@string/hello_blank_fragment" /> </FrameLayout>
例2:ページコントロール
https://qiita.com/sokume2106/items/1af3c59d79673b90473b
の Activity版。
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayoutMediator import kotlinx.android.synthetic.main.activity_main2.* class MainActivity : AppCompatActivity() { private val indexItems = listOf( IndexItem.FirstItem, IndexItem.SecondItem, IndexItem.ThirdItem) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupViewItem() } private fun setupViewItem() { viewPager.adapter = object : FragmentStateAdapter(this) { override fun getItemCount(): Int = indexItems.size override fun createFragment(position: Int): Fragment { return indexItems[position].newInstance() } } viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL TabLayoutMediator(indicator, viewPager) { _, _ -> }.attach() } private sealed class IndexItem { abstract fun newInstance(): Fragment object FirstItem : IndexItem() { override fun newInstance() = Sample1Fragment.newInstance("Hello", "Mike") } object SecondItem : IndexItem() { override fun newInstance() = Sample2Fragment.newInstance("Hi", "Tom") } object ThirdItem : IndexItem() { override fun newInstance() = Sample3Fragment.newInstance("Good evening", "Smith") } } }
activity_main.xml
<?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.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="550dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <FrameLayout android:id="@+id/frameLayout" android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:foregroundGravity="center" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/viewPager"> <com.google.android.material.tabs.TabLayout android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal" app:tabBackground="@drawable/indicator_selector" app:tabGravity="center" app:tabIndicatorFullWidth="true" app:tabIndicatorGravity="center" app:tabIndicatorHeight="0dp" /> </FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout>
indicator_selector.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <shape android:innerRadius="0dp" android:shape="ring" android:thickness="8dp" android:useLevel="false"> <solid android:color="@color/design_default_color_secondary" /> </shape> </item> <item> <shape android:innerRadius="0dp" android:shape="ring" android:thickness="8dp" android:useLevel="false"> <solid android:color="@color/gray" /> </shape> </item> </selector>
Sample1Fragment.kt
import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView private const val ARG_PARAM1 = "param1" private const val ARG_PARAM2 = "param2" class Sample1Fragment : Fragment() { private var param1: String? = null private var param2: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_sample1, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val textView = view.findViewById<TextView>( R.id.textView1) textView.text = "$param1, $param2" } companion object { @JvmStatic fun newInstance(param1: String, param2: String) = Sample1Fragment().apply { arguments = Bundle().apply { putString(ARG_PARAM1, param1) putString(ARG_PARAM2, param2) } } } }
fragment_sample1.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context=".Sample1Fragment"> <TextView android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="@string/hello_blank_fragment" /> </FrameLayout>
参考文献
https://code.luasoftware.com/tutorials/android/setup-android-viewpager2-with-tablayout-and-fragment/
関連記事
画面コンポーネント / ViewPager2
https://dk521123.hatenablog.com/entry/2019/09/30/020307
Mediatorパターン
https://dk521123.hatenablog.com/entry/2011/04/14/011448
Kotlin / Realm で英単語帳を作る
https://dk521123.hatenablog.com/entry/2020/07/20/232009
レイアウト ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2015/08/23/165632