Оптимизация скорости запуска выдвижного ящика и активности
Я использую Google DrawerLayout.
когда элемент нажимается, ящик плавно закрывается и Activity будет запущен. Превращение этих действий в Fragment s - это не опции. Из-за этого запуск действия, а затем закрытие ящика также не является вариантом. Закрытие ящика и запуск активности в то же время заставит закрывающую анимацию заикаться.
учитывая, что я хочу плавно закрыть его сначала, а затем запустите действие, у меня есть проблема с задержкой между тем, когда пользователь нажимает на элемент ящика, и когда они видят действие, которое они хотели перейти.
это то,что прослушиватель щелчка для каждого элемента выглядит.
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 просто запускает один из пяти видов деятельности.
Я ничего не делаю на onPause на DrawerActivity.
Я инструментирование этого и занимает в среднем от 500-650ms с момента вызова onClick, до момента ondrawerclosed заканчивается.
существует заметное отставание, как только ящик закрывается, перед запуском соответствующей деятельности.
Я понимаю, что происходит несколько вещей:
анимация закрытия происходит, что составляет пару миллисекунд прямо там (скажем, 300).
то есть вероятно, некоторая задержка между визуальным закрытием ящика и увольнением его слушателя. Я пытаюсь выяснить, сколько именно это происходит глядя на
DrawerLayoutисточник но еще не понял этого.тогда есть количество времени, которое требуется для запущенного действия, чтобы выполнить его методы жизненного цикла запуска до, и в том числе,
onResume. Я еще не инструментировал это, но я оцениваю около 200-300мс.
это похоже на проблему, когда идти по неправильному пути было бы довольно дорого, поэтому я хочу убедиться, что полностью понимаю это.
одно решение - просто пропустить заключительную анимацию, но я надеялся сохранить ее.
как я могу максимально уменьшить время перехода?
8 ответов:
по docs,
избегайте выполнения дорогостоящих операций, таких как макет во время анимации, поскольку это может вызвать заикание; попробуйте выполнить дорогостоящие операции во время состояния STATE_IDLE.
вместо
Handlerи жесткое кодирование временной задержки, вы можете переопределитьonDrawerStateChangedметодActionBarDrawerToggle(реализующийDrawerLayout.DrawerListener), так что вы можете выполнять дорогостоящие операции, когда ящик полностью закрытый.Внутри 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; } }установить
DrawerListenerнаonCreate: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.
У меня есть исследования для этого, а затем найти одно хорошее решение для него.
что я делаю.....
Если вы ссылаетесь на Android пример приложения для DrawerLayout затем проверьте код для selectItem (позиция);
в этой функции на основе выбора позиции вызывается фрагмент. Я изменил его с помощью кода ниже в соответствии с моей потребностью и отлично работает без анимации близко заикаться.
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 }здесь я сначала закрываю DrawerLayout. который занимает около 250 миллисекунд. и тогда мой обработчик вызовет фрагмент. Что работает ровно и согласно требованию.
надеюсь, что это будет полезно для вас.
Наслаждайтесь Кодирования... :)
так, я, кажется, решил проблему с разумным решением.
самым большим источником воспринимаемой задержки была задержка между тем, когда ящик был визуально закрыт, и когда
onDrawerClosedназывалась. Я решил это, разместивRunnableчастныйHandlerкоторый запускает запланированную деятельность с некоторой заданной задержкой. Эта задержка выбирается в соответствии с закрытием ящика.Я пытался сделать запуск
onDrawerSlideпосле 80% прогресса, но это имеет два проблемы. Во-первых, он заикался. Во-вторых, если вы увеличили процент до 90% или 95%, вероятность того, что он вообще не будет вызван из-за природы анимации, увеличилась-и вам пришлось вернуться кonDrawerClosed, который побеждает цель.это решение имеет возможность заикаться, особенно на старых телефонах, но вероятность может быть уменьшена до 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 ():
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. Смотрите ниже
public void onDrawerSlide(View drawerView, float slideOffset) { if (slideOffset <= 0 && mPendingDrawerIntent != null) { startActivity(mPendingDrawerIntent); mPendingDrawerIntent = null; } }просто установите mPendingDrawerIntent в ListView ящика.Метод onItemClick OnItemClickListener.
вот как я это делаю без необходимости определять какие-либо задержки,
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. Идея состоит в том, чтобы предотвратить запуск активности до тех пор, пока ящик не закроется.
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 { } }); } }