android - quitar - Desactivar la animación de la hamburguesa hacia atrás en la barra de herramientas
barras de animacion para equipos (7)
Es muy fácil implementar la Toolbar
con hamburguesa para la animación de la flecha hacia atrás. En mi opinión, esta animación no tiene sentido porque según el diseño del material, el cajón de navegación cubre la Toolbar
cuando se abre. Mi pregunta es cómo deshabilitar correctamente esta animación y mostrar una hamburguesa o una flecha hacia atrás usando getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Así es como lo hice, pero parece un truco sucio:
mDrawerToggle.setDrawerIndicatorEnabled(false);
if (showHomeAsUp) {
mDrawerToggle.setHomeAsUpIndicator(R.drawable.lib_ic_arrow_back_light);
mDrawerToggle.setToolbarNavigationClickListener(view -> finish());
} else {
mDrawerToggle.setHomeAsUpIndicator(R.drawable.lib_ic_menu_light);
mDrawerToggle.setToolbarNavigationClickListener(view -> toggleDrawer());
}
¿Alguna pista de cómo debería implementarse esto correctamente para usar solo setDisplayHomeAsUpEnabled
para cambiar entre los iconos de hamburguesa y flecha atrás?
En mi opinión esta animación no tiene sentido.
Bueno, ActionBarDrawerToggle
está destinado a ser animado.
Puede personalizar el conmutador animado definiendo drawerArrowStyle en su tema ActionBar.
¿Alguna pista de cómo debería implementarse esto correctamente para usar solo setDisplayHomeAsUpEnabled para cambiar entre los iconos de hamburguesa y flecha atrás?
El ActionBarDrawerToggle
es solo una forma elegante de llamar a ActionBar.setHomeAsUpIndicator
. Entonces, de cualquier forma, tendrás que llamar a ActionBar.setDisplayHomeAsUpEnabled
a true
para poder mostrarlo.
Si está convencido de que tiene que usarlo, le sugiero que llame a ActionBarDrawerToggle.onDrawerOpened(View drawerView)
y ActionBarDrawerToggle.onDrawerClosed(View drawerView)
respectivamente.
Esto establecerá la posición del DrawerIndicator
en 1
o 0
, cambiando entre la flecha y los estados de hamburguesa de DrawerArrowDrawable
.
Y en su caso, no es necesario adjuntar un ActionBarDrawerToggle
como DrawerLayout.DrawerListener
. Como en:
mYourDrawer.setDrawerListener(mYourDrawerToggle);
Pero un enfoque mucho más avanzado sería llamar a ActionBar.setHomeAsUpIndicator
una vez y aplicar su propio icono de hamburguesa, también podría hacerlo a través de un estilo. Luego, cuando desee mostrar la flecha hacia atrás, solo llame a ActionBar.setDisplayHomeAsUpEnabled
y deje que AppCompat o el marco se encarguen del resto. Por los comentarios que has hecho, estoy bastante seguro de que esto es lo que estás buscando.
Si no estás seguro de qué icono utilizar, el tamaño predeterminado de DrawerArrowDrawable
es 24dp
, lo que significa que querrías obtener ic_menu_white_24dp
o ic_menu_black_24dp
del icono de navegación establecido en el paquete de iconos de diseño de materiales oficial de Google.
También puede copiar DrawerArrowDrawable
en su proyecto y dejar que luego cambie la flecha o los estados de hamburguesa según lo necesite. Es autónomo, menos unos pocos recursos.
Ahora hay un método dedicado para deshabilitar la animación: toggle.setDrawerSlideAnimationEnabled(false)
Aquí hay un fragmento que uso:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.setDrawerSlideAnimationEnabled(false);
drawer.addDrawerListener(toggle);
toggle.syncState();
}
Deshabilitar la llamada de la cena en el método onDrawerSlide()
detendrá la animación entre Arrow y Burger. Solo verá el cambio (sin animación) cuando el cajón esté completamente abierto o completamente cerrado.
mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.closed) {
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
//super.onDrawerSlide(drawerView, slideOffset);
}
};
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);
Esta es mi función para controlar el ActionBarDrawableToggle ubicado en NavigationDrawerFragment, al que llamo en la devolución de llamada onActivityCreated de cada fragmento. Las funciones de correos son necesarias. El icono de la hamburguesa cambia a la flecha de retroceso y se puede hacer clic en la flecha de retroceso. Los manipuladores manejan adecuadamente los cambios de orientación.
...
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
...
public class NavigationDrawerFragment extends Fragment
{
private ActionBarDrawerToggle mDrawerToggle;
...
public void syncDrawerState()
{
new Handler().post(new Runnable()
{
@Override
public void run()
{
final ActionBar actionBar = activity.getSupportActionBar();
if (activity.getSupportFragmentManager().getBackStackEntryCount() > 1 && (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != ActionBar.DISPLAY_HOME_AS_UP)
{
new Handler().post(new Runnable()
{
@Override
public void run()
{
mDrawerToggle.setDrawerIndicatorEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);
mDrawerToggle.setToolbarNavigationClickListener(onToolbarNavigationClickListener());
}
});
} else if (activity.getSupportFragmentManager().getBackStackEntryCount() <= 1 && (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP)
{
actionBar.setHomeButtonEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(true);
mDrawerToggle.syncState();
}
}
});
}
}
Este es solo mi método onActivityCreated en mi fragmento base.
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
navigationDrawerFragment.syncDrawerState();
}
Esto deshabilitará la animación, al crear el drawerToggle, anulará onDrawerSlide ():
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
getToolbar(), R.string.open, R.string.close) {
@Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0); // this disables the animation
}
};
Si desea eliminar la flecha completamente, puede agregar
super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed state
al final de la función onDrawerOpened.
Si no quieres la animación, no uses ActionBarDrawerToggle
. Utilice el código de abajo en su lugar.
toolbar.setNavigationIcon(R.drawable.ic_menu);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
drawer.openDrawer(GravityCompat.START);
}
});
Tenía un requisito similar y pasé un tiempo ActionBarDrawerToggle
código ActionBarDrawerToggle
. Lo que tienes actualmente es el mejor camino a seguir.
Más por venir:
La animación de hamburguesa a flecha es proporcionada por una implementación DrawerArrowDrawableToggle
- DrawerArrowDrawableToggle
. Actualmente, no tenemos mucho control sobre cómo reacciona este dibujo a los estados del cajón. actionVarDrawerToggle
dice el constructor de acceso al paquete para actionVarDrawerToggle
:
/**
* In the future, we can make this constructor public if we want to let developers customize
* the
* animation.
*/
<T extends Drawable & DrawerToggle> ActionBarDrawerToggle(Activity activity, Toolbar toolbar,
DrawerLayout drawerLayout, T slider,
@StringRes int openDrawerContentDescRes,
@StringRes int closeDrawerContentDescRes)
Al proporcionar su propia implementación del slider
, puede controlar cómo reacciona a los estados del cajón. La interfaz que el slider
debe implementar:
/**
* Interface for toggle drawables. Can be public in the future
*/
static interface DrawerToggle {
public void setPosition(float position);
public float getPosition();
}
setPosition(float)
es lo más destacado aquí: todos los cambios de estado del cajón lo llaman para actualizar el indicador del cajón.
Para el comportamiento que desea, la implementación de su slider
setPosition(float position)
no haría nada.
Aún necesitarás:
if (showHomeAsUp) {
mDrawerToggle.setDrawerIndicatorEnabled(false);
// Can be set in theme
mDrawerToggle.setHomeAsUpIndicator(R.drawable.lib_ic_arrow_back_light);
mDrawerToggle.setToolbarNavigationClickListener(view -> finish());
}
Si no setDrawerIndicatorEnabled(false)
, OnClickListener
lo configuró con setToolbarNavigationClickListener(view -> finish());
no disparará
¿Qué podemos hacer ahora ?
En una inspección más cercana, encuentro que hay una disposición para su requerimiento en ActionBarDrawerToggle
. Considero que esta disposición es aún más un hack que lo que tienes actualmente. Pero, te dejaré decidir.
ActionBarDrawerToggle
permite tener cierto control sobre el indicador del cajón a través de la interfaz Delegate . Puede hacer que su actividad implemente esta interfaz de la siguiente manera:
public class TheActivity extends ActionBarActivity implements ActionBarDrawerToggle.Delegate {
....
@Override
public void setActionBarUpIndicator(Drawable drawableNotUsed, int i) {
// First, we''re not using the passed drawable, the one that animates
// Second, we check if `displayHomeAsUp` is enabled
final boolean displayHomeAsUpEnabled = (getSupportActionBar().getDisplayOptions()
& ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP;
// We''ll control what happens on navigation-icon click
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (displayHomeAsUpEnabled) {
finish();
} else {
// `ActionBarDrawerToggle#toggle()` is private.
// Extend `ActionBarDrawerToggle` and make provision
// for toggling.
mDrawerToggle.toggleDrawer();
}
}
});
// I will talk about `mToolbarnavigationIcon` later on.
if (displayHomeAsUpEnabled) {
mToolbarNavigationIcon.setIndicator(
CustomDrawerArrowDrawable.HOME_AS_UP_INDICATOR);
} else {
mToolbarNavigationIcon.setIndicator(
CustomDrawerArrowDrawable.DRAWER_INDICATOR);
}
mToolbar.setNavigationIcon(mToolbarNavigationIcon);
mToolbar.setNavigationContentDescription(i);
}
@Override
public void setActionBarDescription(int i) {
mToolbar.setNavigationContentDescription(i);
}
@Override
public Drawable getThemeUpIndicator() {
final TypedArray a = mToolbar.getContext()
.obtainStyledAttributes(new int[]{android.R.attr.homeAsUpIndicator});
final Drawable result = a.getDrawable(0);
a.recycle();
return result;
}
@Override
public Context getActionBarThemedContext() {
return mToolbar.getContext();
}
....
}
ActionBarDrawerToggle
usará setActionBarUpIndicator(Drawable, int)
proporcionado aquí. Ya que ignoramos que se puede pasar el Drawable
, tenemos control total sobre lo que se mostrará.
Captura: ActionBarDrawerToggle
permitirá que nuestra Activity
actúe como un delegado si pasamos el parámetro Toolbar
como nulo aquí:
public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
Toolbar toolbar, @StringRes int openDrawerContentDescRes,
@StringRes int closeDrawerContentDescRes) { .... }
Y, tendrá que anular getV7DrawerToggleDelegate()
en su actividad:
@Nullable
@Override
public ActionBarDrawerToggle.Delegate getV7DrawerToggleDelegate() {
return this;
}
Como puede ver, ir por el camino correcto es mucho trabajo adicional. Y no hemos terminado todavía.
La animación DrawerArrowDrawableToggle
puede DrawerArrowDrawableToggle
usando estos atributos . Si desea sus estados dibujables (homeAsUp y hamburguesa) exactamente como los valores predeterminados, deberá implementarlo como tal:
/**
* A drawable that can draw a "Drawer hamburger" menu or an Arrow
*/
public class CustomDrawerArrowDrawable extends Drawable {
public static final float DRAWER_INDICATOR = 0f;
public static final float HOME_AS_UP_INDICATOR = 1f;
private final Activity mActivity;
private final Paint mPaint = new Paint();
// The angle in degress that the arrow head is inclined at.
private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
private final float mBarThickness;
// The length of top and bottom bars when they merge into an arrow
private final float mTopBottomArrowSize;
// The length of middle bar
private final float mBarSize;
// The length of the middle bar when arrow is shaped
private final float mMiddleArrowSize;
// The space between bars when they are parallel
private final float mBarGap;
// Use Path instead of canvas operations so that if color has transparency, overlapping sections
// wont look different
private final Path mPath = new Path();
// The reported intrinsic size of the drawable.
private final int mSize;
private float mIndicator;
/**
* @param context used to get the configuration for the drawable from
*/
public CustomDrawerArrowDrawable(Activity activity, Context context) {
final TypedArray typedArray = context.getTheme()
.obtainStyledAttributes(null, R.styleable.DrawerArrowToggle,
R.attr.drawerArrowStyle,
R.style.Base_Widget_AppCompat_DrawerArrowToggle);
mPaint.setAntiAlias(true);
mPaint.setColor(typedArray.getColor(R.styleable.DrawerArrowToggle_color, 0));
mSize = typedArray.getDimensionPixelSize(R.styleable.DrawerArrowToggle_drawableSize, 0);
mBarSize = typedArray.getDimension(R.styleable.DrawerArrowToggle_barSize, 0);
mTopBottomArrowSize = typedArray
.getDimension(R.styleable.DrawerArrowToggle_topBottomBarArrowSize, 0);
mBarThickness = typedArray.getDimension(R.styleable.DrawerArrowToggle_thickness, 0);
mBarGap = typedArray.getDimension(R.styleable.DrawerArrowToggle_gapBetweenBars, 0);
mMiddleArrowSize = typedArray
.getDimension(R.styleable.DrawerArrowToggle_middleBarArrowSize, 0);
typedArray.recycle();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.SQUARE);
mPaint.setStrokeWidth(mBarThickness);
mActivity = activity;
}
public boolean isLayoutRtl() {
return ViewCompat.getLayoutDirection(mActivity.getWindow().getDecorView())
== ViewCompat.LAYOUT_DIRECTION_RTL;
}
@Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
final boolean isRtl = isLayoutRtl();
// Interpolated widths of arrow bars
final float arrowSize = lerp(mBarSize, mTopBottomArrowSize, mIndicator);
final float middleBarSize = lerp(mBarSize, mMiddleArrowSize, mIndicator);
// Interpolated size of middle bar
final float middleBarCut = lerp(0, mBarThickness / 2, mIndicator);
// The rotation of the top and bottom bars (that make the arrow head)
final float rotation = lerp(0, ARROW_HEAD_ANGLE, mIndicator);
final float topBottomBarOffset = lerp(mBarGap + mBarThickness, 0, mIndicator);
mPath.rewind();
final float arrowEdge = -middleBarSize / 2;
// draw middle bar
mPath.moveTo(arrowEdge + middleBarCut, 0);
mPath.rLineTo(middleBarSize - middleBarCut, 0);
final float arrowWidth = Math.round(arrowSize * Math.cos(rotation));
final float arrowHeight = Math.round(arrowSize * Math.sin(rotation));
// top bar
mPath.moveTo(arrowEdge, topBottomBarOffset);
mPath.rLineTo(arrowWidth, arrowHeight);
// bottom bar
mPath.moveTo(arrowEdge, -topBottomBarOffset);
mPath.rLineTo(arrowWidth, -arrowHeight);
mPath.moveTo(0, 0);
mPath.close();
canvas.save();
if (isRtl) {
canvas.rotate(180, bounds.centerX(), bounds.centerY());
}
canvas.translate(bounds.centerX(), bounds.centerY());
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
@Override
public void setAlpha(int i) {
mPaint.setAlpha(i);
}
// override
public boolean isAutoMirrored() {
// Draws rotated 180 degrees in RTL mode.
return true;
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getIntrinsicHeight() {
return mSize;
}
@Override
public int getIntrinsicWidth() {
return mSize;
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public void setIndicator(float indicator) {
mIndicator = indicator;
invalidateSelf();
}
/**
* Linear interpolate between a and b with parameter t.
*/
private static float lerp(float a, float b, float indicator) {
if (indicator == HOME_AS_UP_INDICATOR) {
return b;
} else {
return a;
}
}
}
CustomDrawerArrowDrawable''s
implementación CustomDrawerArrowDrawable''s
se ha tomado de AOSP y se ha CustomDrawerArrowDrawable''s
para permitir el dibujo de solo dos estados: homeAsUp y hamburger. Puede alternar entre estos estados llamando a setIndicator(float)
. Usamos esto en el Delegate
que implementamos. Además, el uso de CustomDrawerArrowDrawable
le permitirá CustomDrawerArrowDrawable
en xml: barSize
, color
, etc. Aunque no lo necesite, la implementación anterior le permite proporcionar animaciones personalizadas para abrir y cerrar cajones .
Sinceramente, no sé si debería recomendar esto.
Si llama a ActionBarDrawerToggle#setHomeAsUpIndicator(...)
con un argumento null
, debería elegir el dibujable definido en su tema:
<item name="android:homeAsUpIndicator">@drawable/some_back_drawable</item>
Actualmente, esto no sucede debido a un posible error en ToolbarCompatDelegate#getThemeUpIndicator()
:
@Override
public Drawable getThemeUpIndicator() {
final TypedArray a = mToolbar.getContext()
// Should be new int[]{android.R.attr.homeAsUpIndicator}
.obtainStyledAttributes(new int[]{android.R.id.home});
final Drawable result = a.getDrawable(0);
a.recycle();
return result;
}
Informe de error que discute de manera holgada (lea el Caso 4): Link
Si decide seguir con la solución que ya tiene, considere usar CustomDrawerArrowDrawable
en lugar de pngs (R.drawable.lib_ic_arrow_back_light & R.drawable.lib_ic_menu_light). No necesitarás múltiples elementos dibujables para los cubos de densidad / tamaño y el estilo se realizará en xml. Además, el producto final será el mismo que el del marco.
mDrawerToggle.setDrawerIndicatorEnabled(false);
CustomDrawerArrowDrawable toolbarNavigationIcon
= new CustomDrawerArrowDrawable(this, mToolbar.getContext());
if (showHomeAsUp) {
toolbarNavigationIcon.setIndicator(
CustomDrawerArrowDrawable.HOME_AS_UP_INDICATOR);
mDrawerToggle.setToolbarNavigationClickListener(view -> finish());
} else {
mToolbarNavigationIcon.setIndicator(
CustomDrawerArrowDrawable.DRAWER_INDICATOR);
mDrawerToggle.setToolbarNavigationClickListener(view -> toggleDrawer());
}
mDrawerToggle.setHomeAsUpIndicator(toolbarNavigationIcon);