Optimizing drawer and activity launching speed

2019-01-12 17:57发布

I'm using the Google DrawerLayout.

When an item gets clicked, the drawer is smoothly closed and an Activity will be launched. Turning these activities into Fragments is not an option. Because of this, launching an activity and then closing the drawer is also not an option. Closing the drawer and launching the activity at the same time will make the closing animation stutter.

Given that I want to smoothly close it first, and then launch the activity, I have a problem with the latency between when a user clicks on the drawer item, and when they see the activity they wanted to go to.

This is what the click listener for each item looks like.

final View.OnClickListener mainItemClickListener = new View.OnClickListener() {
    @Override
    public void onClick(final View v) {
        mViewToLaunch = v;
        mDrawerLayout.closeDrawers();
    }
};

My activity is also the DrawerListener, its onDrawerClosed method looks like:

@Override
public synchronized void onDrawerClosed(final View view) {
    if (mViewToLaunch != null) {
        onDrawerItemSelection(mViewToLaunch);
        mViewToLaunch = null;
    }
}

onDrawerItemSelection just launches one of the five activities.

I do nothing on onPause of the DrawerActivity.

I am instrumenting this and it takes on average from 500-650ms from the moment onClick is called, to the moment onDrawerClosed ends.

There is a noticeable lag, once the drawer closes, before the corresponding activity is launched.

I realize a couple of things are happening:

  • The closing animation takes place, which is a couple of milliseconds right there (let's say 300).

  • Then there's probably some latency between the drawer visually closing and its listener getting fired. I'm trying to figure out exactly how much of this is happening by looking at DrawerLayout source but haven't figured it out yet.

  • Then there's the amount of time it takes for the launched activity to perform its startup lifecycle methods up to, and including, onResume. I have not instrumented this yet but I estimate about 200-300ms.

This seems like a problem where going down the wrong path would be quite costly so I want to make sure I fully understand it.

One solution is just to skip the closing animation but I was hoping to keep it around.

How can I decrease my transition time as much as possible?

8条回答
手持菜刀,她持情操
2楼-- · 2019-01-12 18:47

Here is how I am doing it without having to define any kind of delay,

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        LazyNavigationItemSelectedListener lazyNavigationItemSelectedListener =
            new LazyNavigationItemSelectedListener(this, drawer, "drawer_open", "drawer_close");
        drawer.addDrawerListener(navigationItemSelectedListener);
        lazyNavigationItemSelectedListener.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(lazyNvigationItemSelectedListener);
    }
  .
  .
  .

}

and the LazyNavigationItemSelectedListener can be an inner class of MainActivity.

private class LazyNavigationItemSelectedListener extends ActionBarDrawerToggle
        implements NavigationView.OnNavigationItemSelectedListener {
    private int selectedMenuItemID;
    DrawerLayout drawer;

    private LazyNavigationItemSelectedListener(Activity activity, DrawerLayout drawerLayout, 
                                              @StringRes int openDrawerContentDescRes, 
                                              @StringRes int closeDrawerContentDescRes) {
        super(activity, drawerLayout, openDrawerContentDescRes, closeDrawerContentDescRes);
        this.drawer = drawerLayout;
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        }
        selectedMenuItemID = item.getItemId();

        if (!(drawer.isDrawerOpen(GravityCompat.START))) {//only respond to call if drawer is closed.

            switch (selectedMenuItem) {

                case R.id.menu_item_id:
                    Intent intent1 = new Intent() //build your intent
                    startActivity(intent1);
                    break;
            }
        }
        return true;
    }

    @Override
    public void onDrawerClosed(View drawerView) {
        if (selectedMenuItemID > 0) {
            if (drawerView instanceof NavigationView) {
                NavigationView navigationView = (NavigationView) drawerView;
                //perform click on navigation item.
                navigationView.getMenu().performIdentifierAction(selectedMenuItemID, 0);
                selectedMenuItemID = -1;
            }
        }
    }
}
查看更多
孤傲高冷的网名
3楼-- · 2019-01-12 18:59

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 {

          }
        });
  }
}
查看更多
登录 后发表回答