Android CollapsingToolbarLayout kollabieren Listener


106

Ich benutze CollapsingToolBarLayoutzusammen mit AppBarLayoutund CoordinatorLayout, und sie funktionieren insgesamt gut. Ich habe festgelegt, dass mein ToolbarFehler behoben werden soll, wenn ich nach oben scrolle. Ich möchte wissen, ob es eine Möglichkeit gibt, den Titeltext der Symbolleiste zu ändern, wenn CollapsingToolBarLayouter reduziert ist.

Zum Abschluss möchte ich zwei verschiedene Titel, wenn sie gescrollt und erweitert werden .

Vielen Dank im Voraus

Antworten:


150

Ich teile die vollständige Implementierung, basierend auf @Frodio Beggins und @Nifhel Code:

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

Und dann können Sie es verwenden:

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

21
Das ist richtig. Aber bitte nicht, dass mit Proguard diese Aufzählung in einen ganzzahligen Wert übersetzt wird.
Rciovati

1
Das wusste ich nicht. Das ist großartig!
Tim687

2
Auch Aufzählungen sind eine sehr gute Möglichkeit, die Typensicherheit zu gewährleisten. Sie können State.IMPLODED nicht haben, weil es nicht existiert (der Compiler würde sich beschweren), aber mit Integer-Konstanten könnten Sie einen Wert verwenden, von dem der Compiler keine Ahnung hat, dass er falsch ist. Sie sind auch gut als Singletons, aber das ist eine andere Geschichte.
Droppin_science

@ Droppin_science für Android Enums überprüfen Sie IntDef
David Darias

1
@ DavidDarias Persönlich finde ich Enums einen viel saubereren Ansatz, auch mit ihrem Overhead (beginnen Sie hier zu
streiten

95

Diese Lösung funktioniert perfekt für mich, um AppBarLayoutkollabierte oder erweiterte zu erkennen .

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

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


            }
            else
            {
                //Expanded


            }
        }
    });

Wird addOnOffsetChangedListenerauf dem verwendet AppBarLayout.


36

Haken Sie ein OnOffsetChangedListeneran Ihr AppBarLayout. Wenn der verticalOffsetWert 0 oder weniger als die ToolbarHöhe erreicht, bedeutet dies, dass CollapsingToolbarLayout reduziert wurde, andernfalls wird es erweitert oder erweitert.

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
es funktioniert nicht für mich. OnCollapse Ich möchte die Home-Schaltfläche aktivieren und auf Erweitern versteckte ich die Home-Schaltfläche
Maheshwar Ligade

9
Die VerticalOffset-Werte scheinen Null zu sein, wenn die Symbolleiste vollständig erweitert ist, und werden dann beim Reduzieren negativ. Wenn die Symbolleiste reduziert ist, entspricht vertikales Offset der negativen Höhe der Symbolleiste (-mToolbar.getHeight ()). Also ... die Symbolleiste ist teilweise erweitert "if (verticalOffset> -mToolbar.getHeight ())"
Mike

Falls sich jemand fragt, wo sich die appBarLayout.getVerticalOffset()Methode befindet, können Sie aufrufen appBarLayout.getY(), um denselben Wert abzurufen, der im Rückruf verwendet wird.
Jarett Millard

Leider hat Jarett Millard nicht Recht. Abhängig von Ihrer fitSystemWindow-Konfiguration und der StatusBar-Konfiguration (transparent) appBarLayout.getY()kann es sein, dassverticalOffset = appBarLayout.getY() + statusBarHeight
Capricorn

1
Hat jemand bemerkt, dass mAppBarLayout.addOnOffsetChangedListener (Listener) wiederholt aufgerufen wird, auch wenn wir nicht tatsächlich mit der Appbar interagieren? Oder es ist ein Fehler in meinem Layout / meiner App, bei dem ich dieses Verhalten beobachte. Bitte helfen Sie!
Rahul Shukla

16

Dieser Code hat bei mir funktioniert

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

Bessere Antwort als Nikola Despotoski
Vignesh Bala

Scheint keine verlässliche Lösung zu sein. Ich habe es getestet und auf meinem Gerät sind folgende Werte angegeben: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Entsprechend Ihrer Lösung muss vertikaleOffset im reduzierten Zustand -789 sein, jedoch gleich -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

Sie können den Alpha-Prozentsatz für CollapsingToolBar wie folgt ermitteln:

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

Als Referenz: Link


2
Dies ist eine großartige Antwort, da sie einen normalisierten Versatz ergibt. Meiner Meinung nach hätte die API dies direkt anstelle des verticalOffsetPixelabstands bereitstellen sollen .
DBM

5

Hier ist eine Kotlin- Lösung. Fügen Sie ein OnOffsetChangedListenerzum hinzu AppBarLayout.

Methode A:

Fügen Sie AppBarStateChangeListener.ktIhrem Projekt hinzu:

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

}

Fügen Sie den Listener zu Ihrem hinzu 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 */ }
            }
        }
    }
)

Methode B:

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

3

Diese Lösung funktioniert für mich:

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

Verwenden Sie addOnOffsetChangedListener im AppBarLayout.


Können Sie Ihren vollständigen Code teilen? Was ist State.EXPANDED usw.?
Chetna

1

Wenn Sie CollapsingToolBarLayout verwenden, können Sie dies einfügen

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

1

Dieser Code funktioniert perfekt für mich. Sie können die prozentuale Skala verwenden, wie Sie möchten

@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

Mein Versatzwert in der Symbolleiste wird beim Reduzieren -582 beim Erweitern = 0 angezeigt. Ich finde den Wert also, indem ich den Versatzwert in Toast einstelle und den Code entsprechend ändere.

 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");
            }
        }
    });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.