简体   繁体   中英

Lag when switching tabs in BottomNavigationView

I have an Activity that contains a BottomNavigationView, and this bottomnav helps the activity to display three fragments. These fragments load well, and I use an AsyncTask to do every heavy operation, while in the UI thread, I show a ProgressBar until everything loads.
There is a weird behaviour with my fragment: The first time I load the fragment it takes some time to actually display it, instead of displaying it instantly with a progressbar.
This thing only happens the first time, and only in this fragment.
The fragment code only contains this:

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        new LoadData(getView(), getContext()).execute();
    }

private class LoadData extends AsyncTask<Void, Void, Void> {

        private View v;
        private Context context;

        public LoadData(View v, Context context) {
            items = new ArrayList<>();
            this.v = v;
            this.context = context;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            setItems(context); //Heavy operation
            adapter = new DashAdapter(items, context);
            return null;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //shows progressbar
            progress = v.findViewById(R.id.DFProgress);
            progress.setVisibility(View.VISIBLE);
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            setPager();
            //sets viewPager and hides progressbar
            progress.setVisibility(View.GONE);
        }
    }

In the gif below, if you look at the bottomnavigationview at the bottom, you can see that it takes time to display the fragment. But after trying to load the fragment a second time, it loads as expected. 在此处输入图片说明

How could I make the fragment to load the right way?

I had the same problem. I have two options.

  1. Use postdelay when you call LoadData or
  2. First add all fragments with manually. You manage navigationItemSelected yourself.

Like this:

val firstFragment: Fragment = FirstFragment()
val secondFragment: Fragment = SecondFragment()
val thirdFragment: Fragment = ThirdFragment()

val navView: BottomNavigationView = findViewById(R.id.nav_view)

var active = firstFragment
fm.beginTransaction().add(R.id.nav_host_fragment, thirdFragment, "3").hide(thirdFragment).commit()
fm.beginTransaction().add(R.id.nav_host_fragment, secondFragment, "2").hide(secondFragment).commit()
fm.beginTransaction().add(R.id.nav_host_fragment, firstFragment, "1").commit()

navView.setOnNavigationItemReselectedListener {  }
navView.setOnNavigationItemSelectedListener { item ->
       when (item.itemId) {
           R.id.navigation_first -> {
               fm.beginTransaction().hide(active).show(firstFragment).commit()
               active = firstFragment

           }
           R.id.navigation_second -> {
               fm.beginTransaction().hide(active).show(secondFragment).commit()
               active = secondFragment
           }
           R.id.navigation_third -> {
               fm.beginTransaction().hide(active).show(thirdFragment).commit()
               active = thirdFragment
           }
       }
        true
   }

And remove these lines in your nav_host_fragment:

app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"

you can use jetpack navigation for simple bottombar navigation

Simple Bottom Navigation with Jetpack Navigation:

Let's start by including the Jetpack Navigation library in your apps by adding these lines in app's build.gradle file:

def nav_version = "2.1.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

We start by creating a simple bottom navigation flow first. For that, you need to do first add NavHostFragment in your single activity layout file. Add this in the activity_main.xml file inside the FrameLayout tag.

<fragment
  android:id="@+id/fragNavHost"
  android:name="androidx.navigation.fragment.NavHostFragment"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:defaultNavHost="true"
  app:navGraph="@navigation/bottom_nav_graph" />

You will see an error saying “Cannot resolve symbol @navigation/bottom_nav_graph .”

    <?xml version="1.0" encoding="utf-8"?>
<navigation 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"
    android:id="@+id/bottom_nav_graph.xml"
    app:startDestination="@id/homeFragment2">

    <fragment
        android:id="@+id/homeFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/searchFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.SearchFragment"
        android:label="fragment_search"
        tools:layout="@layout/fragment_search" />

    <fragment
        android:id="@+id/notificationsFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.NotificationsFragment"
        android:label="fragment_notifications"
        tools:layout="@layout/fragment_notifications" />

    <fragment
        android:id="@+id/profileFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.ProfileFragment"
        android:label="fragment_profile"
        tools:layout="@layout/fragment_profile" />
</navigation>

Its time for add some code in our activity class. Open MainActivity.kt file, and create a method setupViews() in it. Call this in onCreate() of the activity. Add these lines in the setupVeiws() method.

    fun setupViews()
{
    // Finding the Navigation Controller
    var navController = findNavController(R.id.fragNavHost)

    // Setting Navigation Controller with the BottomNavigationView
    bottomNavView.setupWithNavController(navController)

    // Setting Up ActionBar with Navigation Controller
    // Pass the IDs of top-level destinations in AppBarConfiguration
    var appBarConfiguration = AppBarConfiguration(
        topLevelDestinationIds = setOf (
            R.id.homeFragment,
            R.id.searchFragment,
            R.id.notificationsFragment,
            R.id.profileFragment
        )
    )
    setupActionBarWithNavController(navController, appBarConfiguration)
}

When I tried @Kasım Özdemir 's answer, There was an initial ripple effect on first bottom navigation view(bnv) item when activity starts, and as I didn't want to start my activity with first item the ripple effect was quite irrelevant. So, I just attached the fragment and did not add it and then hide it, and now there is no ripple, Here is how my code looks...

    bnv = findViewById(R.id.bottom_navigation)
    bnv!!.selectedItemId = R.id.middle_page

    supportFragmentManager.beginTransaction().attach(firstFragment).commit()
    supportFragmentManager.beginTransaction().attach(thirdFragment).commit()

    supportFragmentManager.beginTransaction().add(R.id.activity_main_container, middleFragment())
        .commit()

So, using just attach can also work fine instead of adding and then hiding it... for more information you can check this answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM