Migrate from ViewPager to ViewPager2 (#1601)

This commit is contained in:
Michael 2021-11-06 19:07:26 +01:00 committed by GitHub
parent e6f23ab35b
commit 4401df6203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 142 additions and 101 deletions

View File

@ -2,32 +2,33 @@ package io.github.wulkanowy.ui.base
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
//TODO Use ViewPager2
class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) :
FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
class BaseFragmentPagerAdapter(
private val fragmentManager: FragmentManager,
private val pagesCount: Int,
lifecycle: Lifecycle,
) : FragmentStateAdapter(fragmentManager, lifecycle), TabLayoutMediator.TabConfigurationStrategy {
private val pages = mutableMapOf<Fragment, String?>()
lateinit var itemFactory: (position: Int) -> Fragment
var titleFactory: (position: Int) -> String? = { "" }
var containerId = 0
fun getFragmentInstance(position: Int): Fragment? {
require(containerId != 0) { "Container id is 0" }
return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position")
return fragmentManager.findFragmentByTag("f$position")
}
fun addFragments(fragments: List<Fragment>) {
fragments.forEach { pages[it] = null }
override fun createFragment(position: Int): Fragment = itemFactory(position)
override fun getItemCount() = pagesCount
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
tab.text = titleFactory(position)
}
fun addFragmentsWithTitle(pages: Map<Fragment, String>) {
this.pages.putAll(pages)
}
override fun getItem(position: Int) = pages.keys.elementAt(position)
override fun getCount() = pages.size
override fun getPageTitle(position: Int) = pages.values.elementAt(position)
}

View File

@ -8,6 +8,7 @@ import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Semester
@ -29,7 +30,13 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
@Inject
lateinit var presenter: GradePresenter
private val pagerAdapter by lazy { BaseFragmentPagerAdapter(childFragmentManager) }
private val pagerAdapter by lazy {
BaseFragmentPagerAdapter(
fragmentManager = childFragmentManager,
pagesCount = 3,
lifecycle = lifecycle,
)
}
private var semesterSwitchMenu: MenuItem? = null
@ -62,25 +69,34 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
}
override fun initView() {
with(pagerAdapter) {
containerId = binding.gradeViewPager.id
addFragmentsWithTitle(
mapOf(
GradeDetailsFragment.newInstance() to getString(R.string.all_details),
GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary),
GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics)
)
)
}
with(binding.gradeViewPager) {
adapter = pagerAdapter
offscreenPageLimit = 3
setOnSelectPageListener(presenter::onPageSelected)
}
with(pagerAdapter) {
containerId = binding.gradeViewPager.id
titleFactory = {
when (it) {
0 -> getString(R.string.all_details)
1 -> getString(R.string.grade_menu_summary)
2 -> getString(R.string.grade_menu_statistics)
else -> throw IllegalStateException()
}
}
itemFactory = {
when (it) {
0 -> GradeDetailsFragment.newInstance()
1 -> GradeSummaryFragment.newInstance()
2 -> GradeStatisticsFragment.newInstance()
else -> throw IllegalStateException()
}
}
TabLayoutMediator(binding.gradeTabLayout, binding.gradeViewPager, this).attach()
}
with(binding.gradeTabLayout) {
setupWithViewPager(binding.gradeViewPager)
setElevationCompat(context.dpToPx(4f))
}

View File

@ -101,7 +101,6 @@ class GradePresenter @Inject constructor(
private fun loadData() {
flowWithResource {
val student = studentRepository.getCurrentStudent()
delay(200)
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
}.onEach {
when (it.status) {

View File

@ -24,7 +24,13 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
@Inject
override lateinit var presenter: LoginPresenter
private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager)
private val pagerAdapter by lazy {
BaseFragmentPagerAdapter(
fragmentManager = supportFragmentManager,
pagesCount = 5,
lifecycle = lifecycle,
)
}
@Inject
lateinit var updateHelper: UpdateHelper
@ -65,24 +71,26 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
setDisplayShowTitleEnabled(false)
}
with(loginAdapter) {
containerId = binding.loginViewpager.id
addFragments(
listOf(
LoginFormFragment.newInstance(),
LoginSymbolFragment.newInstance(),
LoginStudentSelectFragment.newInstance(),
LoginAdvancedFragment.newInstance(),
LoginRecoverFragment.newInstance()
)
)
}
with(binding.loginViewpager) {
offscreenPageLimit = 2
adapter = loginAdapter
adapter = pagerAdapter
isUserInputEnabled = false
setOnSelectPageListener(presenter::onViewSelected)
}
with(pagerAdapter) {
containerId = binding.loginViewpager.id
itemFactory = {
when (it) {
0 -> LoginFormFragment.newInstance()
1 -> LoginSymbolFragment.newInstance()
2 -> LoginStudentSelectFragment.newInstance()
3 -> LoginAdvancedFragment.newInstance()
4 -> LoginRecoverFragment.newInstance()
else -> throw IllegalStateException()
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -103,12 +111,12 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
}
override fun notifyInitSymbolFragment(loginData: Triple<String, String, String>) {
(loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)
(pagerAdapter.getFragmentInstance(1) as? LoginSymbolFragment)
?.onParentInitSymbolFragment(loginData)
}
override fun notifyInitStudentSelectFragment(studentsWithSemesters: List<StudentWithSemesters>) {
(loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)
(pagerAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)
?.onParentInitStudentSelectFragment(studentsWithSemesters)
}

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
@ -26,7 +27,13 @@ class MessageFragment : BaseFragment<FragmentMessageBinding>(R.layout.fragment_m
@Inject
lateinit var presenter: MessagePresenter
private val pagerAdapter by lazy { BaseFragmentPagerAdapter(childFragmentManager) }
private val pagerAdapter by lazy {
BaseFragmentPagerAdapter(
fragmentManager = childFragmentManager,
pagesCount = 3,
lifecycle = lifecycle,
)
}
companion object {
fun newInstance() = MessageFragment()
@ -43,23 +50,34 @@ class MessageFragment : BaseFragment<FragmentMessageBinding>(R.layout.fragment_m
}
override fun initView() {
with(pagerAdapter) {
containerId = binding.messageViewPager.id
addFragmentsWithTitle(mapOf(
MessageTabFragment.newInstance(RECEIVED) to getString(R.string.message_inbox),
MessageTabFragment.newInstance(SENT) to getString(R.string.message_sent),
MessageTabFragment.newInstance(TRASHED) to getString(R.string.message_trash)
))
}
with(binding.messageViewPager) {
adapter = pagerAdapter
offscreenPageLimit = 2
setOnSelectPageListener(presenter::onPageSelected)
}
with(pagerAdapter) {
containerId = binding.messageViewPager.id
titleFactory = {
when (it) {
0 -> getString(R.string.message_inbox)
1 -> getString(R.string.message_sent)
2 -> getString(R.string.message_trash)
else -> throw IllegalStateException()
}
}
itemFactory = {
when (it) {
0 -> MessageTabFragment.newInstance(RECEIVED)
1 -> MessageTabFragment.newInstance(SENT)
2 -> MessageTabFragment.newInstance(TRASHED)
else -> throw IllegalStateException()
}
}
TabLayoutMediator(binding.messageTabLayout, binding.messageViewPager, this).attach()
}
with(binding.messageTabLayout) {
setupWithViewPager(binding.messageViewPager)
setElevationCompat(context.dpToPx(4f))
}

View File

@ -16,7 +16,6 @@ class MessagePresenter @Inject constructor(
override fun onAttachView(view: MessageView) {
super.onAttachView(view)
presenterScope.launch {
delay(150)
view.initView()
Timber.i("Message view was initialized")
loadData()

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.FragmentSchoolandteachersBinding
@ -24,7 +25,13 @@ class SchoolAndTeachersFragment :
@Inject
lateinit var presenter: SchoolAndTeachersPresenter
private val pagerAdapter by lazy { BaseFragmentPagerAdapter(childFragmentManager) }
private val pagerAdapter by lazy {
BaseFragmentPagerAdapter(
fragmentManager = childFragmentManager,
pagesCount = 2,
lifecycle = lifecycle,
)
}
companion object {
fun newInstance() = SchoolAndTeachersFragment()
@ -41,22 +48,36 @@ class SchoolAndTeachersFragment :
}
override fun initView() {
with(pagerAdapter) {
containerId = binding.schoolandteachersViewPager.id
addFragmentsWithTitle(mapOf(
SchoolFragment.newInstance() to getString(R.string.school_title),
TeacherFragment.newInstance() to getString(R.string.teachers_title)
))
}
with(binding.schoolandteachersViewPager) {
adapter = pagerAdapter
offscreenPageLimit = 2
setOnSelectPageListener(presenter::onPageSelected)
}
with(pagerAdapter) {
containerId = binding.schoolandteachersViewPager.id
titleFactory = {
when (it) {
0 -> getString(R.string.school_title)
1 -> getString(R.string.teachers_title)
else -> throw IllegalStateException()
}
}
itemFactory = {
when (it) {
0 -> SchoolFragment.newInstance()
1 -> TeacherFragment.newInstance()
else -> throw IllegalStateException()
}
}
TabLayoutMediator(
binding.schoolandteachersTabLayout,
binding.schoolandteachersViewPager,
this
).attach()
}
with(binding.schoolandteachersTabLayout) {
setupWithViewPager(binding.schoolandteachersViewPager)
setElevationCompat(context.dpToPx(4f))
}
}
@ -77,7 +98,8 @@ class SchoolAndTeachersFragment :
}
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
(pagerAdapter.getFragmentInstance(index) as? SchoolAndTeachersChildView)?.onParentLoadData(forceRefresh)
(pagerAdapter.getFragmentInstance(index) as? SchoolAndTeachersChildView)
?.onParentLoadData(forceRefresh)
}
override fun onDestroyView() {

View File

@ -16,7 +16,6 @@ class SchoolAndTeachersPresenter @Inject constructor(
override fun onAttachView(view: SchoolAndTeachersView) {
super.onAttachView(view)
presenterScope.launch {
delay(150)
view.initView()
Timber.i("Message view was initialized")
loadData()

View File

@ -1,19 +0,0 @@
package io.github.wulkanowy.ui.widgets
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.viewpager.widget.ViewPager
class SwipeDisabledViewPager : ViewPager {
constructor(context: Context) : super(context)
constructor(context: Context, attr: AttributeSet) : super(context, attr)
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent) = false
override fun onInterceptTouchEvent(ev: MotionEvent) = false
}

View File

@ -1,13 +1,11 @@
package io.github.wulkanowy.utils
import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.widget.ViewPager2
inline fun ViewPager.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) {
addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
inline fun ViewPager2.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
selectListener(position)
}
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
})
}

View File

@ -10,7 +10,7 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent" />
<io.github.wulkanowy.ui.widgets.SwipeDisabledViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/loginViewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -17,7 +17,7 @@
tools:ignore="UnusedAttribute"
tools:visibility="visible" />
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/gradeViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -20,7 +20,7 @@
tools:ignore="UnusedAttribute"
tools:visibility="visible" />
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/messageViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -20,7 +20,7 @@
tools:ignore="UnusedAttribute"
tools:visibility="visible" />
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/schoolandteachersViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"