2015-07-16 19 views
8

Sto provando a fare qualcosa di veramente semplice.Android - FAB da nascondere durante la navigazione tra diversi frammenti in un viewpager

Desidero che il FAB venga visualizzato in una sola scheda del TabLayout e venga nascosto durante la navigazione in un'altra scheda. Ad esempio, una scheda ti consente di aggiungere nuovi elementi nel FAB, ma la scheda successiva non ti consente di aggiungere elementi.

ho seguito la progettazione del layout XML 'tipico':

<?xml version="1.0" encoding="utf-8"?> 
<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/container" 
    xmlns:tools="http://schemas.android.com/tools" 
    > 

<android.support.design.widget.AppBarLayout 
    android:id="@+id/appBarLayout" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content"> 

    <android.support.v7.widget.Toolbar 
     android:id="@+id/toolbar" 
     android:minHeight="?attr/actionBarSize" 
     android:background="?attr/colorPrimary" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     app:layout_scrollFlags="scroll|enterAlways"> 

     <LinearLayout 
      android:id="@+id/search_container" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:gravity="center_vertical" 
      android:orientation="horizontal"> 

      <EditText 
       android:id="@+id/search_view" 
       android:layout_width="0dp" 
       android:layout_height="?attr/actionBarSize" 
       android:layout_weight="1" 
       android:background="@android:color/transparent" 
       android:gravity="center_vertical" 
       android:hint="Search" 
       android:imeOptions="actionSearch" 
       android:inputType="text" 
       android:maxLines="1" 
       android:paddingLeft="2dp" 
       android:singleLine="true" 
       android:textColor="#ffffff" 
       android:textColorHint="#b3ffffff" /> 

      <ImageView 
       android:id="@+id/search_clear" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:layout_gravity="center" 
       android:paddingLeft="16dp" 
       android:paddingRight="16dp" 
       android:src="@drawable/ic_action_cancel" /> 

     </LinearLayout> 

    </android.support.v7.widget.Toolbar> 

    <android.support.design.widget.TabLayout 
     android:id="@+id/sliding_tabs" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:background="?attr/colorPrimary" 
     app:tabMode="scrollable" 
     /> 


</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="com.example.simon.behaviours.PatchedScrollingViewBehavior"/> 

<android.support.design.widget.FloatingActionButton 
    android:id="@+id/fab" 
    app:borderWidth="0dp" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:src="@drawable/ic_action_new" 
    android:layout_margin="16dp" 
    android:layout_gravity="bottom|right" 
    app:layout_anchor="@+id/viewPager" 
    app:layout_anchorGravity="bottom|right|end" 
    app:layout_behavior="com.example.simon.behaviours.ScrollingFABBehavior" 
    android:visibility="gone" 
    app:fabSize="normal"> 
</android.support.design.widget.FloatingActionButton> 

</android.support.design.widget.CoordinatorLayout> 

ho usato il seguente comportamento per la FAB. Questo si traduce in alcun upscrolls per causare la FAB a scomparire e tornerà di nuovo sullo schermo su un downscroll:

public class ScrollingFABBehavior extends FloatingActionButton.Behavior { 
    private int toolbarHeight; 

    public ScrollingFABBehavior(Context context, AttributeSet attrs) { 
     super(); 
     this.toolbarHeight = getToolbarHeight(context); 
    } 

    @Override 
    public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) { 
     return super.layoutDependsOn(parent, fab, dependency) || (dependency instanceof AppBarLayout); 
    } 

    @Override 
    public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) { 
     boolean returnValue = super.onDependentViewChanged(parent, fab, dependency); 
     if (dependency instanceof AppBarLayout) { 
      CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams(); 
      int fabBottomMargin = lp.bottomMargin; 
      int distanceToScroll = fab.getHeight() + fabBottomMargin; 
      float ratio = (float)dependency.getY()/(float)toolbarHeight; 
      fab.setTranslationY(-distanceToScroll * ratio); 
     } 
     return returnValue; 
    } 

    public static int getToolbarHeight(Context context) { 
     final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
       new int[]{R.attr.actionBarSize}); 
     int toolbarHeight = (int) styledAttributes.getDimension(0, 0); 
     styledAttributes.recycle(); 

     return toolbarHeight; 
    } 
} 

Ho aggiunto un addOnPageChangeListener viewpager:

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 
    @Override 
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 

    } 

    @Override 
    public void onPageSelected(int position) { 
     if (position == 0) { 
      FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab); 
      floatingActionButton.setVisibility(View.VISIBLE); 
     } 
     else 
     { 
      FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab); 
      floatingActionButton.setVisibility(View.GONE); 
     } 
    } 

    @Override 
    public void onPageScrollStateChanged(int state) { 

    } 
}); 

voglio solo il FAB a comparire su la prima pagina e scompare su tutte le altre pagine.

Il codice funziona ma quando si scorre verso il basso nella pagina successiva, il FAB appare dopo anche se la visibilità è impostata su esaurito. Penso che abbia qualcosa a che fare con il comportamento impostato per il FAB. Qualcuno sa perché il FAB diventa ancora visibile con un colpo verso il basso se la visibilità è finita?

+1

'FAB' è solo una vista normale. Non è necessario utilizzare layout speciali. Basta cambiare la visibilità o animare la traduzione quando il cercapersone cambia un frammento. – eleven

+0

Dopo aver esaminato il codice FAB, hide() imposta la sua visibilità su View.GONE, mentre show() imposta la visibilità su View.VISIBLE..so sembra quando abbiamo fatto scorrere lo schermo chiamato hide(), e quando abbiamo Scorri lo schermo chiamato show() che lo riporta sempre a View.VISIBLE .. –

+0

Considera l'uso dei metodi standard 'hide()' e 'show()' 'FAB' in' OnPageChangeListener' e rende 'FAB' visibile su predefinito. [Questa soluzione] (http://stackoverflow.com/a/31663686/2047442) funziona per me. – irudyak

risposta

7

Questo non ha finito per essere qualcosa che voglio implementare nella mia app, ma alla fine ho trovato una risposta, con un po 'di aiuto guardando in che modo hanno implementato la stessa cosa sull'app wordpress.

Nell'applicazione wordpress, vediamo un pulsante di azione che galleggia sulla prima pagina del app, che scompare se swipe a qualsiasi delle altre pagine sul viewpager:

wordpress

Lo hanno fatto attraverso il seguente codice: questo è il codice per l'attività che detiene il viewpager. È possibile visualizzare la parte rilevante del codice sotto il metodo onPageScrolled che contiene il bus degli eventi che registra un evento ogni volta che si scorre la pagina. L'evento contiene solo una variabile denominata positionOffset che è un valore intero compreso tra 0 e 1. Se si scorre e la pagina è a metà strada tra i due viewpager, positionOffset è 0.5, si ottiene l'idea:

WPMainActivity.java

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 
      @Override 
      public void onPageSelected(int position) { 
       AppPrefs.setMainTabIndex(position); 

       switch (position) { 
        case WPMainTabAdapter.TAB_NOTIFS: 
         new UpdateLastSeenTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
         break; 
       } 
       trackLastVisibleTab(position); 
      } 

      @Override 
      public void onPageScrollStateChanged(int state) { 
       // noop 
      } 

      @Override 
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
       // fire event if the "My Site" page is being scrolled so the fragment can 
       // animate its fab to match 
       if (position == WPMainTabAdapter.TAB_MY_SITE) { 
        EventBus.getDefault().post(new MainViewPagerScrolled(positionOffset)); 
       } 
      } 
     }); 

l'evento viene prelevato nel frammento che contiene il seguente codice. L'evento sparare il metodo translationY che anima FAB verticalmente quando la pagina scorre secondo quanto la pagina scorre fuori dalla vista come determinato dal positionOffset:

MySiteFragment

/* 
* animate the fab as the users scrolls the "My Site" page in the main activity's ViewPager 
*/ 
@SuppressWarnings("unused") 
public void onEventMainThread(CoreEvents.MainViewPagerScrolled event) { 
    mFabView.setTranslationY(mFabTargetYTranslation * event.mXOffset); 
} 

Infine , il layout nel file my_site_fragment.xml mostra che il FAB viene effettivamente inserito nei frammenti xml anziché nell'attività xml.

<!-- this coordinator is only here due to https://code.google.com/p/android/issues/detail?id=175330 --> 
<android.support.design.widget.CoordinatorLayout 
    android:id="@+id/coordinator" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <android.support.design.widget.FloatingActionButton 
     android:id="@+id/fab_button" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_gravity="end|bottom" 
     android:layout_marginBottom="@dimen/fab_margin" 
     android:layout_marginRight="@dimen/fab_margin" 
     android:src="@drawable/gridicon_create_light" 
     app:borderWidth="0dp" 
     app:rippleColor="@color/fab_pressed" /> 
</android.support.design.widget.CoordinatorLayout> 
1

È possibile farlo a livello di codice quando si passa alla scheda successiva.

FloatingActionButton fab = (FloatingActionButton) view.findViewByID(R.id.fab) 
fab.setVisibility(View.GONE); 

e quindi impostare le visiblity per View.VISIBLE quando si scorrere indietro.

+0

Ciao, ho provato la tua risposta, ma non ho avuto successo. La mia app ha una barra degli strumenti e un FAB che scompare quando scorri verso l'alto e ritorna quando scorri verso il basso. Il FAB scompare quando lo codifico a livello di codice come suggerito in addOnPageChangeListener, ma quando scorro verso il basso, il FAB "restituisce" per visualizzare e diventa visibile anche se l'ho impostato per essere invisibile/andato. – Simon

0

Sembra che si sta avvicinando questo dal punto di vista sbagliato ...

Così, per esempio, una scheda sarebbe permetterà di aggiungere nuovi elementi nel FAB, ma la scheda successiva non avrebbe consente di aggiungere elementi.

Mettere il FAB nel Fragment della prima pagina invece del ospitare Fragment/Activity. In questo modo scomparirà automaticamente insieme al frammento della prima pagina, che si animerà anche al suo fianco. Sarà anche dove appartiene, dal momento che non è affatto correlato alle altre pagine/schede.

Se si ha realmente bisogno per gestire l'evento click nel tuo hosting Fragment/Activity, effettuare la prima chiamata pagina frammento di nuovo al di hosting Fragment/Activity quando si verifica quel clic.

+0

Ciao. Ho provato il tuo approccio ma ancora non sono stato in grado di risolverlo. Il FAB sembra non essere più associato ai movimenti della barra degli strumenti, quindi se la barra degli strumenti scompare quando scorri verso il basso, il FAB appare effettivamente anziché scompare. Se la barra degli strumenti è completamente visibile, il FAB è solo a metà visibile. Penso che il problema sia all'interno del codice per ScrollingFABBehavior. – Simon

+2

Se si inserisce il FAB in ciascun frammento, esso si sposterà nel viewpager, il che è contrario alle linee guida sulla progettazione del materiale: https://www.google.com/design/spec/components/buttons-floating-action-button.html # buttons-floating-action-button-behavior –

1

Creare un booleano statico nella propria attività (vedere YourActivity.isFABinCurrentTabVisible di seguito) e modificarlo dal ViewPager, di conseguenza se ci dovrebbe essere un fab nella scheda corrente o meno. Potrebbe essere una soluzione sporca ... E considerare l'utilizzo di fab.show() e fab.hide() anziché setVisiblity(). Questi eseguono le animazioni di fade-in e fade-out.

public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior { 

public ScrollAwareFABBehavior(Context context, AttributeSet attrs) { 
    super(); 
} 

@Override 
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child, 
            final View directTargetChild, final View target, final int nestedScrollAxes) { 
    // Ensure we react to vertical scrolling 
    return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL 
      || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); 
} 

@Override 
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child, 
          final View target, final int dxConsumed, final int dyConsumed, 
          final int dxUnconsumed, final int dyUnconsumed) { 
    super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); 
    if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) { 
     // User scrolled down and the FAB is currently visible -> hide the FAB 
     child.hide(); 
    } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE && YourActivity.isFABinCurrentTabVisible) { 
     // User scrolled up and the FAB is currently not visible -> show the FAB 
     child.show(); 
    } 
} 
} 
1

Implementare ViewPager.onPageListener e over ride onPageSelected e mostrare o nascondere FAB sulle schede secondo i vostri requisiti seguito è riportato il codice di esempio:

@Override 
public void onPageSelected(int position) { 
    //FAB_PAGE is the index of the page on which you want to show FAB 
    if(position == FAB_PAGE) 
    { 
     fab.setVisibility(View.VISIBLE); 

    } 
    else 
    { 
     fab.setVisibility(View.GONE); 
    } 

} 

È inoltre possibile utilizzare fab.show(); e fab.hide();

Controllare anche this per ulteriori dettagli.

+0

Invece di usare un 'OnPageChangeListener' come sopra, puoi usare Rx e RxBinding per un 'ViewPager'. Sembrerebbe qualcosa di simile: 'RxViewPager.pageSelections (mViewPager) .subscribe ([...])'. Iscriviti per ottenere la posizione della pagina corrente. La cosa bella di questo è che non devi implementare nessuno degli altri callback come faresti con 'OnPageChangeListener'. Basta non dimenticare di annullare l'iscrizione. –

3

Ehi, ho avuto esattamente lo stesso problema di te.

Ho trovato due metodi che hanno funzionato per me.

Metodo 1:

Aggiungi una bandiera statica nel vostro MainActivity: public static boolean fabVisible;.

ho notato che hai un listener per il tuo viewPager per rilevare la pagina di frammento di corrente, in modo da poter aggiungere questo:

public void onPageSelected(int position) { 
     switch (position) { 
      case 0: 
       fabVisible = true; 
       mFloatingActionButton.show(); 
       break; 
      default: 
       fabVisible = false; 
       mFloatingActionButton.hide(); 
       break; 
     } 
    } 

E dentro il vostro comportamento fab personalizzato, aggiungere il seguente codice:

if (dyConsunmed > 0 && child.getVisibility() == View.VISIBLE) { 
      child.hide(); 
     } else if (dyConsunmed < 0 && child.getVisibility() != View.VISIBLE && MainActivity.fabVisible) { 
      child.show(); 
     } 

Il punto di questo metodo è di assicurarsi che il fab dovrebbe essere visibile prima di implementare il metodo show hide &.

Metodo 2:

Non c'è bisogno di aggiungere qualsiasi bandiera come "fabVisible" nel tuo MainActivity più. Sebbene questo metodo richieda di impostare viewPager come statico nel tuo MainActivity, perché dobbiamo essere in grado di accedervi nella nostra classe di comportamento fab personalizzata.

Nella classe ScrollAwareFABBehavior, aggiungere il seguente codice:

if (MainActivity.viewPager.getCurrentItem() == 0) { 
     if (dyConsunmed > 0 && child.getVisibility() == View.VISIBLE) { 
      child.hide(); 
     } else if (dyConsunmed < 0 && child.getVisibility() != View.VISIBLE) { 
      child.show(); 
     } 

    } 

Il punto chiave di questo metodo, non è che per rendere lo spettacolo e il metodo nascondere quando si è all'interno del frammento che si desidera (Nel mio caso si tratta di frammento uno in modo che il currenItem == 0). Ovviamente per rimuovere il fab nel secondo frammento, è comunque necessario nasconderlo all'interno del listener di viewPager.

Se vi sentite ancora confusi riguardo all'implementazione, sentitevi liberi di controllare il mio repository su GitHub e giocare con il codice. L'indirizzo è: https://github.com/Anthonyeef/FanfouDaily

Anche se il contenuto del feed è tutto in cinese, il codice è tutto in inglese.