Слушатель свертывания Android CollapsingToolbarLayout

106

Я использую CollapsingToolBarLayoutвместе с AppBarLayoutи CoordinatorLayout, и они в целом работают нормально. Я установил Toolbarфиксацию при прокрутке вверх, я хочу знать, есть ли способ изменить текст заголовка панели инструментов, когда CollapsingToolBarLayoutона свернута.

В заключение, мне нужны два разных заголовка при прокрутке и при раскрытии .

Спасибо всем заранее

Анаксимандро Андраде
источник

Ответы:

150

Я разделяю полную реализацию, основанную на коде @Frodio Beggins и @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);
}

И тогда вы можете использовать это:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) {
        Log.d("STATE", state.name());
    }
});
Рчовати
источник
21
Это правильно. Но не обращайте внимания на то, что при использовании Proguard это перечисление будет преобразовано в целое число.
rciovati
1
Я этого не знал. Замечательно!
tim687
2
Кроме того, перечисления - очень хороший способ обеспечить безопасность типов. У вас не может быть State.IMPLODED, потому что его не существует (компилятор будет жаловаться), но с целочисленными константами вы можете использовать значение, о котором компилятор не подозревает, является неправильным. Они хороши и как одиночки, но это уже другая история.
droppin_science
@droppin_science для перечислений Android проверьте IntDef
Дэвид Дариас
1
@DavidDarias Лично я считаю, что перечисления намного чище, даже с их накладными расходами (начните аргумент здесь ... :-)
droppin_science
95

Это решение отлично работает для меня, чтобы обнаружить AppBarLayoutсвернутый или развернутый.

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

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


            }
            else
            {
                //Expanded


            }
        }
    });

Используется addOnOffsetChangedListenerна платформе AppBarLayout.

Мухамед Рияс М
источник
36

Подключите OnOffsetChangedListenerк вашему AppBarLayout. Когда высота verticalOffsetдостигает 0 или меньше Toolbar, это означает, что CollapsingToolbarLayout свернут, в противном случае он расширяется или расширяется.

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
это не работает для меня. OnCollapse я хочу включить кнопку «Домой», а «Развернуть» спрятал кнопку «Домой»
Maheshwar Ligade
9
Значения verticalOffset кажутся равными нулю, когда панель инструментов полностью развернута, а затем становятся отрицательными при сворачивании. Когда панель инструментов свернута, verticalOffset равно отрицательной высоте панели инструментов (-mToolbar.getHeight ()). Итак ... панель инструментов частично расширена "if (verticalOffset> -mToolbar.getHeight ())"
Майк
Если кому-то интересно, где находится appBarLayout.getVerticalOffset()метод, вы можете вызвать его, appBarLayout.getY()чтобы получить то же значение, которое используется в обратном вызове .
Джаретт Миллард,
К сожалению, Джаретт Миллард не прав. В зависимости от вашей конфигурации fitsSystemWindow и конфигурации StatusBar (прозрачной) appBarLayout.getY()это может быть такverticalOffset = appBarLayout.getY() + statusBarHeight
Козерог
1
Кто-нибудь заметил, если mAppBarLayout.addOnOffsetChangedListener (слушатель) вызывается неоднократно, даже если мы фактически не взаимодействуем с панелью приложений? Или это ошибка в моем макете / приложении, где я наблюдаю за этим поведением. Помогите, пожалуйста!
Рахул Шукла,
16

Этот код работал у меня

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
            }
        }
    });
ВОФК
источник
Лучший ответ, чем Никола Деспотоски
Бала
Вроде не надежное решение. Я проверил его, и значения на моем устройстве следующие: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Итак, согласно вашему решению verticalOffset в свернутом состоянии должно быть -789, однако оно равно -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

Вы можете получить альфа-процент collapsingToolBar, используя ниже:

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

Для справки: ссылка

Навин Кумар М
источник
2
Это отличный ответ, поскольку он дает нормализованное смещение. На мой взгляд, API должен был предоставить это напрямую, а не verticalOffsetрасстояние в пикселях.
dbm
5

Вот решение Kotlin . Добавьте OnOffsetChangedListenerв AppBarLayout.

Метод А:

Добавьте AppBarStateChangeListener.ktв свой проект:

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

}

Добавьте слушателя в свой 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 */ }
            }
        }
    }
)

Метод Б:

appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
        if (abs(verticalOffset) - appBarLayout.totalScrollRange == 0) { 
            // Collapsed
        } else if (verticalOffset == 0) {
            // Expanded
        } else {
            // Idle
        }
    }
)
olearyj234
источник
3

Это решение работает для меня:

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

Используйте addOnOffsetChangedListener в AppBarLayout.

Nifhel
источник
Вы можете поделиться своим полным кодом? Что такое State.EXPANDED и т. Д.?
Chetna
1

Если вы используете CollapsingToolBarLayout, вы можете поместить это

collapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(activity, android.R.color.transparent));
collapsingToolbar.setTitle(title);
Ирвинг Лоп
источник
1

Этот код работает идеально для меня. Вы можете использовать процентную шкалу Как вам нравится

@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

Значение смещения моей панели инструментов при свертывании получается -582 при расширении = 0 Итак, я нахожу значение, установив значение смещения в Toast и соответствующим образом изменив код.

 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");
            }
        }
    });
Мажар Али
источник