优化抽屉和活动发射速度

我正在使用Google DrawerLayout

当一个项目被点击时,抽屉会顺利closures,并启动一个Activity 。 把这些活动变成Fragment 不是一种select。 因此,启动一个活动,然后closures抽屉也不是一个选项。 closures抽屉并同时启动活动将使闭幕animation结束。

考虑到我想先平滑地closures它,然后启动活动,那么当用户点击抽屉项目时,以及何时看到他们想要的活动之间的延迟时,我遇到了一个问题。

这是每个项目的点击监听器的样子。

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

我的活动也是DrawerListener,它的onDrawerClosed方法如下所示:

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

onDrawerItemSelection只是启动五个活动之一。

我没有做任何事情,因为DrawerActivity

我正在处理这件事情,从onClick被调用的那一刻起,平均需要500-650ms,直到onDrawerClosed结束。

一旦抽屉closures,在相应的活动启动之前,会有明显的滞后。

我意识到有一些事情正在发生:

  • closuresanimation发生在那里几毫秒(比方说300)。

  • 那么抽屉在视觉上的closures和听众之间可能会出现一些延迟。 我试图通过查看DrawerLayout源来确定究竟发生了多less事情,但还没有弄清楚。

  • 然后,启动的活动执行其启动生命周期方法所需的时间,直到并包括onResume 。 我还没有装备这个,但我估计大约200-300ms。

这似乎是一个问题,走错路将是非常昂贵的,所以我想确保我完全理解它。

一个解决办法就是跳过最后一个animation,但是我希望能保持它。

我怎样才能尽可能减less我的转换时间?

根据文件 ,

避免在animation中执行布局等昂贵的操作,因为这会导致口吃; 尝试在STATE_IDLE状态期间执行昂贵的操作。

您可以重写ActionBarDrawerToggle (实现DrawerLayout.DrawerListener )的onDrawerStateChanged方法,以便在抽屉完全closures时执行昂贵的操作,而不是使用Handler并对时间延迟进行硬编码。

在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; } } 

onCreate设置DrawerListener

 mDrawerToggle = new SmoothActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close); mDrawerLayout.setDrawerListener(mDrawerToggle); 

最后,

 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; } } } 

我面对与DrawerLayout相同的问题。

我有研究,然后find一个很好的解决scheme。

我正在做的是…..

如果您将Android示例应用程序引用到DrawerLayout,请检查selectItem(position)的代码;

在这个函数的基础上调用位置select片段。 我已经修改了下面的代码根据我的需要和工作正常,没有animationclosures口吃。

 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 } 

在这里我首先closuresDrawerLayout。 这需要大约250毫秒。 然后我的处理程序会调用片段。 哪个工作顺利,并根据要求。

希望这对你也有帮助。

享受编码… 🙂

所以我似乎用合理的解决scheme解决了这个问题。

可感知延迟的最大来源是当抽屉被视觉closures时,和onDrawerClosed被调用时之间的延迟。 我通过发布一个Runnable给一个私人Handler程序来解决这个问题,该Handler程序在某个特定的延迟时间启动了预期的活动 该延迟被select为与抽屉closures对应。

我尝试onDrawerSlide在80%的进度之后启动onDrawerSlide ,但是这有两个问题。 首先是它结结巴巴。 第二个是如果你把百分比提高到90%或95%,那么由于animation的性质而不可能被调用的可能性就会增加 – 然后你必须回到onDrawerClosed ,这样就失去了目的。

这个解决scheme有可能导致结局,特别是在较老的手机上,但是通过增加足够高的延迟,可能性可以降低到0。 我认为250毫秒是口吃和延迟之间的合理平衡。

代码的相关部分如下所示:

 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(); } } 

Google IOsched 2015的运行非常顺利(除了设置)之外,原因是他们已经实现了抽屉以及他们如何启动东西。

他们首先使用处理程序来启动延迟:

  // launch the target Activity after a short delay, to allow the close animation to play mHandler.postDelayed(new Runnable() { @Override public void run() { goToNavDrawerItem(itemId); } }, NAVDRAWER_LAUNCH_DELAY); 

延迟是:

 private static final int NAVDRAWER_LAUNCH_DELAY = 250; 

他们所做的另一件事是从活动onCreate()内部使用以下代码启动的活动中删除animation:

 overridePendingTransition(0, 0); 

要查看源代码,请转至git 。

我正在使用下面的方法。 工作顺利。

 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; } } 

与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); 

一个更好的方法是使用onDrawerSlide(View,float)方法,并在slideOffset为0时启动Activity。请参阅下文

 public void onDrawerSlide(View drawerView, float slideOffset) { if (slideOffset <= 0 && mPendingDrawerIntent != null) { startActivity(mPendingDrawerIntent); mPendingDrawerIntent = null; } } 

只需在Drawer的ListView.OnItemClickListener onItemClick方法中设置mPendingDrawerIntent即可。

这是我如何做,而不必定义任何种类的延迟,

 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); } . . . } 

LazyNavigationItemSelectedListener可以是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; } } } } 

这个答案适用于使用RxJava和RxBinding的人 。 想法是为了防止活动启动,直到抽屉closures。 NavigationView用于显示菜单。

 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 { } }); } }