안녕하세요. 이번에는 레이아웃에 viewpager를 적용하기 위해 해당 라이브러리를 구현해 보았습니다. 해당 viewpager를 compose 에 넣어서 만들기 좋은 라이브러리가 있습니다만 저는 이 라이브러리를 사용하지 않고 jetpack의 viewpager2로만 기능을 구성하려 합니다.
저는 옛날 안드로이드에서 viewpager를 사용한 기억이 있는데요. viewpager2에서 개선된 점을 설명 드리겠습니다.
android:orientation
속성을 설정하여 ViewPager2 요소의 세로 페이징을 사용 설정할 수 있습니다.setOrientation()
메서드를 사용하여 프로그래밍 방식으로 이 속성을 설정할 수도 있습니다android:layoutDirection
속성을 설정하여 ViewPager2 요소의 RTL 페이징을 수동으로 사용 설정할 수도 있습니다.notifyDatasetChanged()
를 호출하여 UI를 업데이트합니다.class CollectionDemoFragment : Fragment() {
// When requested, this adapter returns a DemoObjectFragment,
// representing an object in the collection.
private lateinit var demoCollectionAdapter: DemoCollectionAdapter
private lateinit var viewPager: ViewPager2
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.collection_demo, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
demoCollectionAdapter = DemoCollectionAdapter(this)
viewPager = view.findViewById(R.id.pager)
viewPager.adapter = demoCollectionAdapter
}
}
private const val ARG_OBJECT = "object"
// Instances of this class are fragments representing a single
// object in our collection.
class DemoObjectFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_collection_object, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
arguments?.takeIf { it.containsKey(ARG_OBJECT) }?.apply {
val textView: TextView = view.findViewById(android.R.id.text1)
textView.text = getInt(ARG_OBJECT).toString()
}
}
}
class DemoCollectionAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 100
override fun createFragment(position: Int): Fragment {
// Return a NEW fragment instance in createFragment(int)
val fragment = DemoObjectFragment()
fragment.arguments = Bundle().apply {
// Our object is just an integer :-P
putInt(ARG_OBJECT, position + 1)
}
return fragment
}
}
viewpage2 위에 tabLayout 을 추가 하면 됩니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
그러고 나서 viewpager와 함께 binding 하면 되는데요. 해당 방법은 paging fragment에서 TabLayoutMediator
을 만들어 매개변수에 두고 text값으로 된 데이터 값을 주는 방식입니다. 이 text는 title이 됩니다.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
TabLayoutMediator(binding.tabLayout, binding.pager) { tab, position ->
tab.text = "OBJECT ${(position + 1)}"
}.attach()
...
}
제가 viewpager를 적용하면서 만난 독특한 에러를 소개해드리겠습니다.
저는 tabLayout을 넣어주고 따로 해당 activity에 테마를 새로 지정해 주지 않았더니 theme을 지정해 달라는 에러가 발생하였습니다. Theme.appCompat 테마를 지정해주면 된다고 합니다.
Caused by: java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.AppCompat (or a descendant).
at com.google.android.material.internal.ThemeEnforcement.checkTheme(ThemeEnforcement.java:247)
at com.google.android.material.internal.ThemeEnforcement.checkAppCompatTheme(ThemeEnforcement.java:212)
at com.google.android.material.internal.ThemeEnforcement.checkCompatibleTheme(ThemeEnforcement.java:147)
at com.google.android.material.internal.ThemeEnforcement.obtainStyledAttributes(ThemeEnforcement.java:76)
at com.google.android.material.tabs.TabLayout.<init>(TabLayout.java:525)
at com.google.android.material.tabs.TabLayout.<init>(TabLayout.java:505)
테마 에러가 나타날때마다 정말 난감한데요. 테마에 대해서도 한번 다루고 싶습니다.
이 에러는 manifest.xml에 딱 한줄 추가하면 되는데요, tab을 사용하게될 actvitiy 에 한줄을 추가합니다.
android:theme="@style/Theme.AppCompat.Light.NoActionBar"