简体   繁体   中英

Navigation drawer lags when opening and closing android

I am at trying to to get my navigation drawer to run smoothly as there is a stutter/delay in transition when opening and closing the drawer. I would like this to run perfectly. Any help?

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        final MediaPlayer mp = MediaPlayer.create(this, R.raw.firsteps);
        mp.start();


        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setItemIconTintList(null);
        navigationView.setNavigationItemSelectedListener(this);


        FragmentManager fm = getSupportFragmentManager();
        fm.beginTransaction().replace(R.id.content_frame, new MainFragment()).commit();

    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            startActivity(new Intent(getApplicationContext(),Testing.class));
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {

        //final MediaPlayer mp = MediaPlayer.create(this, R.raw.xlophone);

        FragmentManager fm = getSupportFragmentManager();

        int id = item.getItemId();

        if (id == R.id.car_stories) {

            fm.beginTransaction().replace(R.id.content_frame,new ChildrensList()).commit();
            //mp.start();
        } else if (id == R.id.car_places) {

            fm.beginTransaction().replace(R.id.content_frame,new ImportFragment()).commit();
            //mp.start();

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }
}

Documentation: Click

Try this:
//Use Handler().postDelayed

  @Override
  public boolean onNavigationItemSelected(MenuItem item) {
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
             switch (item.getItemId()) {
                 case: R.id.example1:
                       // do something
                       break;
                 case: R.id.example2:
                       // do something
                       break;
                 default: // do something
        }
     }, 200);
     drawer.closeDrawer(GravityCompat.START);
     return true;
  }

In my humble opinion you need to avoid calling 'new' every click.

To fix this you can use constant values to navigation drawer's android:layout_width and android:layout_height attributes ie.

        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"

You may also want to enable hardware acceleration on activity in AndroidManifest.xml

          <activity
              android:name=".ui.SomeActivity"
              android:hardwareAccelerated="true" />

The lag is caused by two heavy operations (fragment replacement and drawer animation) happening in the same thread (main thread aka UI thread).

One way to address this problem is to call the replacement of fragments after the drawer completely closes. In this way the two operations will not happen simultaneously.

In addition, it's a nice design implementation to cross fade a progress bar with the fragment container.

Inside your Activity you should have something like this:

override fun onNavigationItemSelected(item: MenuItem): Boolean {

    // Show progress bar and hide content container
    crossfade(progressBar, container, false)

    drawerLayout?.addDrawerListener(object : DrawerLayout.DrawerListener {
        override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}
        override fun onDrawerOpened(drawerView: View) {}
        override fun onDrawerStateChanged(newState: Int) {}
        override fun onDrawerClosed(drawerView: View) {
            // This method will be called after drawer animation finishes
            // Perform the fragment replacement
            when (item.itemId) {
                R.id.drawer_item_1 -> {
                    supportFragmentManager.beginTransaction()
                            .replace(R.id.container, MyFragmentOne.newInstance())
                            .addToBackStack(null)
                            .commit()
                }
                R.id.drawer_item_2 -> {
                    supportFragmentManager.beginTransaction()
                            .replace(R.id.container, MyFragmentTwo.newInstance())
                            .addToBackStack(null)
                            .commit()
                }
            }

            // Cross fade back the content container and hide progress bar
            crossfade(container, progressBar, false)

            // Remove this listener so close by, for example, swiping do not call it again
            drawerLayout.removeDrawerListener(this)
        }
    })

    // Closes the drawer, triggering the listener above
    drawerLayout.closeDrawer(GravityCompat.START)
    return true
}

private fun crossfade(viewIn: View, viewOut: View, animateViewOut: Boolean = true) {

    val crossfadeDuration = 200L

    // Set the content view to 0% opacity but visible, so that it is visible
    // (but fully transparent) during the animation.
    viewIn.alpha = 0f
    viewIn.visibility = View.VISIBLE
    viewIn.bringToFront()

    // Animate the in view to 100% opacity, and clear any animation
    // listener set on the view.
    viewIn.animate()
            .alpha(1f)
            .setDuration(crossfadeDuration)
            .setListener(null)

    // Animate the out view to 0% opacity. After the animation ends,
    // set its visibility to GONE as an optimization step (it won't
    // participate in layout passes, etc.)
    viewOut.animate()
            .alpha(0f)
            .setDuration(if (animateViewOut) crossfadeDuration else 0)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    viewOut.visibility = GONE
                }
            })

}

Inside your xml, you should have something like this:

...
<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:openDrawer="start">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone" />

        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone" />

    </FrameLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/drawer" />

</android.support.v4.widget.DrawerLayout>
....

Just close the Drawer in some delay, it makes close animation smooth

navigation.setNavigationItemSelectedListener { menuItem ->

            // make closing side-menu smooth
            Completable.timer(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
                .subscribe {
                    drawer.closeDrawer(GravityCompat.START)
                }.addTo(disposable)

            when(menuItem.itemId){
                R.id.sideMenuDashboard ->
                    navController.navigate(R.id.fragment1)
                R.id.sideMenuProfile ->
                    navController.navigate(R.id.fragment2)
            }
            true
        }

After call drawerLayout.closeDrawers() create a new Thread

new Thread(new Runnable() {
public void run() {
try {
     TimeUnit.MILLISECONDS.sleep(300);
     //Here call you fragmentManager
    } catch (InterruptedException e) {
     e.printStackTrace();
                    }
   }
 }).start();

of course you can vary MILISECONDS as you want. Sorry my english

The lag is caused by two heavy operations (fragment replacement and drawer animation) happening in the same thread (main thread aka UI thread).

In my case navigation header background image size is large, so its works on high performance devices but lack on low performance devices.

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