Cómo cambiar FloatingActionButton entre pestañas?
Estoy tratando de implementar FloatingActionButton desde Biblioteca de Soporte de Diseño de Google en dos de tres pestañas, y de acuerdo con las Directrices de Diseño de Materiales - Flotaciónactionbutton dice:
Si hay un botón de acción flotante en múltiples pantallas laterales (tales como en las pestañas), al entrar en cada pantalla, el botón debe mostrar y ocultar si la acción contenida en cada uno es diferente. Si la acción es el mismo, el botón debe permanecer en la pantalla (y traducir a un nuevo posición, si es necesario.)
¿Cómo puedo hacer este tipo de transición o animación para los botones FAB de mi aplicación?
7 answers
Esta funcionalidad no está incorporada actualmente en el botón Floatingaction, por lo que tendrá que animarla usted mismo. Asumiendo que el botón FloatingActionButton está en su actividad principal, agregue la siguiente función a su actividad.
int[] colorIntArray = {R.color.walking,R.color.running,R.color.biking,R.color.paddling,R.color.golfing};
int[] iconIntArray = {R.drawable.ic_walk_white,R.drawable.ic_run_white,R.drawable.ic_bike_white,R.drawable.ic_add_white,R.drawable.ic_arrow_back_white};
protected void animateFab(final int position) {
fab.clearAnimation();
// Scale down animation
ScaleAnimation shrink = new ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
shrink.setDuration(150); // animation duration in milliseconds
shrink.setInterpolator(new DecelerateInterpolator());
shrink.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
// Change FAB color and icon
fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));
// Scale up animation
ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
expand.setDuration(100); // animation duration in milliseconds
expand.setInterpolator(new AccelerateInterpolator());
fab.startAnimation(expand);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
fab.startAnimation(shrink);
}
Actualice los recursos de color y elementos de diseño para que coincidan con su proyecto. Agregue un listener de selección de pestañas en su método onCreate y llame a la función animate cuando se seleccione una pestaña.
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
animateFab(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
Asegúrese de tener suficientes colores e iconos para que coincida con el número de tienes fichas.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-14 22:34:56
A continuación se muestra la forma sencilla de lograr el resultado deseado
Agregue dos (o equivalente a sus acciones de pestaña) FloatingActionButton
en su actividad principal como a continuación
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/appbar_padding_top"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay">
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabChat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_fab_chat" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabPerson"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_fab_person"
android:visibility="gone" />
Ahora en su actividad principal.java utiliza las funciones predeterminadas de Fab para ocultar y mostrar en cada selección de pestañas como a continuación
private void animateFab(int position) {
switch (position) {
case 0:
fabChat.show();
fabPerson.hide();
break;
case 1:
fabPerson.show();
fabChat.hide();
break;
default:
fabChat.show();
fabPerson.hide();
break;
}
}
Llame a animateFab
función como se muestra a continuación
TabLayout.OnTabSelectedListener onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
animateFab(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
};
ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
animateFab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-07 08:10:31
Extendiendo la respuesta de blackcj, la solución funcionó muy bien como se explicó. Sin embargo, me gustaría añadir algo que.
He visto ese video en cámara lenta. El drawable y fab se animan de manera diferente. Al ocultar, fab y drawable están sincronizados. Mientras se muestra, fab vuelve primero, y después de completar el 60-70 por ciento, el elemento de diseño inicia la animación desde 0 y gira y escala llegando a tamaño completo.
Sin embargo, no pude lograr la animación de elementos de diseño saperatly. Pero, logré rotar y escalar con diferentes interpoladores y un tiempo ligeramente modificado. Así que parece más como en el video que también se presenta en las directrices de diseño de Google.
int[] colorIntArray = {R.color.red,R.color.gray,R.color.black};
int[] iconIntArray = {R.drawable.ic_btn1, R.drawable.ic_btn2, R.drawable.ic_btn3};
protected void animateFab(final int position) {
fab.clearAnimation();
// Scale down animation
ScaleAnimation shrink = new ScaleAnimation(1f, 0.1f, 1f, 0.1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
shrink.setDuration(100); // animation duration in milliseconds
shrink.setInterpolator(new AccelerateInterpolator());
shrink.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
// Change FAB color and icon
fab.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), colorIntArray[position]));
fab.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), iconIntArray[position]));
// Rotate Animation
Animation rotate = new RotateAnimation(60.0f, 0.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
rotate.setDuration(150);
rotate.setInterpolator(new DecelerateInterpolator());
// Scale up animation
ScaleAnimation expand = new ScaleAnimation(0.1f, 1f, 0.1f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
expand.setDuration(150); // animation duration in milliseconds
expand.setInterpolator(new DecelerateInterpolator());
// Add both animations to animation state
AnimationSet s = new AnimationSet(false); //false means don't share interpolators
s.addAnimation(rotate);
s.addAnimation(expand);
fab.startAnimation(s);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
fab.startAnimation(shrink);
}
Y tab tab cambian el oyente como de costumbre:
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
animateFab(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 07:37:10
Finalmente, he encontrado una solución que es bastante fácil y muestra una animación que es exactamente igual que el gif adjunto: en las soluciones de @Nauman o @Omid, la animación del espectáculo comienza antes de que la animación de ocultar haya terminado. Pero asegúrese de utilizar la biblioteca de soporte más reciente! Lo he probado con la versión 23.2.1.
Caso de uso:
- Mostrar fab 1 en la pestaña 1 (índice 0)
- Mostrar fab 2 en la pestaña 2 (índice 1)
- No muestre ningún fab en la pestaña 3 (índice 2)
En su xml, coloque en fabs con id únicos y visibilidad configurada en invisible:
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/some_icon"
android:visibility="invisible" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/another_icon"
android:visibility="invisible" />
Luego, agregue dos campos para sus fabs a su Actividad (también puede usar variables locales u obtener la vista cada vez por findViewById(...)
):
public class MainActivity extends AppCompatActivity {
private FloatingActionButton fab1;
private FloatingActionButton fab2;
En su función onCreate(...)
, busque estas vistas y guárdelas en los campos declarados:
fab1 = (FloatingActionButton) findViewById(R.id.fab1);
fab2 = (FloatingActionButton) findViewById(R.id.fab2);
Luego declare una función que muestra el fab correcto para la posición dada. El caso predeterminado (pestaña 3 o más) es bastante fácil: Simplemente llame al método hide()
en los fabs. show()
y hide()
ya implementan una animación de escalado. Pero si ocultamos fab2 en la pestaña 1, tenemos que esperar hasta que haya terminado antes de poder mostrar fab1. Así que establezca un FloatingActionButton.OnVisibilityChangedListener
como parámetro para el método hide(...)
y muestre el nuevo fab deseado en el método onHidden(...)
de ese oyente. El resultado es el siguiente:
public void showRightFab(int tab) {
switch (tab) {
case 0:
fab2.hide(new FloatingActionButton.OnVisibilityChangedListener() {
@Override
public void onHidden(FloatingActionButton fab) {
fab1.show();
}
});
break;
case 1:
fab1.hide(new FloatingActionButton.OnVisibilityChangedListener() [
@Override
public void onHidden(FloatingActionButton fab) {
fab2.show();
}
});
break;
default:
fab1.hide();
fab2.hide();
break;
}
}
¡Esa fue la parte más difícil! Ahora agregue un oyente al ViewPager para llamar a la función showRightFab(...)
cada vez que cambie la pestaña seleccionada.
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
showRightFab(position);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageScrollStateChanged(int state) {}
});
Por fin, llame a la función una vez manualmente en el método onCreate(...)
para mostrar el fab en la pestaña predeterminada, porque el método ViewPager.OnPageChangeListener
's onPageSelected(...)
no se llama al inicio (por ejemplo, si abre la aplicación y muestra la pestaña 1, no se mostrará ningún fab porque la función showRightFab(...)
nunca se ha llamado).
showRightFab(viewPager.getCurrentItem());
¡Ese trabajo está perfectamente en mi aplicación!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-17 20:29:01
Ampliando las respuestas de blackcj y kirtan403, también he agregado la capacidad de ocultar la fab
en una pestaña elegida (en este caso la 1ra pestaña), que responde a la pregunta de bernzkie bajo la respuesta de blackcj.
Para lograr esto primero declaré int[]
's con 3 elementos, cada uno para el fab
de cada una de las 3 pestañas. Establecí el primer elemento en cada uno a 0 porque será la 1ra pestaña invisible fab
que no necesita recursos.
int[] colorIntArray = {0, R.color.fab_red, R.color.fab_green};
int[] iconIntArray = {0, R.drawable.fab_pencil, R.drawable.fab_chevron};
Luego establezco una instrucción if
en el onCreate
método de la Activity
que cuenta con la fab
y pestañas. Esta afirmación oculta la fab y la escala hacia abajo, de modo que cuando se hace visible de nuevo, se puede hacer que solo se escala hacia arriba, no hacia abajo innecesariamente, y luego hacia arriba de nuevo. Ajusté la escala para que coincida con la escala final de la animación de blackcj.
if (tabLayout.getSelectedTabPosition() == 0) {
// if on the 1st tab
fab.hide();
// scale down to only scale up when switching from 1st tab
fab.setScaleX(0.2f);
fab.setScaleY(0.2f);
}
Luego fuera del método onCreate
, agregué el método animateFab
de blackcj, con la modificación rotate
de kirtan403. Sin embargo, modifiqué el método animateFab
para tener también una sentencia condicional, donde:
Si vuelve a la pestaña 1st, el
fab
está oculto (se reduce automáticamente cuando se oculta);-
Cuando se cambia de una pestaña donde el fab ya es de tamaño completo y visible a otra pestaña donde está destinado a ser visible, realiza la escala completa hacia abajo, cambiar la animación de & escalar hacia arriba;
-
Al cambiar desde la pestaña con el
fab
oculto (en este caso la 1a pestaña), elfab
se hace visible, luego se escala (no escalado hacia abajo, luego escalado hacia arriba) con la animación personalizada.protected void animateFab(final int position) { fab.clearAnimation(); if (tabLayout.getSelectedTabPosition() == 0) { // if on the 1st tab fab.hide(); } else if (fab.getScaleX() == 1f) { // if the fab is full scale // Scale down animation ScaleAnimation shrink = new ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); shrink.setDuration(150); // animation duration in milliseconds shrink.setInterpolator(new DecelerateInterpolator()); shrink.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { // Change FAB color and icon fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position])); fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null)); // Scale up animation ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); expand.setDuration(100); // animation duration in milliseconds expand.setInterpolator(new AccelerateInterpolator()); // Rotate Animation Animation rotate = new RotateAnimation(60.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotate.setDuration(200); rotate.setInterpolator(new DecelerateInterpolator()); // Add both animations to animation state AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators animationSet.addAnimation(expand); animationSet.addAnimation(rotate); fab.startAnimation(animationSet); } @Override public void onAnimationRepeat(Animation animation) { } }); fab.startAnimation(shrink); } else { // if the fab is already scaled down // Change FAB color and icon fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position])); fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null)); fab.show(); // Scale up animation ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); expand.setDuration(100); // animation duration in milliseconds expand.setInterpolator(new AccelerateInterpolator()); // Rotate Animation Animation rotate = new RotateAnimation(60.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotate.setDuration(200); rotate.setInterpolator(new DecelerateInterpolator()); // Add both animations to animation state AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators animationSet.addAnimation(expand); animationSet.addAnimation(rotate); fab.startAnimation(animationSet); } }
Luego simplemente agregué el oyente de selección de pestañas sin cambios de blackcj al método onCreate
.
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
animateFab(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
Espero que esto ayude, ciertamente funciona perfectamente para mí. Gracias a blackcj & kirtan403. :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-22 00:17:12
Puede agregar un oyente a viewpager y mostrar y ocultar fab de acuerdo con su estado cuando se empieza a desplazar el viewpager este es el orden de los estados SCROLL_STATE_DRAGGING SCROLL_STATE_SETTLING SCROLL_STATE_IDLE
Por ejemplo:
viewPager.addOnPageChangeListener(this);
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
if(state==ViewPager.SCROLL_STATE_IDLE)
fab.show();
else if(state==ViewPager.SCROLL_STATE_DRAGGING)
fab.hide();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-09 19:51:53
Eso es lo que funcionó para mí:
private boolean isFloatingActionButtonHidden = false;
private int[] colorIntArray = {R.color.walking,R.color.running,R.color.biking,R.color.paddling,R.color.golfing};
private int[] iconIntArray = {R.drawable.ic_walk_white,R.drawable.ic_run_white,R.drawable.ic_bike_white,R.drawable.ic_add_white,R.drawable.ic_arrow_back_white};
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
case ViewPager.SCROLL_STATE_SETTLING:
// This is triggered just before the view pager reaches the final state
// if you want to trigger the animation after the page reaches its final position
// just move this to "case ViewPager.SCROLL_STATE_IDLE:"
showFloatingActionButton(viewPager.getCurrentItem());
break;
case ViewPager.SCROLL_STATE_IDLE:
// This is only triggered if user pulls to the left of the start or right of the end
if (isFloatingActionButtonHidden) {
showFloatingActionButton(viewPager.getCurrentItem());
}
break;
default:
// in all other cases just hide the fab if it is not visable
if (!isFloatingActionButtonHidden) {
hideFloatingActionButton();
}
}
}
});
private void showFloatingActionButton(int position) {
fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
floatingActionButton.setBackgroundTintList(getResources().getColorStateList(iconIntArray[position], getTheme()));
} else {
floatingActionButton.setBackgroundTintList(getResources().getColorStateList(iconIntArray[position]));
}
floatingActionButton.show();
}
private void hideFloatingActionButton() {
isFloatingActionButtonHidden = true;
floatingActionButton.hide();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-19 16:23:21