Оптимизация скорости выдвижения ящика и активности

Я использую Google DrawerLayout .

Когда элемент получает щелчок, ящик плавно закрывается, и действие будет запущено. Превращение этих действий в 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-650 мс с момента вызова onClick, до момента окончания onDrawerClosed.

После того, как ящик закрывается, до того, как будет запущена соответствующая операция, произойдет заметное отставание.

Я понимаю, что происходит несколько вещей:

  • Завершающая анимация имеет место, которая составляет пару миллисекунд прямо там (скажем 300).

  • Тогда, вероятно, есть некоторое задержка между визуальным закрытием ящика и его прослушивателем. Я пытаюсь выяснить, сколько из этого происходит , глядя на источник DrawerLayout но пока не понял.

  • Затем требуется время, затрачиваемое запущенной onResume на выполнение своих жизненных циклов запуска, вплоть до onResume . Я пока ничего не сделал, но оцениваю около 200-300 мс.

Это похоже на проблему, когда спуск по неправильному пути будет довольно дорогостоящим, поэтому я хочу убедиться, что полностью его понимаю.

Одно из решений – просто пропустить заключительную анимацию, но я надеялся сохранить ее.

Как я могу как можно увеличить время перехода?

Согласно документам ,

Избегайте выполнения дорогостоящих операций, таких как макет во время анимации, поскольку это может вызвать заикание; Попытайтесь выполнить дорогостоящие операции в состоянии STATE_IDLE.

Вместо использования Handler и жесткого кодирования временной задержки вы можете переопределить метод ActionBarDrawerToggle DrawerLayout.DrawerListener (который реализует 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 Sample для DrawerLayout, тогда проверьте код selectItem (position);

В этой функции на основе фрагмента выбора позиции вызывается. Я изменил его с помощью кода ниже, в соответствии с моей потребностью, и отлично работает без анимации, заикающейся.

 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) и запустить Activity, как только slideOffset будет равен 0. См. Ниже

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

Просто установите mPendingDrawerIntent в методе ListView.OnItemClickListener в Drawer.OnItemClickListener метода onItemClick.

Вот как я это делаю, не задавая никаких задержек,

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