Optimizar el cajón y la velocidad de lanzamiento de la actividad

Estoy usando Google DrawerLayout .

Cuando se hace clic en un elemento, el cajón se cierra suavemente y se inicia una Activity . Convertir estas actividades en Fragment no es una opción. Debido a esto, el lanzamiento de una actividad y, a continuación, el cierre del cajón tampoco es una opción. Cerrando el cajón y lanzando la actividad al mismo tiempo hará tartamudear el cierre de la animación.

Dado que quiero cerrarlo sin problemas primero y luego iniciar la actividad, tengo un problema con la latencia entre cuando un usuario hace clic en el elemento del cajón y cuando ven la actividad a la que desean ir.

Esto es lo que parece el oyente de clics para cada elemento.

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

Mi actividad es también el DrawerListener, su método onDrawerClosed se parece a:

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

onDrawerItemSelection acaba de lanzar una de las cinco actividades.

No hago nada en onPause de la DrawerActivity .

Estoy instrumentando esto y toma en promedio de 500-650ms desde el momento onClick se llama, hasta el momento enDrawerClosed termina.

Hay un desfase notable, una vez que el cajón se cierra, antes de que se lance la actividad correspondiente.

Me doy cuenta de un par de cosas que están sucediendo:

  • La animación de cierre tiene lugar, que es un par de milisegundos justo allí (digamos 300).

  • Entonces probablemente hay algo de latencia entre el cajón de cierre visual y su oyente de ser despedido. Estoy tratando de averiguar exactamente cuánto de esto está sucediendo mirando fuente de DrawerLayout pero no lo he descubierto todavía.

  • A continuación, hay la cantidad de tiempo que tarda la actividad iniciada para realizar sus métodos de ciclo de vida de inicio hasta, e incluyendo, onResume . No he instrumentado esto todavía pero estimo alrededor de 200-300ms.

Esto parece un problema donde ir por el camino equivocado sería bastante costoso, así que quiero asegurarme de entenderlo completamente.

Una solución es saltar la animación de cierre, pero esperaba mantenerla.

¿Cómo puedo reducir mi tiempo de transición tanto como sea posible?

Según los documentos ,

Evite realizar operaciones costosas como el diseño durante la animación ya que puede causar tartamudeo; Intente realizar operaciones caras durante el estado STATE_IDLE.

En lugar de utilizar un Handler y codificar el retardo de tiempo, puede anular el método onDrawerStateChanged de ActionBarDrawerToggle (que implementa DrawerLayout.DrawerListener ), para que pueda realizar las operaciones costosas cuando el cajón esté completamente cerrado.

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

Establezca el DrawerListener en onCreate :

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

Finalmente,

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

Yo estaba enfrentando el mismo problema con DrawerLayout.

Tengo investigación para eso y luego encontrar una solución agradable para él.

Lo que estoy haciendo es

Si remiten la aplicación de ejemplo de Android a DrawerLayout, comprueban el código para selectItem (posición);

En esta función basada en el fragmento de selección de posición se llama. Tengo modificarlo con código debajo según mi necesidad y trabaja muy bien con ninguna animación tartamudea cercana.

 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 } 

Aquí estoy primero cerrando el DrawerLayout. Que toma aproximadamente 250 milisegundos. Y entonces mi manejador llamará al fragmento. Que funciona suave y según el requisito.

Espero que también sea útil para usted.

Disfrute de la codificación … 🙂

Así que parece haber resuelto el problema con una solución razonable.

La mayor fuente de latencia perceptible era el retraso entre el momento en que el cajón se cerró visualmente y cuando se llamó a onDrawerClosed . He resuelto esto mediante la publicación de un Runnable a un Handler privado que lanza la actividad prevista en algún retraso especificado. Este retardo se elige para corresponder con el cierre del cajón.

He intentado hacer el lanzamiento onDrawerSlide después de un 80% de progreso, pero esto tiene dos problemas. La primera fue que tartamudeó. La segunda fue que si aumentas el porcentaje al 90% o 95% la probabilidad de que no se llamara en absoluto debido a la naturaleza de la animación aumentó – y luego tuvo que recurrir a onDrawerClosed , lo que derrota el propósito .

Esta solución tiene la posibilidad de tartamudear, especialmente en teléfonos antiguos, pero la probabilidad puede reducirse a 0 simplemente aumentando el retraso lo suficientemente alto. Pensé 250ms era un equilibrio razonable entre el tartamudeo y la latencia.

Las partes pertinentes del código tienen este aspecto:

 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 se ejecuta muy bien (excepto de los ajustes) la razón de ello es la forma en que han implementado el cajón y cómo se lanzan cosas.

En primer lugar de utilizar el controlador para lanzar retrasado:

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

Siendo el retraso:

 private static final int NAVDRAWER_LAUNCH_DELAY = 250; 

Otra cosa que hacen es quitar animaciones de las actividades que se lanzan con el siguiente código dentro de las actividades onCreate ():

 overridePendingTransition(0, 0); 

Para ver el origen, vaya a git .

Estoy usando el enfoque como abajo. Funciona sin problemas.

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

Lo mismo con 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); 

Un mejor enfoque sería usar el método onDrawerSlide (View, float) e iniciar la Actividad una vez que el slideOffset sea 0. Ver abajo

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

Simplemente establezca mPendingDrawerIntent en el método ListView.OnItemClickListener onItemClick del cajón.

Aquí es cómo lo estoy haciendo sin tener que definir ningún tipo de retraso,

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

Y el LazyNavigationItemSelectedListener puede ser una clase interna de 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; } } } } 
  • Cajón de navegación siempre visible (un poco)
  • Dos cajones de navegación en la misma actividad
  • Cómo cerrar el cajón de navegación cuando se pulsa un elemento desde el mismo?
  • ActionBarDrawerToggle no está configurando el Indicador de cajón
  • Cambiar el idioma del elemento navigationdrawer
  • Cómo ajustar el ancho del cajón de navegación en el estudio de Android
  • ¿Cómo mostrar la imagen de perfil de Facebook en el cajón de navegación android?
  • ¿Cómo puedo añadir un elemento personalizado a un NavigationView con un diseño de menú?
  • Toque en cualquier lugar para deslizar el menú abierto para el cajón de navegación
  • Guardar estado de fragmentos con el cajón de navegación
  • Cajón de navegación para múltiples actividades
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.