Оптимизация скорости запуска выдвижного ящика и активности
Я использую 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 { } }); } }