Navigation drawer lags when opening and closing android
Solution 1
The lag is caused by two heavy operations (fragment replacement and drawer animation) happening in the same thread (main thread a.k.a. 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>
....
Solution 2
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" />
mard dean
Updated on June 19, 2022Comments
-
mard dean almost 2 years
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; } }
-
Milos over 4 yearsYeah handler helped me so I upvoted you, but this acceleration is not enhancing at all so that's it. I didnt understand you well with this calling new and const values.
-
Adi over 4 yearsI went through various similiar questions in SO. But I found this answer to be perfect.
-
Vishal Vaishnav over 3 yearsOnBackPressed not working properly by using this code @Adi
-
Adi over 3 yearsWhat do you mean by 'not properly'?