이번 포스팅에서는 ViewPager 를 써서 화면을 좌우로 스와이프할 수 있는 앱을 만들어 보겠습니다.
ViewPager는 페이지를 만들어놓고 유저가 좌우로 스와이프 함으로써 각 페이지를 전환할 수 있게 하는 모듈입니다. 예를들어 페이지 1,2,3을 준비하고 이 페이지들을 ViewPager 어댑터에 전달합니다. 어댑터는 페이지를 순서대로 배열하고 페이지 2를 표시합니다. 페이지 1과 3은 화면 바깥의 좌우로 배치한 뒤 스와이프를 하면 현재 페이지를 밀어내고 화면 밖에서 대기하는 페이지가 ViewPager 화면으로 들어오게 됩니다.
화면 구성
우선은 화면을 구성하겠습니다. 화면에는 현재 어떤 페이지인지를 보여주는 인디케이터를 설치하고 나머지 영역을 ViewPager로 지정합니다. 인디케이터는 CircleIndicator 라는 외부 라이브러리를 이용하겠습니다.
1
2
3
dependencies {
implementation 'me.relex:circleindicator:2.1.6'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation= "vertical"
tools:context= ".MainActivity" >
<me.relex.circleindicator.CircleIndicator
android:id= "@+id/indicator"
android:layout_height= "48dp"
android:layout_width= "match_parent"
app:ci_drawable= "@drawable/black_radius"
app:ci_height= "7dp"
app:ci_width= "7dp"
app:ci_margin= "4dp" />
<androidx.viewpager.widget.ViewPager
android:id= "@+id/viewpager"
android:layout_width= "match_parent"
android:layout_height= "wrap_content" />
</LinearLayout>
CircleIndicator
를 사용시 인디케이터로 사용할 마크를 ci_drawable
특성으로 지정해 주어야 하는데 여기서는 drawable
폴더 안에 black_radius.xml
파일을 추가해서 지정해 주었습니다.
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android= "http://schemas.android.com/apk/res/android"
android:shape= "oval" >
<solid
android:color= "@android:color/black" />
</shape>
프래그먼트 작성
다음은 페이지를 만드는데 사용할 프래그먼트를 작성해줍니다. TestFragment
라는 이름의 빈 프래그먼트를 하나 만들고 다음과 같이 이미지뷰와 텍스트뷰를 가지는 화면을 만들어줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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= ".TestFragment" >
<ImageView
android:id= "@+id/imageView"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
app:layout_constraintBottom_toTopOf= "@+id/textView"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintHorizontal_bias= "0.5"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toTopOf= "parent"
tools:src= "@tools:sample/avatars" />
<TextView
android:id= "@+id/textView"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:text= "TextView"
android:textAppearance= "@style/TextAppearance.AppCompat.Large"
app:layout_constraintBottom_toBottomOf= "parent"
app:layout_constraintEnd_toEndOf= "parent"
app:layout_constraintHorizontal_bias= "0.5"
app:layout_constraintStart_toStartOf= "parent"
app:layout_constraintTop_toBottomOf= "@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
TestFragment
클래스의 내용은 기본적으로 만들어지는 코드를 조금만 바꾸어주겠습니다. 외부에서 전달되는 image와 text 값을 newInstance
에서 번들형태로 초기화한후 onCreate
에서 값을 부여해줍니다. onCreateView
에서 화면을 만들고 onViewCreated
에서 이미지뷰와 텍스트뷰를 표시하도록 하였습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class TestFragment : Fragment () {
private var image : Int ? = null
private var text : String ? = null
override fun onCreate ( savedInstanceState : Bundle ?) {
super . onCreate ( savedInstanceState )
arguments ?. let {
image = it . getInt ( "image" , 0 )
text = it . getString ( "text" , "" )
}
}
override fun onCreateView (
inflater : LayoutInflater ,
container : ViewGroup ?,
savedInstanceState : Bundle ?
): View ? {
return inflater . inflate ( R . layout . fragment_test , container , false )
}
override fun onViewCreated ( view : View , savedInstanceState : Bundle ?) {
super . onViewCreated ( view , savedInstanceState )
imageView . setImageResource ( image !! )
textView . text = text
}
companion object {
fun newInstance ( image : Int , text : String ) =
TestFragment (). apply {
arguments = Bundle (). apply {
putInt ( "image" , image )
putString ( "text" , text )
}
}
}
}
다음으로는 ViewPager Adapter를 만들어줍니다. 별개 클래스이지만 내용이 복잡하지 않으므로 메인액티비티 클래스 안에 만들어줍니다. 어댑터는 FragmentStatePagerAdapter 혹은 FragmentStateAdapter 를 상속받아야 하는데 페이지가 많을 경우에는 자원을 동적으로 관리해 주는 FragmentStatePagerAdapter
가 적절하고 페이지수가 적다면 FragmentStateAdapter
를 사용하면 됩니다. 전체 페이지수는 4개이고 getItem
에서 페이지를 선택했을 때 반환할 프래그먼트를 지정해줍니다. 이때 프래그먼트에 전달할 내용을 어댑터 안에서 지정해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainActivity : AppCompatActivity () {
class CustomPagerAdapter ( fm : FragmentManager ): FragmentStatePagerAdapter ( fm , BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT ) {
private val PAGENUMBER = 4
override fun getCount (): Int {
return PAGENUMBER
}
override fun getItem ( position : Int ): Fragment {
return when ( position ) {
0 -> TestFragment . newInstance ( R . raw . img00 , "test 00" )
1 -> TestFragment . newInstance ( R . raw . img01 , "test 01" )
2 -> TestFragment . newInstance ( R . raw . img02 , "test 02" )
else -> TestFragment . newInstance ( R . raw . img03 , "test 03" )
}
}
}
}
마지막으로 메인액티비티에서 ViewPager와 어댑터를 연결하고, 인디케이터와 ViewPager를 연결하면 작성 완료입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
class MainActivity : AppCompatActivity () {
private var vpAdapter : FragmentStatePagerAdapter ? = null
override fun onCreate ( savedInstanceState : Bundle ?) {
super . onCreate ( savedInstanceState )
setContentView ( R . layout . activity_main )
vpAdapter = CustomPagerAdapter ( supportFragmentManager )
viewpager . adapter = vpAdapter
indicator . setViewPager ( viewpager )
}
}
이렇게해서 ViewPager를 사용하는 법에 대해 알아보았습니다.
VIDEO