I have a fragments displaying lists of items and it's forms to populate the lists. When i go to the list fragment to the form fragment using findNavController().navigate()
the form fragment is shown only on the 50% of the screen, after a couple seconds it finishs showing the other 50%. If i scroll the screen the fragments shows it´s 100% instantly, if i idle and do not touch the screen it takes several seconds. In some of the fragment transacction the glitch is not random, meaning i can reproduce it every time i make that findNavController().navigate()
. But there are other cases where the glitch happens some times and some other times it does´t.
This is happening in emulator and in real devices also.
This is how it looks like (this is the one i can reproduce)
Top half is my form fragment. Bottom half is the list fragment that haven´t updated, the form fragment should be 100% of the container i am not spliting this into 2 fragments in the same container one top of another.
This is how i nav from list fragment to form fragment. Backwards i use the generated backwards button at the left top corner.
findNavController().navigate(
R.id.nav_form_automovil,
args = b
)
I may add that i am using abstract generic clases that i created to reutilize code. The glitchs started happening around that time.
/*
* Params:
* id: layout res
* Generics:
* T: data entity
* B: ViewBinding
* F: Field Validator
* */
abstract class BaseListItemFragment<T, B : ViewBinding, F : BaseFormState>(@LayoutRes id: Int) :
BaseItemFragment<T, B, F>(id) {
private var _recyclerView: RecyclerView? = null
private var _adapter: BaseAdapter<T>? = null
fun initRecycler(recyclerView: RecyclerView, adapter: BaseAdapter<T>) {
_recyclerView = recyclerView
_recyclerView?.layoutManager =
LinearLayoutManager(
MainApplication.instance.applicationContext,
LinearLayoutManager.VERTICAL,
false
)
_adapter = adapter
_recyclerView?.adapter = _adapter
}
private fun observeItemList() =
viewModel?.itemList?.observe(viewLifecycleOwner, Observer {
val itemList = it ?: return@Observer
viewModel?.updateIsLoading(false)
if (itemList.isNotEmpty()) {
_adapter?.updateModels(itemList)
_recyclerView?.smoothScrollToPosition(0)
}
})
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
observeItemList()
}
override fun onResume() {
super.onResume()
viewModel?.fetchAndObserve()
}
}
abstract class BaseItemFragment<T, B : ViewBinding, F : BaseFormState>(@LayoutRes id: Int) :
Fragment(id) {
private var _binding: B? = null
val binding get() = _binding
var viewModel: BaseItemViewModel<T, F>? = null
fun inflateLayout(viewBinding: B) {
_binding = viewBinding
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
provideViewModels()
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
enableLoadingScreen()
observeLogoutFlag()
setUpListeners(view)
}
private fun observeLogoutFlag() =
viewModel?.refreshTokenExpired?.observe(viewLifecycleOwner, Observer {
val isExpired = it ?: return@Observer
if (isExpired) {
activity?.finish()
}
})
abstract fun enableLoadingScreen()
abstract fun setUpListeners(view: View)
abstract fun provideViewModels()
//** Extension functions start here **//
/**
* Extension function to simplify setting an afterTextChanged action to EditText components.
*/
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
})
}
fun Int.toDp(context: Context): Int = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics
).toInt()
}
Provided a viewModel instance my fragments usually performs 1 or 2 or 3 network operations with IO dispatcher, most common case
override fun onResume() {
super.onResume()
viewModel?.fetchAndObserve()
}
Implementations look like this.
class AutomovilListFragment() :
BaseListItemFragment<Automovil, FragmentListAutomovilBinding, AutomovilFormState>(
R.layout.fragment_list_automovil
) {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)
inflateLayout(FragmentListAutomovilBinding.inflate(inflater, container, false))
initRecycler(
binding!!.listAuto,
AutomovilAdapter(ArrayList(), WeakReference(this))
)
return binding!!.root
}
override fun setUpListeners(view: View) {
}
override fun enableLoadingScreen() {
viewModel?.isLoading?.observe(viewLifecycleOwner, Observer {
val isLoading = it ?: return@Observer
if (isLoading) binding!!.pBarListAutomovil.visibility = View.VISIBLE
else binding!!.pBarListAutomovil.visibility = View.INVISIBLE
})
}
override fun provideViewModels() {
viewModel =
ViewModelProvider(
requireActivity(),
AutomovilViewModelFactory()
)[AutomovilViewModel::class.java]
}
}
Do anyone see what could be the root cause of this and what route take to find a solution?
from What could be causing Android (Kotlin) Fragments visual glitchs (fragments overlap on nagivate)?
No comments:
Post a Comment