Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BottomNavigationView lags on fragment transaction

The problem

I'm using the BottomNavigationView from the Android Design Support Library on one of my Activities, alongside with Fragments for each navigation item.

Each time I select an item on the bar, I do a fragment transaction, like the snippet below (some parts of the code was removed for brevity):

private var fragmentToSet: Fragment? = null

private val onNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->

    fragmentToSet = when (item.itemId) {
        // Choose fragment based on selection
        // ...
    }

// ...

supportFragmentManager.beginTransaction()
                .replace(R.id.container, fragmentToSet)
                .commit()
}

The problem is... The bottom bar animation gets super laggy, and only finishes after the fragment is fully loaded and displayed on the screen.

This issue is not exactly new since it can also happen while using the Navigation Menu, but at least it's possible to solve it by using the DrawerLayout.DrawerListener and do the actual Fragment transaction only after the drawer is closed.

What I've tried so far

I tried to "cache" the fragments, holding their reference to avoid recreating the objects every time (e.g. MyFragment.newInstance()), but that didn't work.

I also tried to use handlers, which kinda solved the problem, but it might lead me to an exception in some cases. Something like the snippet below:

handler.postDelayed({changeFragment(fragmentToSet!!)}, 200)

Is there a way to solve this issue without using handlers (or other async calls), on a similar fashion to this solution while using the Navigation Menu?

like image 803
Mauker Avatar asked Sep 04 '18 18:09

Mauker


1 Answers

I handled this situation by hiding and showing fragments using fragment manager. I wrote a sample code to deal with it as below.

class MainActivity : BaseActivity() {

    private val homeFragment = HomeFragment.newInstance()
    private val categoryFragment = CategoryFragment.newInstance()
    private val searchFragment = SearchFragment.newInstance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
        navigation.menu.findItem(R.id.navigation_home).isChecked = true

        supportFragmentManager.beginTransaction()
                .add(R.id.containerFrameLayout, homeFragment)
                .add(R.id.containerFrameLayout, categoryFragment)
                .add(R.id.containerFrameLayout, searchFragment)
                .commit()
        setTabStateFragment(TabState.HOME).commit()
    }

    override fun onBackPressed() {
        if (supportFragmentManager.backStackEntryCount > 0 || !homeFragment.isHidden) {
            super.onBackPressed()
        } else {
            setTabStateFragment(TabState.HOME).commit()
            navigation.menu.findItem(R.id.navigation_home).isChecked = true
        }
    }

    private fun setTabStateFragment(state: TabState): FragmentTransaction {
        supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
        val transaction = supportFragmentManager.beginTransaction()
        transaction.setCustomAnimations(R.anim.fragment_enter, R.anim.fragment_exit)
        when (state) {
            TabState.HOME -> {
                transaction.show(homeFragment)
                transaction.hide(categoryFragment)
                transaction.hide(searchFragment)
            }
            TabState.CATEGORY -> {
                transaction.hide(homeFragment)
                transaction.show(categoryFragment)
                transaction.hide(searchFragment)
            }
            TabState.SEARCH -> {
                transaction.hide(homeFragment)
                transaction.hide(categoryFragment)
                transaction.show(searchFragment)
            }
        }
        return transaction
    }

    private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
        when (item.itemId) {
            R.id.navigation_home -> {
                setTabStateFragment(TabState.HOME).commit()
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_category -> {
                setTabStateFragment(TabState.CATEGORY).commit()
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_search -> {
                setTabStateFragment(TabState.SEARCH).commit()
                return@OnNavigationItemSelectedListener true
            }
        }
        false
    }

    internal enum class TabState {
        HOME,
        CATEGORY,
        SEARCH,
    }

}
like image 56
aminography Avatar answered Sep 22 '22 19:09

aminography