优化抽屉和活动发射速度
我正在使用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 { } }); } }