samsung - mejorar rendimiento android 2018
Optimizar la velocidad de lanzamiento del cajón y la actividad (8)
Estoy usando Google DrawerLayout
.
Cuando se hace clic en un elemento, el cajón se cierra suavemente y se iniciará una Activity
. Convertir estas actividades en Fragment
no es una opción. Debido a esto, lanzar una actividad y luego cerrar el cajón tampoco es una opción. Cerrar el cajón e iniciar la actividad al mismo tiempo hará que la animación de cierre tartamudee.
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 querían ir.
Esto es lo que parece el oyente de clic para cada elemento.
final View.OnClickListener mainItemClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
mViewToLaunch = v;
mDrawerLayout.closeDrawers();
}
};
Mi actividad también es DrawerListener, su método onDrawerClosed
se ve así:
@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
of DrawerActivity
.
Estoy instrumentando esto y toma un promedio de 500-650 ms desde el momento en que se llama a OnClick, hasta el momento en que termina el DrawerClosed.
Hay un retraso notable, una vez que se cierra el cajón, antes de que se inicie la actividad correspondiente.
Me doy cuenta de que están sucediendo un par de cosas:
La animación de cierre tiene lugar, que es un par de milisegundos allí (digamos 300).
Entonces es probable que haya alguna latencia entre el cajón que se cierra visualmente y su oyente siendo despedido. Estoy tratando de averiguar exactamente cuánto de esto está sucediendo mirando a la fuente de
DrawerLayout
pero aún no lo he descubierto.Luego está la cantidad de tiempo que le toma a la actividad iniciada realizar sus métodos de ciclo de vida de inicio hasta, e incluyendo, en
onResume
. No he instrumentado esto todavía, pero estimo unos 200-300ms.
Esto parece un problema en el que tomar el camino equivocado sería bastante costoso, así que quiero asegurarme de que lo entiendo completamente.
Una solución es simplemente omitir la animación de cierre, pero esperaba mantenerla.
¿Cómo puedo disminuir mi tiempo de transición tanto como sea posible?
Así es como 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;
}
}
}
}
De acuerdo con los docs ,
Evite realizar operaciones costosas, como el diseño durante la animación, ya que puede causar tartamudez; intente realizar operaciones costosas durante el estado STATE_IDLE.
En lugar de utilizar un Handler
y codificar el retraso, puede anular el método onDrawerStateChanged
de ActionBarDrawerToggle
(que implementa DrawerLayout.DrawerListener
), de modo que pueda realizar las costosas operaciones 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 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;
}
}
}
Entonces parece que he resuelto el problema con una solución razonable.
La fuente más importante de latencia perceptible fue la demora entre el momento en que el cajón se cerró visualmente y el momento en que se llamó onDrawerClosed
. Lo resolví publicando Runnable
un Runnable
privado que inicia la actividad prevista con un retraso específico. Este retraso se elige para corresponderse con el cierre del cajón.
Intenté hacer el lanzamiento onDrawerSlide
después del 80% de progreso, pero esto tiene dos problemas. El primero fue que tartamudeó. El segundo fue que si aumentaba el porcentaje al 90% o al 95%, aumentaba la probabilidad de que no se llamara debido a la naturaleza de la animación, y luego tenía que recurrir a onDrawerClosed
, lo que onDrawerClosed
el objetivo. .
Esta solución tiene la posibilidad de tartamudear, especialmente en teléfonos más antiguos, pero la probabilidad se puede reducir a 0 simplemente aumentando la demora lo suficiente. Pensé que 250ms era un equilibrio razonable entre tartamudeo y latencia.
Las partes relevantes del código se ven así:
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();
}
}
Esta respuesta es para chicos que usan RxJava y RxBinding . La idea es evitar el lanzamiento de la actividad hasta que el cajón se cierre. NavigationView
se usa para mostrar el menú.
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 {
}
});
}
}
Estaba enfrentando el mismo problema con DrawerLayout.
Tengo investigación para eso y luego encuentro una buena solución para eso.
Lo que estoy haciendo es .....
Si refiere la aplicación Android Sample para DrawerLayout, entonces verifique el código para selectItem (position);
En esta función basada en el fragmento de selección de posición se llama. Lo he modificado con el código a continuación según mi necesidad y funciona bien sin tartamudeo cercano a la animación.
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 luego mi manejador llamará al fragmento. Que funciona sin problemas y según el requisito.
Espero que también te sea útil.
Disfruta Codificando ... :)
Estoy usando el enfoque como a continuación. 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);
Google IOsched 2015 se ejecuta sin problemas (excepto desde la configuración), la razón de ello es cómo han implementado el cajón y cómo inician el proceso.
Primero usan el controlador para iniciar el retraso:
// 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);
con retraso siendo:
private static final int NAVDRAWER_LAUNCH_DELAY = 250;
Otra cosa que hacen es eliminar las animaciones de las actividades que se inician con el siguiente código dentro de las actividades de Crear ():
overridePendingTransition(0, 0);
Para ver la fuente, ve a git .
Un mejor enfoque sería usar el método onDrawerSlide (Ver, flotar) e iniciar la Actividad una vez que slideOffset sea 0. Ver abajo
public void onDrawerSlide(View drawerView, float slideOffset) {
if (slideOffset <= 0 && mPendingDrawerIntent != null) {
startActivity(mPendingDrawerIntent);
mPendingDrawerIntent = null;
}
}
Simplemente configure mPendingDrawerIntent en el método ListView.OnItemClickListener onItemClick del Drawer.