I\'m using the Google DrawerLayout
.
When an item gets clicked, the drawer is smoothly closed and an Activity
will be launched. Turning thes
I was facing same issue with DrawerLayout.
I have research for that and then find one nice solution for it.
What i am doing is.....
If you refer Android Sample app for the DrawerLayout then check the code for selectItem(position);
In this function based on the position selection fragment is called. I have modify it with below code as per my need and works fine with no animation close stutter.
private void selectItem(final int position) {
//Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_SHORT).show();
mDrawerLayout.closeDrawer(drawerMain);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Fragment fragment = new TimelineFragment(UserTimeLineActivity.this);
Bundle args = new Bundle();
args.putInt(TimelineFragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
// update selected item and title, then close the drawer
mCategoryDrawerList.setItemChecked(position, true);
setTitle("TimeLine: " + mCategolyTitles[position]);
}
}, 200);
// update the main content by replacing fragments
}
Here i am first closing the DrawerLayout. which takes approx 250 miliseconds. and then my handler will call the fragment. Which works smooth and as per the requirement.
Hope it will also helpful to you.
Enjoy Coding... :)
I'm using approach like below. Works smoothly.
public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener {
private DrawerLayout drawerLayout;
private MenuItem menuItemWaiting;
/* other stuff here ... */
private void setupDrawerLayout() {
/* other stuff here ... */
drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if(menuItemWaiting != null) {
onNavigationItemSelected(menuItemWaiting);
}
}
});
}
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
menuItemWaiting = null;
if(drawerLayout.isDrawerOpen(GravityCompat.START)) {
menuItemWaiting = menuItem;
drawerLayout.closeDrawers();
return false;
};
switch(menuItem.getItemId()) {
case R.id.drawer_action:
startActivity(new Intent(this, SecondActivity.class));
/* other stuff here ... */
}
return true;
}
}
The same with ActionBarDrawerToggle:
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close){
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if(menuItemWaiting != null) {
onNavigationItemSelected(menuItemWaiting);
}
}
};
drawerLayout.setDrawerListener(drawerToggle);
A better approach would be to use the onDrawerSlide(View, float) method and start the Activity once the slideOffset is 0. See below
public void onDrawerSlide(View drawerView, float slideOffset) {
if (slideOffset <= 0 && mPendingDrawerIntent != null) {
startActivity(mPendingDrawerIntent);
mPendingDrawerIntent = null;
}
}
Just set the mPendingDrawerIntent in the Drawer's ListView.OnItemClickListener onItemClick method.
This answer is for guys who uses RxJava and RxBinding. Idea is to prevent the activity launch until drawer closes. NavigationView
is used for displaying the menu.
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
private DrawerLayout drawer;
private CompositeDisposable compositeDisposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setup views and listeners (NavigationView.OnNavigationItemSelectedListener)
compositeDisposable = new CompositeDisposable();
compositeDisposable.add(observeDrawerClose());
}
// uncomment if second activitiy comes back to this one again
/*
@Override
protected void onPause() {
super.onPause();
compositeDisposable.clear();
}
@Override
protected void onResume() {
super.onResume();
compositeDisposable.add(observeDrawerClose());
}*/
@Override
protected void onDestroy() {
super.onDestroy();
compositeDisposable.clear();
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
navSubject.onNext(id);
drawer.closeDrawer(GravityCompat.START);
return true;
}
private Disposable observeDrawerClose() {
return RxDrawerLayout.drawerOpen(drawer, GravityCompat.START)
.skipInitialValue() // this is important otherwise caused to zip with previous drawer event
.filter(open -> !open)
.zipWith(navSubject, new BiFunction<Boolean, Integer, Integer>() {
@Override
public Integer apply(Boolean aBoolean, Integer u) throws Exception {
return u;
}
}).subscribe(id -> {
if (id == R.id.nav_home) {
// Handle the home action
} else {
}
});
}
}
According the docs,
Avoid performing expensive operations such as layout during animation as it can cause stuttering; try to perform expensive operations during the STATE_IDLE state.
Instead of using a Handler
and hard-coding the time delay, you can override the onDrawerStateChanged
method of ActionBarDrawerToggle
(which implements DrawerLayout.DrawerListener
), so that you can perform the expensive operations when the drawer is fully closed.
Inside MainActivity,
private class SmoothActionBarDrawerToggle extends ActionBarDrawerToggle {
private Runnable runnable;
public SmoothActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes) {
super(activity, drawerLayout, toolbar, openDrawerContentDescRes, closeDrawerContentDescRes);
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
invalidateOptionsMenu();
}
@Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
invalidateOptionsMenu();
}
@Override
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
if (runnable != null && newState == DrawerLayout.STATE_IDLE) {
runnable.run();
runnable = null;
}
}
public void runWhenIdle(Runnable runnable) {
this.runnable = runnable;
}
}
Set the DrawerListener
in onCreate
:
mDrawerToggle = new SmoothActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close);
mDrawerLayout.setDrawerListener(mDrawerToggle);
Finally,
private void selectItem(int position) {
switch (position) {
case DRAWER_ITEM_SETTINGS: {
mDrawerToggle.runWhenIdle(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(intent);
}
});
mDrawerLayout.closeDrawers();
break;
}
case DRAWER_ITEM_HELP: {
mDrawerToggle.runWhenIdle(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this, HelpActivity.class);
startActivity(intent);
}
});
mDrawerLayout.closeDrawers();
break;
}
}
}
So I seem to have solved the problem with a reasonable solution.
The largest source of perceivable latency was the delay between when the drawer was visually closed, and when onDrawerClosed
was called. I solved this by posting a Runnable
to a private Handler
that launches the intended activity at some specified delay. This delay is chosen to correspond with the drawer closing.
I tried to do the launching onDrawerSlide
after 80% progress, but this has two problems. The first was that it stuttered. The second was that if you increased the percentage to 90% or 95% the likelihood that it wouldn't get called at all due to the nature of the animation increased--and you then had to fall back to onDrawerClosed
, which defeats the purpose.
This solution has the possibility to stutter, specially on older phones, but the likelihood can be reduced to 0 simply by increasing the delay high enough. I thought 250ms was a reasonable balance between stutter and latency.
The relevant portions of the code look like this:
public class DrawerActivity extends SherlockFragmentActivity {
private final Handler mDrawerHandler = new Handler();
private void scheduleLaunchAndCloseDrawer(final View v) {
// Clears any previously posted runnables, for double clicks
mDrawerHandler.removeCallbacksAndMessages(null);
mDrawerHandler.postDelayed(new Runnable() {
@Override
public void run() {
onDrawerItemSelection(v);
}
}, 250);
// The millisecond delay is arbitrary and was arrived at through trial and error
mDrawerLayout.closeDrawer();
}
}