Animación FAB con viewpager / tabslider

Estoy intentando encontrar la forma de lograr dicho efecto https://material-design.storage.googleapis.com/publish/material_v_4/material_ext_publish/0B6Okdz75tqQsVlhxNGJCNTEzNFU/components-buttons-fab-behavior_04_xhdpi_009.webm

Estoy usando android.support.v4.view.ViewPager

Agradezco cualquier pista y ayuda

En primer lugar, debe tener el diseño con el botón de acción flotante anclado a ViewPager:

        

Ahora que tiene el FAB anclado a ViewPager (nota: he probado esto en un fragmento que es una pestaña en el viewpager, pero parece que no se comporta correctamente), agregue un OnPageChangeListener a su ViewPager de la siguiente manera:

  viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { switch (position) { case INDEX_OF_TAB_WITH_FAB: fab.show(); break; default: fab.hide(); break; } } @Override public void onPageScrollStateChanged(int state) { } }); 

Las animaciones para el “pop” y el “encogimiento” al cambiar las tabs se manejan automáticamente con la nueva versión de la Biblioteca de soporte.

Aquí hay otra solución. El problema para mí con otros es que dar el control del FAB a la actividad que contiene el ViewPager dificulta el código.

Necesito muchos controles en el FAB por parte de los Fragmentos para cambiar fácilmente el ícono de FAB y establecer oyentes específicos en él.

Aproximadamente, los pasos son:

  1. Declara un FAB en la actividad principal que contiene el ViewPager, y no en los fragmentos (es un paso obligatorio que quería evitar, pero la magia viene después, de lo contrario hace las cosas difíciles para una buena animación)
  2. En cada fragmento que se mostrará en ViewPager, creo un método donde puedo pasar un FAB, como public void shareFab(FloatingActionButton sharedFab) ; Dentro de este método, cada fragmento configurará el icono de FAB y agregará los oyentes necesarios
  3. Configuré un oyente ViewPager.OnPageChangeListener en ViewPager que activará animaciones y llamará a mis métodos shareFab .

Por lo tanto, cada vez que se muestre un fragmento, la actividad contenedora dará al fragmento mostrado la referencia del FAB. El fragmento luego restablece / recicla el FAB para satisfacer sus necesidades.

Aquí hay un ejemplo:

Los fragmentos para mostrar

 public void Fragment1 extends Fragment { private FloatingActionButton mSharedFab; @Override public void onDestroy() { super.onDestroy(); mSharedFab = null; // To avoid keeping/leaking the reference of the FAB } public void shareFab(FloatingActionButton fab) { if (fab == null) { // When the FAB is shared to another Fragment if (mSharedFab != null) { mSharedFab.setOnClickListener(null); } mSharedFab = null; } else { mSharedFab = fab; mSharedFab.setImageResource(R.drawable.ic_search); mSharedFab.setOnClickListener((fabView) -> { // Your code }); } } } 

La actividad del contenedor

 public class ActivityMain extends AppCompatActivity { FloatingActionButton mSharedFab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSharedFab = (FloatingActionButton) findViewById(R.id.shared_fab); ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); Fragment1 fragment1 = new Fragment1(); Fragment2 fragment2 = new Fragment2(); adapter.addFragment(fragment1, "Fragment1"); adapter.addFragment(fragment2, "Fragment2"); ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); viewPager.setAdapter(adapter); fragment1.shareFab(mSharedFab); // To init the FAB 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_DRAGGING: mSharedFab.hide(); // Hide animation break; case ViewPager.SCROLL_STATE_IDLE: switch (viewPager.getCurrentItem()) { case 0: fragment2.shareFab(null); // Remove FAB from fragment fragment1.shareFab(mSharedFab); // Share FAB to new displayed fragment break; case 1: default: fragment1.shareFab(null); // Remove FAB from fragment fragment2.shareFab(mSharedFab); // Share FAB to new displayed fragment break; } mSharedFab.show(); // Show animation break; } } }); } } 

… y el diseño clásico de la actividad principal

          

Los pros son:

  • ¡animación como la que se describe en las pautas de Material Design!
  • control total del FAB por cada fragmento

Los contras son:

  • dado que el FAB se puede compartir con otros fragmentos, la referencia del FAB debe verificarse cada vez para asegurarse de que no es nulo
  • statement del FAB a nivel de ViewPager, un poco engorroso

Para una buena animación de ocultar y mostrar, usa esto:

 mViewPager.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_IDLE: fab.show(); break; case ViewPager.SCROLL_STATE_DRAGGING: case ViewPager.SCROLL_STATE_SETTLING: fab.hide(); break; } } }); 

La solución que descubrí fue:

  1. Mis fragmentos que se muestran en mi ViewPager implementan una interfaz (en mi caso FloatingActionButtonFragment ). Esta interfaz requiere que el fragmento tenga un método handleFABClick(View) .

  2. Mi escucha de cambio de ViewPager se ve a continuación:

     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: displayFloatingActionButtonIfNeeded(viewPager.getCurrentItem()); break; case ViewPager.SCROLL_STATE_IDLE: displayFloatingActionButtonIfNeeded(viewPager.getCurrentItem()); break; default: mFloatingActionButton.hide(); } } }); private void displayFloatingActionButtonIfNeeded(int position) { if (mFragmentStatePagerAdapter.getItem(position) instanceof FloatingActionButtonFragment) { final FloatingActionButtonFragment floatingActionButtonFragment = (FloatingActionButtonFragment) mFragmentStatePagerAdapter.getItem(position); mFloatingActionButton.show(); mFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { floatingActionButtonFragment.handleFABClick(v); } }); } } 

Esto hace que el FAB se ‘oculte’ cuando el usuario cambia las vistas, pero luego activa el método show cuando el estado de la vista está inactivo (se ha solucionado) o se está resolviendo (necesitaba hacer ambas cosas, ya que no me sentía bien solo usando ocioso).

Recientemente encontré una solución mucho mejor con la que estoy satisfecho. El método FAB hide también puede tener un parámetro – listener que se activa cuando el botón está oculto (el método show también tiene esto). El único inconveniente es que debe implementar manualmente los cambios de viewpager y tablayout.

 @Override public void onPageSelected(int position) { onToolbarTitleChanged(adapter.getPageTitle(position).toString()); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } @Override public void onTabSelected(TabLayout.Tab tab) { viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(final TabLayout.Tab tab) { fab.hide(new FloatingActionButton.OnVisibilityChangedListener() { // Hide FAB @Override public void onHidden(FloatingActionButton fab) { fab.show(); // After FAB is hidden show it again } }); } @Override public void onTabReselected(TabLayout.Tab tab) { } 

Si desea mostrar FAB solo en tabs específicas, puede cambiar el método onTabSelected a:

 @Override public void onTabSelected(TabLayout.Tab tab) { if (tab.getPosition() == 2) { createButton.hide(); } viewPager.setCurrentItem(tab.getPosition()); } 

Para mi problema, no quería mostrar el botón de acción flotante en la primera pestaña; para lograr esto agregué

 android:visibility="invisible" 

en la barra de acción flotante.

  

Y agregué ViewpagerListner en mi actividad según lo sugerido por @chantell

 mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { switch (position) { case 0: addFloatingActionBt.hide(); break; default: addFloatingActionBt.show(); break; } } @Override public void onPageScrollStateChanged(int state) { } });