Android CollapsingToolbarLayout collapse Listener

106

Używam CollapsingToolBarLayoutrazem z AppBarLayouti CoordinatorLayout, a one działają w porządku. Ustawiam Toolbarjako naprawione, gdy przewijam w górę, chcę wiedzieć, czy istnieje sposób na zmianę tekstu tytułu paska narzędzi po CollapsingToolBarLayoutzwinięciu.

Podsumowując, chcę mieć dwa różne tytuły po przewinięciu i rozwinięciu .

Z góry dziękuję

Anaximandro Andrade
źródło

Odpowiedzi:

150

Udostępniam pełną implementację, opartą na kodzie @Frodio Beggins i @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);
}

A potem możesz go użyć:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) {
        Log.d("STATE", state.name());
    }
});
rciovati
źródło
21
To jest poprawne. Ale proszę, nie to, że używając Proguard, to wyliczenie zostanie przetłumaczone na wartość całkowitą.
rciovati
1
Tego nie wiedziałem. To wspaniale!
tim687
2
Również wyliczenia są bardzo dobrym sposobem na zapewnienie bezpieczeństwa czcionek. Nie możesz mieć State.IMPLODED, ponieważ nie istnieje (kompilator narzekałby), ale ze stałymi typu Integer możesz użyć wartości, o której kompilator nie ma pojęcia, jest błędna. Są również dobrzy jako single, ale to już inna historia.
droppin_science
@droppin_science dla Androida wyliczenia sprawdź IntDef
David Darias
1
@DavidDarias Osobiście uważam, że wyliczenia są znacznie czystsze, nawet z ich narzutem (rozpocznij argumentację tutaj ... :-)
droppin_science
95

To rozwiązanie działa idealnie dla mnie do wykrywania AppBarLayout zwinięte lub rozwinięte.

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

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


            }
            else
            {
                //Expanded


            }
        }
    });

Używany addOnOffsetChangedListenerw AppBarLayout.

Muhamed Riyas M.
źródło
36

Podłącz a OnOffsetChangedListenerdo swojego AppBarLayout. Gdy wartość verticalOffsetosiągnie 0 lub mniej niż Toolbarwysokość, oznacza to, że CollapsingToolbarLayout zwinął się, w przeciwnym razie jest rozwijany lub rozwijany.

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
źródło
1
to nie działa dla mnie. OnCollapse chcę włączyć przycisk Home, a on Expand ukrył przycisk Home
Maheshwar Ligade
9
Wartości verticalOffset wydają się wynosić zero, gdy pasek narzędzi jest w pełni rozwinięty, a następnie stają się ujemne podczas zwijania. Kiedy pasek narzędzi jest zwinięty, verticalOffset jest równe ujemnej wysokości paska narzędzi (-mToolbar.getHeight ()). Więc ... pasek narzędzi jest częściowo rozwinięty "if (verticalOffset> -mToolbar.getHeight ())"
Mike
W przypadku, gdy ktoś zastanawia się, gdzie jest ta appBarLayout.getVerticalOffset()metoda, możesz wywołać appBarLayout.getY()tę samą wartość, która jest używana w wywołaniu zwrotnym.
Jarett Millard
Niestety Jarett Millard nie ma racji. W zależności od konfiguracji dopasowaniaSystemWindow i konfiguracji StatusBar (przezroczysta) appBarLayout.getY()może być takverticalOffset = appBarLayout.getY() + statusBarHeight
Koziorożec
1
Czy ktoś zauważył, że mAppBarLayout.addOnOffsetChangedListener (listener) jest wywoływana wielokrotnie, nawet jeśli w rzeczywistości nie wchodzimy w interakcję z paskiem aplikacji? Lub jest to błąd w moim układzie / aplikacji, w którym obserwuję to zachowanie. Proszę o pomoc!
Rahul Shukla
16

Ten kod zadziałał dla mnie

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
źródło
Lepsza odpowiedź niż Nikola Despotoski
Vignesh Bala
Wydaje się, że nie jest to niezawodne rozwiązanie. Przetestowałem to i wartości na moim urządzeniu są następujące: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Zatem zgodnie z twoim rozwiązaniem verticalOffset w stanie zwiniętym musi wynosić -789, jednak jest równe -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
źródło
10

Możesz uzyskać procent alfa collapsingToolBar, używając poniżej:

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

W celach informacyjnych: link

Naveen Kumar M
źródło
2
To świetna odpowiedź, ponieważ daje znormalizowany offset. Moim zdaniem API powinno to zapewnić bezpośrednio zamiast verticalOffsetodległości w pikselach.
dbm
5

Oto rozwiązanie Kotlin . Dodaj OnOffsetChangedListenerdo AppBarLayout.

Metoda A:

Dodaj AppBarStateChangeListener.ktdo swojego projektu:

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

}

Dodaj słuchacza do 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 */ }
            }
        }
    }
)

Metoda B:

appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
        if (abs(verticalOffset) - appBarLayout.totalScrollRange == 0) { 
            // Collapsed
        } else if (verticalOffset == 0) {
            // Expanded
        } else {
            // Idle
        }
    }
)
olearyj234
źródło
3

To rozwiązanie działa dla mnie:

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

Użyj addOnOffsetChangedListener na AppBarLayout.

Nifhel
źródło
Czy możesz udostępnić cały kod? Co to jest State.EXPANDED itp?
Chetna
1

Jeśli używasz CollapsingToolBarLayout, możesz to umieścić

collapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(activity, android.R.color.transparent));
collapsingToolbar.setTitle(title);
Irving Lóp
źródło
1

Ten kod działa idealnie dla mnie. Możesz użyć skali procentowej Jak chcesz

@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
źródło
0

Wartość przesunięcia mojego paska narzędzi wynosi -582 po zwinięciu, przy expand = 0 Więc znajduję wartość, ustawiając wartość przesunięcia w Toast i odpowiednio zmieniając kod.

 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
źródło