extended android android-viewpager material-design floating-action-button

android - extended - floating action button menu



AnimaciĆ³n FAB con viewpager/tabslider (6)

Estoy tratando de encontrar la manera de lograr tal 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

Aprecio cualquier consejo y ayuda


Aquí hay otra solución. El problema para mí con otros es que otorgar el control de la FAB a la actividad que contiene el ViewPager hace que sea difícil codificarlo.

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

A grandes rasgos, 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; hacer lo contrario dificulta las cosas para una buena animación)
  2. En cada fragmento que se muestra en el ViewPager, creo un método donde puedo pasar un FAB, como public void shareFab(FloatingActionButton sharedFab) ; Dentro de este método, cada fragmento establecerá el ícono FAB y agregará los oyentes necesarios.
  3. Configuré una escucha ViewPager.OnPageChangeListener en el ViewPager que activará animaciones y llamará a mis métodos shareFab .

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

Aquí hay un ejemplo:

Los fragmentos a desplegar.

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 trazado clásico de la actividad principal.

<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="fixed" app:tabGravity="fill"/> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" 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/shared_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" /> </android.support.design.widget.CoordinatorLayout>

Los pros son:

  • Animación como la descrita en las pautas de Diseño de Materiales!
  • Control total de la FAB por cada fragmento.

Los contras son:

  • ya que la FAB se puede compartir con otros fragmentos, la referencia de la FAB debe verificarse cada vez que se asegure que no sea nula
  • Declaración de la FAB a nivel del ViewPager, un poco engorroso

La solución que descubrí fue:

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

  2. Mi oyente 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 la FAB se "esconda" cuando el usuario está cambiando las vistas, pero luego activa el método de mostrar cuando el estado de la vista está inactivo (se ha calmado) o se está resolviendo (necesitaba hacer las dos cosas porque no me sentía bien usando solo ocioso).


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 barra de acción flotante.

<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="@dimen/fab_margin" android:visibility="invisible" app:srcCompat="@android:drawable/ic_input_add" />

Y he agregado ViewpagerListner en mi actividad como lo sugiere @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) { } });


Para una buena animación de hide y show 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; } } });


Primero, debe tener el diseño con el botón de acción flotante anclado al ViewPager:

<android.support.design.widget.CoordinatorLayout android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <android.support.v7.widget.Toolbar android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" app:tabGravity="center" app:tabIndicatorColor="?attr/colorAccent" app:tabMode="scrollable" app:tabSelectedTextColor="?attr/colorAccent" app:tabTextColor="@android:color/black" /> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" 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/fab" app:layout_anchor="@id/viewpager" app:layout_anchorGravity="bottom|right|end" android:layout_width="56dp" android:layout_height="56dp" android:src="@drawable/ic_add_white" app:borderWidth="@dimen/fab_border_width" app:fabSize="normal" /> </android.support.design.widget.CoordinatorLayout>

Ahora que tiene el FAB anclado en el ViewPager (nota: lo he intentado en un fragmento que es una pestaña en el viewpager; parece que no se comporta correctamente), agregue un OnPageChangeListener a su ViewPager así:

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 de pestaña se manejan automáticamente con la nueva versión de la biblioteca de soporte.


Recientemente encontré una solución mucho mejor con la que estoy satisfecho. El método de ocultación FAB también puede tener un detector de parámetros que se activa cuando el botón está oculto (el método de mostrar también lo tiene). El único inconveniente es que tiene que implementar los cambios de viewpager y tablayout manualmente.

@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 pestañas específicas, puede cambiar el método onTabSelected para:

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