[android : kotlin] 코틀린 프레그먼트(Fragment)를 사용한 뷰페이저2(ViewPager2) 사용 예제 (FragmentStateAdapter)
뷰페이저2(ViewPager2)는 스크린 화면을 좌우로 스와이프(swipe)를 통해 컨텐츠 전환을 할 수 있는 컨테이너 이다. 뷰페이저(ViewPager2)는 데이터를 페이지 단위로 표시한다. 뷰페이저는 뷰그룹(ViewGroup)으로 부터 상속된다. 프레그먼트(Fragment)를 사용하여 뷰페이저2를 생성하려면 FragmentStateAdapter()를 사용한다. registerOnPageChangeCallback()를 사용하여 뷰가 전환될 때 이벤트 처리를 할 수 있다.
뷰페이저2를 사용하려면 build.gradle(:app)파일에 라이브러리를 추가해주어야한다.
implementation ‘androidx.viewpager2:viewpager2:1.0.0’
제일 먼저 페이지로 사용할 프레그먼트 레이아웃을 추가하자. 클래스 파일과 레이아웃 xml파일을 동시에 만들어보자
왼쪽 Project탭에서 java폴더 아래 본인이 만든 패키지명을 클릭후 마우스 오른쪽 버튼을 눌러 NEW > Fragment > 본인에게 필요한 프레그먼트를 선택한다. 아무겂도 없는 빈 프레그먼트 레이아웃을 선택하였다.
프레그먼트 이름과 개발한 언어를 선택 후 Finish버튼을 클릭하면 완료된다.
동일한 생성방법으로 3개의 프레그먼트를 준비하였다.
[MyFragment1.kt]
package edu.kotlin.study
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
class MyFragment1 : 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_my1, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment MyFragment1.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
MyFragment1().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
[fragment_my1.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=".MyFragment1">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="1st blank_fragment" />
</FrameLayout>
[MyFragment2.kt]
package edu.kotlin.study
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [MyFragment2.newInstance] factory method to
* create an instance of this fragment.
*/
class MyFragment2 : Fragment() {
// TODO: Rename and change types of parameters
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_my2, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment MyFragment2.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
MyFragment2().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
[fragment_my2.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=".MyFragment2">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>
[MyFragment3.kt]
package edu.kotlin.study
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [MyFragment3.newInstance] factory method to
* create an instance of this fragment.
*/
class MyFragment3 : Fragment() {
// TODO: Rename and change types of parameters
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_my3, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment MyFragment3.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
MyFragment3().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
[fragment_my3.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=".MyFragment3">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="3rd blank_fragment" />
</FrameLayout>
FragmentStateAdapter를 상속받아서 MyFragmentPagerAdapter클래스를 생성하였다. 오버라이드해야할 메서드는 getItemCount()와 createFragment() 등 2개이다. createFragment메서드안에서 처리해야할 것은 position값을 받아서 원하는 프레그먼트 레이아웃을 호출 하는 것이다.
[MainActivity.kt]
package edu.kotlin.study
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import kotlinx.android.synthetic.main.activity_main.*
private const val NUM_PAGES = 3
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mAdapter = MyFragmentPagerAdapter(FragmentActivity())
//어댑터 적용
viewPager1.adapter = mAdapter
viewPager1.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
textView1.text = "$position 페이지"
super.onPageSelected(position)
}
})
}
inner class MyFragmentPagerAdapter(fm: FragmentActivity) : FragmentStateAdapter(fm) {
override fun getItemCount(): Int {
return NUM_PAGES
}
override fun createFragment(position: Int): Fragment {
val idx = position
return when (idx) {
0 -> {
MyFragment1()
}
1 -> {
MyFragment2()
}
else -> {
MyFragment3()
}
}
}
}
}
[activity_main.xml]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="텍스트뷰"
android:textAppearance="@style/TextAppearance.AppCompat.Display2" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</androidx.viewpager2.widget.ViewPager2>
</LinearLayout>
[build.gradle(:app)]
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
applicationId "edu.kotlin.study"
minSdkVersion 22
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.viewpager2:viewpager2:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
[REFERENCE]
ViewPager2로 프래그먼트 간 슬라이드 – Android Developers
ViewPager2 Releases – Android Developers
ViewPager2 Reference – Android Developers