Listener di compressione di Android CollapsingToolbarLayout


106

Sto usando CollapsingToolBarLayoutinsieme a AppBarLayoute CoordinatorLayout, e stanno lavorando bene insieme. Ho impostato il mio Toolbarda correggere quando scorro verso l'alto, voglio sapere se c'è un modo per cambiare il testo del titolo della barra degli strumenti, quando CollapsingToolBarLayoutè compressa.

In conclusione, voglio due titoli diversi quando scorro e quando espanso .

Grazie a tutti in anticipo

Risposte:


150

Condivido l'intera implementazione, basata su @Frodio Beggins e codice @Nifhel:

public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {

    public enum State {
        EXPANDED,
        COLLAPSED,
        IDLE
    }

    private State mCurrentState = State.IDLE;

    @Override
    public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
        if (i == 0) {
            if (mCurrentState != State.EXPANDED) {
                onStateChanged(appBarLayout, State.EXPANDED);
            }
            mCurrentState = State.EXPANDED;
        } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
            if (mCurrentState != State.COLLAPSED) {
                onStateChanged(appBarLayout, State.COLLAPSED);
            }
            mCurrentState = State.COLLAPSED;
        } else {
            if (mCurrentState != State.IDLE) {
                onStateChanged(appBarLayout, State.IDLE);
            }
            mCurrentState = State.IDLE;
        }
    }

    public abstract void onStateChanged(AppBarLayout appBarLayout, State state);
}

E poi puoi usarlo:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) {
        Log.d("STATE", state.name());
    }
});

21
È corretto. Ma per favore non che usando Proguard quell'enum verrà tradotto in un valore intero.
rciovati

1
Non lo sapevo. È fantastico!
tim687

2
Anche le enumerazioni sono un modo molto carino per garantire l'indipendenza dai tipi. Non puoi avere State.IMPLODED perché non esiste (il compilatore si lamenterebbe) ma con le costanti Integer potresti usare un valore che il compilatore non ha idea che sia sbagliato. Sono buoni anche come single, ma questa è un'altra storia.
droppin_science

@droppin_science per enumerazioni Android controlla IntDef
David Darias

1
@DavidDarias Personalmente trovo che gli enum siano un approccio molto più pulito anche con il loro sovraccarico (inizia la discussione qui ... :-)
droppin_science

95

Questa soluzione funziona perfettamente per me per rilevare AppBarLayoutcompressi o espansi.

appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {

            if (Math.abs(verticalOffset)-appBarLayout.getTotalScrollRange() == 0)
            {
                //  Collapsed


            }
            else
            {
                //Expanded


            }
        }
    });

Utilizzato addOnOffsetChangedListenersu AppBarLayout.


36

Aggancia un OnOffsetChangedListeneral tuo AppBarLayout. Quando verticalOffsetraggiunge 0 o meno Toolbardell'altezza, significa che CollapsingToolbarLayout è compresso, altrimenti si espande o si espande.

mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                if(verticalOffset == 0 || verticalOffset <= mToolbar.getHeight() && !mToolbar.getTitle().equals(mCollapsedTitle)){
                    mCollapsingToolbar.setTitle(mCollapsedTitle);
                }else if(!mToolbar.getTitle().equals(mExpandedTitle)){
                    mCollapsingToolbar.setTitle(mExpandedTitle);
                }

            }
        });

1
non funziona per me. OnCollapse voglio abilitare il pulsante home e su Expand ho nascosto il pulsante home
Maheshwar Ligade

9
I valori verticalOffset sembrano essere zero quando la barra degli strumenti è completamente espansa e quindi diventa negativo durante la compressione. Quando la barra degli strumenti è compressa, verticalOffset è uguale all'altezza della barra degli strumenti negativa (-mToolbar.getHeight ()). Quindi ... la barra degli strumenti è parzialmente espansa "if (verticalOffset> -mToolbar.getHeight ())"
Mike

Nel caso in cui qualcuno si chieda dove sia il appBarLayout.getVerticalOffset()metodo, puoi chiamare appBarLayout.getY()per recuperare lo stesso valore utilizzato nella richiamata.
Jarett Millard

Sfortunatamente Jarett Millard non ha ragione. A seconda della configurazione di FitSystemWindow e StatusBar (trasparente) appBarLayout.getY()potrebbe essere cheverticalOffset = appBarLayout.getY() + statusBarHeight
Capricorno

1
Qualcuno ha notato se mAppBarLayout.addOnOffsetChangedListener (listener) viene chiamato ripetutamente anche se non stiamo effettivamente interagendo con l'appbar? Oppure è un bug nel mio layout / app in cui sto osservando questo comportamento. Plz help!
Rahul Shukla

16

Questo codice ha funzionato per me

mAppBarLayout.addOnOffsetChangedListener(new   AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if (verticalOffset == -mCollapsingToolbarLayout.getHeight() + mToolbar.getHeight()) {
                //toolbar is collapsed here
                //write your code here
            }
        }
    });

Risposta migliore di Nikola Despotoski
Vignesh Bala

Sembra non essere una soluzione affidabile. L'ho testato e i valori sul mio dispositivo sono i seguenti: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Quindi, in base alla tua soluzione, verticalOffset nello stato compresso deve essere -789, tuttavia è uguale a -693
Leo Droidcoder

16
private enum State {
    EXPANDED,
    COLLAPSED,
    IDLE
}

private void initViews() {
    final String TAG = "AppBarTest";
    final AppBarLayout mAppBarLayout = findViewById(R.id.appbar);
    mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        private State state;

        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if (verticalOffset == 0) {
                if (state != State.EXPANDED) {
                    Log.d(TAG,"Expanded");
                }
                state = State.EXPANDED;
            } else if (Math.abs(verticalOffset) >= appBarLayout.getTotalScrollRange()) {
                if (state != State.COLLAPSED) {
                    Log.d(TAG,"Collapsed");
                }
                state = State.COLLAPSED;
            } else {
                if (state != State.IDLE) {
                    Log.d(TAG,"Idle");
                }
                state = State.IDLE;
            }
        }
    });
}

10

Puoi ottenere la percentuale alfa di collapsingToolBar usando di seguito:

appbarLayout.addOnOffsetChangedListener( new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            float percentage = ((float)Math.abs(verticalOffset)/appBarLayout.getTotalScrollRange());
            fadedView.setAlpha(percentage);
    });

Per riferimento: link


2
Questa è un'ottima risposta in quanto fornisce un offset normalizzato. A mio parere, l'API avrebbe dovuto fornire questo direttamente invece della verticalOffsetdistanza dei pixel.
dbm

5

Ecco una soluzione Kotlin . Aggiungere un OnOffsetChangedListeneral AppBarLayout.

Metodo A:

Aggiungi AppBarStateChangeListener.ktal tuo progetto:

import com.google.android.material.appbar.AppBarLayout
import kotlin.math.abs

abstract class AppBarStateChangeListener : AppBarLayout.OnOffsetChangedListener {

    enum class State {
        EXPANDED, COLLAPSED, IDLE
    }

    private var mCurrentState = State.IDLE

    override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) {
        if (i == 0 && mCurrentState != State.EXPANDED) {
            onStateChanged(appBarLayout, State.EXPANDED)
            mCurrentState = State.EXPANDED
        }
        else if (abs(i) >= appBarLayout.totalScrollRange && mCurrentState != State.COLLAPSED) {
            onStateChanged(appBarLayout, State.COLLAPSED)
            mCurrentState = State.COLLAPSED
        }
        else if (mCurrentState != State.IDLE) {
            onStateChanged(appBarLayout, State.IDLE)
            mCurrentState = State.IDLE
        }
    }

    abstract fun onStateChanged(
        appBarLayout: AppBarLayout?,
        state: State?
    )

}

Aggiungi l'ascoltatore al tuo appBarLayout:

appBarLayout.addOnOffsetChangedListener(object: AppBarStateChangeListener() {
        override fun onStateChanged(appBarLayout: AppBarLayout?, state: State?) {
            Log.d("State", state.name)
            when(state) {
                State.COLLAPSED -> { /* Do something */ }
                State.EXPANDED -> { /* Do something */ }
                State.IDLE -> { /* Do something */ }
            }
        }
    }
)

Metodo B:

appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
        if (abs(verticalOffset) - appBarLayout.totalScrollRange == 0) { 
            // Collapsed
        } else if (verticalOffset == 0) {
            // Expanded
        } else {
            // Idle
        }
    }
)

3

Questa soluzione sta funzionando per me:

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
  if (i == 0) {
    if (onStateChangeListener != null && state != State.EXPANDED) {
      onStateChangeListener.onStateChange(State.EXPANDED);
    }
    state = State.EXPANDED;
  } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
    if (onStateChangeListener != null && state != State.COLLAPSED) {
      onStateChangeListener.onStateChange(State.COLLAPSED);
    }
    state = State.COLLAPSED;
  } else {
    if (onStateChangeListener != null && state != State.IDLE) {
      onStateChangeListener.onStateChange(State.IDLE);
    }
    state = State.IDLE;
  }
}

Usa addOnOffsetChangedListener su AppBarLayout.


Puoi condividere il tuo codice completo? Cos'è State.EXPANDED ecc?
Chetna

1

Se stai usando CollapsingToolBarLayout puoi mettere questo

collapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(activity, android.R.color.transparent));
collapsingToolbar.setTitle(title);

1

Questo codice funziona perfettamente per me. Puoi usare la scala percentuale come preferisci

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
    double percentage = (double) Math.abs(verticalOffset) / collapsingToolbar.getHeight();
    if (percentage > 0.8) {
        collapsingToolbar.setTitle("Collapsed");
    } else {
        collapsingToolbar.setTitle("Expanded");
    }
}

0

Il valore di offset della mia barra degli strumenti ottiene -582 quando viene compresso, in espansione = 0 Quindi trovo il valore impostando offsetvalue in Toast e modifica il codice di conseguenza.

 mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if(verticalOffset == -582) {
            Toast.makeText(MainActivity.this, "collaped" + verticalOffset, Toast.LENGTH_SHORT).show();
            mCollapsingToolbarLayout.setTitle("Collapsed");
            }else if(verticalOffset == 0){
                Toast.makeText(MainActivity.this, "expanded" + verticalOffset, Toast.LENGTH_SHORT).show();
            mCollapsingToolbarLayout.setTitle("expanded");
            }
        }
    });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.