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

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

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.


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

            }
        });

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

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

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


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

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.


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

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

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");
            }
        }
    });
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.