[UI] Add lazy loading to fragments with view pager.

This commit is contained in:
Kuba Szczodrzyński 2020-03-29 23:11:17 +02:00
parent 41a79caf83
commit 043f8210ba
12 changed files with 148 additions and 41 deletions

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
import androidx.fragment.app.Fragment
abstract class LazyFragment : Fragment() {
private var isPageCreated = false
var swipeRefreshLayoutCallback: ((isEnabled: Boolean) -> Unit)? = null
/**
* Called when the page is first shown, or if previous
* [onPageCreated] returned false
*
* @return true if the view is set up
* @return false if the setup failed. The method may be then called
* again, when page becomes visible.
*/
abstract fun onPageCreated(): Boolean
internal fun createPage() {
if (!isPageCreated && isAdded) {
isPageCreated = onPageCreated()
}
}
override fun onResume() {
createPage()
super.onResume()
}
}

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
abstract class LazyPagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
var swipeRefreshLayoutCallback: ((isEnabled: Boolean) -> Unit)? = null
abstract override fun getItem(position: Int): LazyFragment
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.base.lazypager
import android.content.Context
import android.util.AttributeSet
import androidx.viewpager.widget.ViewPager
class LazyViewPager @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : ViewPager(context, attrs) {
init {
addOnPageChangeListener(object : OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
if (adapter is LazyPagerAdapter) {
val fragment = adapter?.instantiateItem(this@LazyViewPager, position)
val lazyFragment = fragment as? LazyFragment
lazyFragment?.createPage()
}
}
})
}
}

View File

@ -68,6 +68,10 @@ class HomeworkFragment : Fragment() {
}))
b.viewPager.adapter = MessagesFragment.Adapter(childFragmentManager).also { adapter ->
adapter.swipeRefreshLayoutCallback = { isEnabled ->
b.refreshLayout.isEnabled = isEnabled
}
adapter.addFragment(HomeworkListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("homeworkDate", HomeworkDate.CURRENT)

View File

@ -4,17 +4,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.databinding.HomeworkListBinding
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.utils.models.Date
class HomeworkListFragment : Fragment() {
class HomeworkListFragment : LazyFragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
@ -30,11 +31,7 @@ class HomeworkListFragment : Fragment() {
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (!isAdded)
return
override fun onPageCreated(): Boolean {
if (arguments != null) {
homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
}
@ -45,6 +42,16 @@ class HomeworkListFragment : Fragment() {
b.homeworkView.setHasFixedSize(true)
b.homeworkView.layoutManager = layoutManager
b.homeworkView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1)) {
swipeRefreshLayoutCallback?.invoke(false)
}
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
swipeRefreshLayoutCallback?.invoke(true)
}
}
})
val filter = when(homeworkDate) {
HomeworkDate.CURRENT -> "eventDate >= '" + Date.getToday().stringY_m_d + "'"
@ -66,5 +73,6 @@ class HomeworkListFragment : Fragment() {
b.homeworkNoData.visibility = View.VISIBLE
}
})
return true
}
}

View File

@ -6,7 +6,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.App
@ -14,8 +13,9 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
import pl.szczodrzynski.edziennik.utils.Themes
import java.util.*
class MessagesFragment : Fragment() {
companion object {
@ -56,6 +56,10 @@ class MessagesFragment : Fragment() {
b.viewPager.adapter = Adapter(childFragmentManager).also { adapter ->
adapter.swipeRefreshLayoutCallback = { isEnabled ->
b.refreshLayout.isEnabled = isEnabled
}
adapter.addFragment(MessagesListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("messageType", Message.TYPE_RECEIVED)
@ -126,11 +130,11 @@ class MessagesFragment : Fragment() {
}*/
}
internal class Adapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
private val mFragmentList = ArrayList<Fragment>()
private val mFragmentTitleList = ArrayList<String>()
internal class Adapter(manager: FragmentManager) : LazyPagerAdapter(manager) {
private val mFragmentList = mutableListOf<LazyFragment>()
private val mFragmentTitleList = mutableListOf<String>()
override fun getItem(position: Int): Fragment {
override fun getItem(position: Int): LazyFragment {
return mFragmentList[position]
}
@ -138,7 +142,8 @@ class MessagesFragment : Fragment() {
return mFragmentList.size
}
fun addFragment(fragment: Fragment, title: String) {
fun addFragment(fragment: LazyFragment, title: String) {
fragment.swipeRefreshLayoutCallback = this@Adapter.swipeRefreshLayoutCallback
mFragmentList.add(fragment)
mFragmentTitleList.add(title)
}

View File

@ -10,11 +10,10 @@ import android.view.ViewGroup;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
@ -26,13 +25,15 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message;
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull;
import pl.szczodrzynski.edziennik.databinding.MessagesListBinding;
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment;
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration;
import pl.szczodrzynski.edziennik.utils.Themes;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class MessagesListFragment extends Fragment {
public class MessagesListFragment extends LazyFragment {
private App app = null;
private MainActivity activity = null;
@ -65,9 +66,9 @@ public class MessagesListFragment extends Fragment {
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
public boolean onPageCreated() {
if (app == null || activity == null || b == null || !isAdded())
return;
return false;
long messageId = -1;
if (getArguments() != null) {
@ -78,7 +79,7 @@ public class MessagesListFragment extends Fragment {
args.putLong("messageId", messageId);
getArguments().remove("messageId");
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args);
return;
return false;
}
if (getArguments() != null) {
@ -161,11 +162,24 @@ public class MessagesListFragment extends Fragment {
// TODO ANIMATION
//postponeEnterTransition();
viewParent = (ViewGroup) view.getParent();
viewParent = (ViewGroup) getView().getParent();
b.emailList.setLayoutManager(new LinearLayoutManager(view.getContext()));
b.emailList.addItemDecoration(new SimpleDividerItemDecoration(view.getContext()));
b.emailList.setLayoutManager(new LinearLayoutManager(getView().getContext()));
b.emailList.addItemDecoration(new SimpleDividerItemDecoration(getView().getContext()));
b.emailList.setAdapter(messagesAdapter);
b.emailList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
if (b.emailList.canScrollVertically(-1)) {
if (getSwipeRefreshLayoutCallback() != null)
getSwipeRefreshLayoutCallback().invoke(false);
}
if (!b.emailList.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
if (getSwipeRefreshLayoutCallback() != null)
getSwipeRefreshLayoutCallback().invoke(true);
}
}
});
if (messageType == Message.TYPE_RECEIVED) {
App.db.messageDao().getReceived(App.Companion.getProfileId()).observe(this, messageFulls -> {
@ -215,7 +229,7 @@ public class MessagesListFragment extends Fragment {
});
}
return true;
}
private void createMessageList(List<MessageFull> messageFulls) {

View File

@ -27,7 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
import pl.szczodrzynski.edziennik.databinding.TimetableNoTimetableBinding
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog
import pl.szczodrzynski.edziennik.ui.modules.base.PagerFragment
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_END_HOUR
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_START_HOUR
import pl.szczodrzynski.edziennik.utils.ListenerScrollView
@ -36,7 +36,7 @@ import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.min
class TimetableDayFragment : PagerFragment(), CoroutineScope {
class TimetableDayFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "TimetableDayFragment"
}
@ -104,9 +104,6 @@ class TimetableDayFragment : PagerFragment(), CoroutineScope {
}
override fun onPageCreated(): Boolean {
if (!isAdded)
return false
// observe lesson database
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer { lessons ->
launch {

View File

@ -5,9 +5,9 @@
package pl.szczodrzynski.edziennik.ui.modules.timetable
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Week
@ -16,7 +16,7 @@ class TimetablePagerAdapter(
private val items: List<Date>,
private val startHour: Int,
private val endHour: Int
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
) : LazyPagerAdapter(fragmentManager) {
companion object {
private const val TAG = "TimetablePagerAdapter"
}
@ -25,8 +25,9 @@ class TimetablePagerAdapter(
private val weekStart by lazy { today.weekStart }
private val weekEnd by lazy { weekStart.clone().stepForward(0, 0, 6) }
override fun getItem(position: Int): Fragment {
override fun getItem(position: Int): LazyFragment {
return TimetableDayFragment().apply {
swipeRefreshLayoutCallback = this@TimetablePagerAdapter.swipeRefreshLayoutCallback
arguments = Bundle().apply {
putInt("date", items[position].value)
putInt("startHour", startHour)

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
xmlns:app="http://schemas.android.com/apk/res-auto">
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
android:id="@+id/refreshLayout"
@ -23,7 +22,7 @@
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="?android:textColorPrimary" />
<androidx.viewpager.widget.ViewPager
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -21,7 +21,7 @@
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="?android:textColorPrimary"/>
<androidx.viewpager.widget.ViewPager
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -29,4 +29,4 @@
</LinearLayout>
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
</layout>
</layout>

View File

@ -28,7 +28,7 @@
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/colorSurface_6dp"
android:background="@color/colorSurface_1dp"
app:rtl_tabIndicatorColor="?colorPrimary"
app:rtl_tabMaxWidth="300dp"
app:rtl_tabMinWidth="90dp"
@ -40,7 +40,7 @@
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -94,4 +94,4 @@
</FrameLayout>
<!--</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>-->
</layout>
</layout>