ViewPager2 예제

jericho·2024년 1월 25일

Android

목록 보기
6/15

implementation("androidx.viewpager2:viewpager2:1.0.0") 는 할 필요가 없다. 기본적으로 뷰페이저2 들어있음. 디자인탭에서 꺼내쓰면 됨.

먼저 레이아웃을 짠다. 메인에 툴바, 탭레이아웃, 뷰페이저2 놓는다.
그리고 뷰페이저2에 들어갈 프래그먼트 두 개 만든다.
(여기에서는 투두리스트랑 북마크리스트. 일단 리스트는 안만듦. 프래그먼트에 텍뷰만.)

뷰페이저 어댑터를 만들어야 한다. FragmentStateAdapter를 상속받는데, 매개변수로 FragmentActivity가 올 수도 있고, Fragment가 올 수도 있다. 뷰페이저2가 액티비티 안에 있으면 어댑터가 FragmentActivity를 매개변수로 받아 넘기고, 프래그먼트 안에 있으면 Fragment를 받아서 넘긴다. (AppCompatActivity는 FragmentActivity를 상속받는다)

어댑터에서는 각 탭에 들어갈 프래그먼트들을 리스트로 들고 있는데, 각 프래그먼트의 제목도 함께 들고있으려면(탭레이아웃에 띄우기 위함) 데이터클래스를 만들어서 프래그먼트와 제목을 갖게 하고, 이 데이터클래스를 리스트로 들고 있으면 된다.

FragmentStateAdapter를 상속받으면 getItemCount랑 createFragment를 구현하라고 에러가 뜬다. 리스트의 사이즈랑, 포지션의 프래그먼트를 반환해주면 된다.
이외에 getFragment, getTitle, getTodoListFragment 등 필요한 메서드를 만들면 된다.

이제 메인을 작성해준다.
먼저 뷰페이저에 어댑터를 넣어준다. (어댑터 변수는 lazy로 선언해둠)
뷰페이저에 registerOnPageChangeCallback 해준다. 이 메서드는 파라미터로 OnPageChangeCallback 추상클래스?를 받으므로 object : OnPageChangeCallback() {...} 을 인자로 넣어준다. onPageScrolled, onPageSelected, onPageScrollStateChanged 함수를 오버라이드 할 수 있으니 필요에 따라 추가한다.
(예제에서는 선택된 페이지가 투두리스트 프래그먼트인지에 따라서 플로팅액션버튼을 show(), hide() 해줬다.)

뷰페이저2와 탭레이아웃을 연결하는 건 TabLayoutMediator 으로 한다.

나중에 손을 좀 봐야겠지만, 일단 뷰페이저 적용 코드를 정리해보자.

xml

/*
메인 레이아웃은 툴바, 탭레이아웃, 뷰페이저2, 플로팅 버튼을 둔다.
(툴바, 플로팅 버튼은 선택사항)
 */
// viewpager2 패키지, TodoMainActivity 추가
// activity_todo_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=".TodoMainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/tool_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            android:theme="?attr/actionBarTheme"
            app:title="TODO LIST" />

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="52dp">

            <com.google.android.material.tabs.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Monday" />

            <com.google.android.material.tabs.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Tuesday" />

            <com.google.android.material.tabs.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Wednesday" />
        </com.google.android.material.tabs.TabLayout>

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

        </androidx.viewpager2.widget.ViewPager2>

    </LinearLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_create_todo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="24dp"
        android:clickable="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:srcCompat="@android:drawable/ic_input_add"
        android:contentDescription="할일 추가 버튼"
        tools:ignore="HardcodedText" />
</androidx.constraintlayout.widget.ConstraintLayout>

/*
프래그먼트 두 개는 구현 안하고 텍뷰만 띄워서 프래그먼트 표시.
 */

코드

// 페이저 어댑터
class TodoMainViewPagerAdapter(fragmentActivity: FragmentActivity) :
    FragmentStateAdapter(fragmentActivity) {
    private val fragments = listOf(
        TodoMainTab(TodoListFragment.newInstance(), R.string.main_tab_todo_title),
        TodoMainTab(BookmarkListFragment.newInstance(), R.string.main_tab_bookmark_title),
    )

    override fun getItemCount(): Int = fragments.size
    override fun createFragment(position: Int): Fragment = fragments[position].fragment

    fun getFragment(position: Int): Fragment = fragments[position].fragment

    fun getTitle(position: Int): Int = fragments[position].title

    fun getTodoListFragment(): Fragment? = fragments.find {
        it.fragment::class.java == TodoListFragment::class.java
    }?.fragment

//    fun getFragment(clazz: Class<out Fragment>): Fragment? =
//        fragments.find { it.fragment::class.java == clazz }?.fragment
}


// TodoMainTab 데이터 클래스 (어댑터의 프래그먼트를 제목이랑 같이 관리하기 위함)
data class TodoMainTab(
    val fragment: Fragment,
    @StringRes val title: Int
)


/*
어댑터에 들어갈 프래그먼트 2개. TodoListFragment, BookmarkListFragment.
각 프래그먼트에는 텍뷰 하나 넣어서 표시.
 */
class TodoListFragment : Fragment() {
    companion object {
        @JvmStatic
        fun newInstance() = TodoListFragment()
    }

    private var _binding: FragmentTodoListBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentTodoListBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        _binding = null
        super.onDestroyView()
    }
}


class BookmarkListFragment : Fragment() {
    companion object {
        @JvmStatic
        fun newInstance() = BookmarkListFragment()
    }

    private var _binding: FragmentBookmarkListBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentBookmarkListBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        _binding = null
        super.onDestroyView()
    }
}


/*
R.string.main_tab_todo_title  ==  TODO
R.string.main_tab_bookmark_title  ==  BOOKMARK
 */


// 액티비티
private val viewPagerAdapter by lazy { TodoMainViewPagerAdapter(this) }


private fun initView(ac: AppCompatActivity) = binding.apply {
    viewPager.adapter = viewPagerAdapter
    // object : ViewPager2.OnPageChangeCallback() 일수도?
    viewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
        override fun onPageSelected(position: Int) {
            super.onPageSelected(position)
            if (viewPagerAdapter.getFragment(position) is TodoListFragment)
                fabCreateTodo.show()
            else fabCreateTodo.hide()
        }
    })

    // TabLayout x ViewPager2
    TabLayoutMediator(tabLayout, viewPager) { tab, position ->
        tab.setText(viewPagerAdapter.getTitle(position))
    }.attach()

//    // fab
//    fabCreateTodo.setOnClickListener {
//        createTodoLauncher.launch(
//            TodoContentActivity.newIntent(this@TodoMainActivity)
//        )
//    }
}

0개의 댓글