Android CollapsingToolbarLayout collapse Listener

106

J'utilise CollapsingToolBarLayoutavec AppBarLayoutet CoordinatorLayout, et ils fonctionnent très bien. Je configure mon Toolbarpour être corrigé lorsque je fais défiler vers le haut, je veux savoir s'il existe un moyen de modifier le texte du titre de la barre d'outils, lorsqu'elle CollapsingToolBarLayoutest réduite.

En conclusion, je veux deux titres différents lors du défilement et de l' expansion .

Merci d'avance à tous

Anaximandro Andrade
la source

Réponses:

150

Je partage l'implémentation complète, basée sur le code @Frodio Beggins et @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);
}

Et puis vous pouvez l'utiliser:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) {
        Log.d("STATE", state.name());
    }
});
rciovati
la source
21
C'est correct. Mais s'il vous plaît pas que l'utilisation de Proguard cette enum va être traduite en une valeur entière.
rciovati
1
Je ne savais pas ça. C'est génial!
tim687
2
Les énumérations sont également un très bon moyen d'assurer la sécurité des caractères. Vous ne pouvez pas avoir State.IMPLODED car il n'existe pas (le compilateur se plaindrait) mais avec les constantes Integer, vous pouvez utiliser une valeur que le compilateur n'a aucune idée est fausse. Ils sont également bons comme singletons, mais c'est une autre histoire.
droppin_science
@droppin_science pour les enums Android consultez IntDef
David Darias
1
@DavidDarias Personnellement, je trouve que les énumérations sont beaucoup plus propres même avec leurs frais généraux (commencez la discussion ici ... :-)
droppin_science
95

Cette solution fonctionne parfaitement pour moi pour détecter les AppBarLayouteffondrés ou les étendues.

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

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


            }
            else
            {
                //Expanded


            }
        }
    });

Utilisé addOnOffsetChangedListenersur le AppBarLayout.

Muhamed Riyas M
la source
36

Accrochez un OnOffsetChangedListenerà votre AppBarLayout. Lorsque le verticalOffsetatteint 0 ou moins que la Toolbarhauteur, cela signifie que CollapsingToolbarLayout s'est réduit, sinon il est en expansion ou en expansion.

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);
                }

            }
        });
Nikola Despotoski
la source
1
cela ne fonctionne pas pour moi. OnCollapse je veux activer le bouton d'accueil et sur Développer a
masqué le
9
Les valeurs verticalOffset semblent être nulles lorsque la barre d'outils est entièrement développée, puis deviennent négatives lors de la réduction. Lorsque la barre d'outils est réduite, verticalOffset est égal à négatif la hauteur de la barre d'outils (-mToolbar.getHeight ()). Donc ... la barre d'outils est partiellement développée "if (verticalOffset> -mToolbar.getHeight ())"
Mike
Au cas où quelqu'un se demanderait où se trouve la appBarLayout.getVerticalOffset()méthode, vous pouvez appeler appBarLayout.getY()pour récupérer la même valeur que celle utilisée dans le rappel.
Jarett Millard
Malheureusement Jarett Millard n'a pas raison. En fonction de votre configuration fitsSystemWindow et de la configuration de StatusBar (transparent), appBarLayout.getY()il se peut queverticalOffset = appBarLayout.getY() + statusBarHeight
Capricorn
1
Quelqu'un a-t-il remarqué si mAppBarLayout.addOnOffsetChangedListener (auditeur) est appelé à plusieurs reprises même si nous n'interagissons pas réellement avec la barre d'applications? Ou c'est un bogue dans ma mise en page / application où j'observe ce comportement. Aide Plz!
Rahul Shukla
16

Ce code a fonctionné pour moi

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
            }
        }
    });
SAI
la source
Meilleure réponse que Nikola Despotoski
Vignesh Bala
Semble ne pas être une solution fiable. Je l'ai testé et les valeurs sur mon appareil sont les suivantes: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Donc, selon votre solution, verticalOffset à l'état réduit doit être de -789, mais il est égal à -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;
            }
        }
    });
}
terrakok
la source
10

Vous pouvez obtenir le pourcentage alpha de collapsingToolBar en utilisant ci-dessous:

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

Pour référence: lien

Naveen Kumar M
la source
2
C'est une excellente réponse car elle donne un décalage normalisé. À mon avis, l'API aurait dû fournir cela directement au lieu de la verticalOffsetdistance de pixel.
dbm
5

Voici une solution Kotlin . Ajoutez un OnOffsetChangedListenerau AppBarLayout.

Méthode A:

Ajoutez AppBarStateChangeListener.ktà votre projet:

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?
    )

}

Ajoutez l'auditeur à votre 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 */ }
            }
        }
    }
)

Méthode B:

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

Cette solution fonctionne pour moi:

@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;
  }
}

Utilisez addOnOffsetChangedListener sur AppBarLayout.

Nifhel
la source
Pouvez-vous partager votre code complet? Qu'est-ce que State.EXPANDED, etc.?
Chetna
1

Si vous utilisez CollapsingToolBarLayout, vous pouvez mettre ceci

collapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(activity, android.R.color.transparent));
collapsingToolbar.setTitle(title);
Irving Lóp
la source
1

Ce code fonctionne parfaitement pour moi. Vous pouvez utiliser l'échelle de pourcentage comme vous le souhaitez

@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");
    }
}
Artur Gniewowski
la source
0

La valeur de décalage de ma barre d'outils obtient -582 lors de la réduction, lors de l'expansion = 0.Je trouve donc une valeur en définissant la valeur de décalage dans Toast et changez le code en conséquence.

 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");
            }
        }
    });
Mazhar Ali
la source