Отрегулируйте высоту обзора ресайклера, когда постоянный нижний лист развернут

У меня есть постоянный нижний лист (который в основном представляет собой кнопку) и представление ресайклера, которые содержатся в CoordinatorLayout.

Когда нижний лист развернут, я не хочу, чтобы он закрыл обзор ресайклера. Я могу добиться этого, установив app:layout_insetEdge="bottom" на нижнем листе и app:layout_dodgeInsetEdges="bottom" в представлении ресайклера соответственно.

Однако, поскольку высота вида ресайклера установлена ​​на android:layout_height="match_parent", его верхняя часть частично выходит за пределы экрана при раскрытии нижнего листа.

Вместо этого я хочу, чтобы вид ресайклера динамически регулировал свою высоту в соответствии с высотой нижнего листа, чтобы он больше не выходил за пределы экрана. Как я могу этого добиться?

Вот полный макет:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:nestedScrollingEnabled="false"
    app:layout_dodgeInsetEdges="bottom" />

<Button
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/update_all"
    android:foreground="?attr/selectableItemBackground"
    android:background="@drawable/angled_button"
    app:behavior_hideable="false"
    app:behavior_peekHeight="0dp"
    app:layout_insetEdge="bottom"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />

</android.support.design.widget.CoordinatorLayout>

Изменить: скриншоты добавлены

Без нижнего листа все выглядит нормально. введите описание изображения здесь

Когда нижний лист развернут, вид ресайклера больше не виден полностью. введите описание изображения здесь

Редактировать 2: добавлен GIF

введите описание изображения здесь


comment
its top partly moves out of the screen when the bottom sheet is expanded. Не могли бы вы уточнить, добавив гифку или картинку? Было бы здорово, спасибо.   -  person ʍѳђઽ૯ท    schedule 11.09.2018
comment
Вы пытались удалить это ?: android:nestedScrollingEnabled="false"   -  person ʍѳђઽ૯ท    schedule 12.09.2018
comment
Никаких изменений, кроме исчезновения эффектов прокрутки.   -  person Henste    schedule 12.09.2018
comment
Почему бы не позволить нижнему листу закрывать нижнюю часть RecyclerView? Если вы удалите layout_dodgeInsetEdges и layout_insetEdge, вы получите желаемый вид, или ваш фактический макет немного сложнее, чем тот, который вы представили здесь?   -  person Cheticamp    schedule 14.09.2018
comment
Попробуйте использовать AppBarLayout с ToolBar вместо ActionBar. Также добавьте app:layout_behavior="@string/appbar_scrolling_view_behavior" в свой RecyclerView.   -  person Durgadass S    schedule 15.09.2018
comment
@Cheticamp Да, я действительно упростил свой макет для этого вопроса, поэтому нельзя позволять нижнему листу закрывать нижнюю часть RecyclerView.   -  person Henste    schedule 17.09.2018
comment
@DurgadassS Также никаких изменений в поведении. RecyclerView по-прежнему выталкивается из видимой области просмотра   -  person Henste    schedule 17.09.2018


Ответы (4)


Недавно столкнулся с той же проблемой, не нашел лучшего способа, чем удалить app:layout_dodgeInsetEdges="bottom" и вместо этого использовать заполнение. Вот как этого можно достичь:

Котлин:

val rv = findViewById<RecyclerView>(R.id.recycler_view))
val behavior = BottomSheetBehavior.from(findViewById<Button>(R.id.bottom_sheet))
behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback(){
    override fun onSlide(bottomSheet: View, offset: Float) {
        rv.setPadding(0, 0, 0, (bottomSheet.height * offset).toInt())
    }

    override fun onStateChanged(bottomSheet: View, newState: Int){}
})

Ява:

RecyclerView rv = (RecyclerView) findViewById(R.id.recycler_view); 
BottomSheetBehavior behavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        rv.setPadding(0, 0, 0, (int)(slidingView.getHeight() * offset));
    }
});

Плюсы:

  • Отсутствие наложения на экране ни с панелью инструментов, ни с кнопкой BottomSheet;
  • Не нарушает функциональность продвижения для FAB;
  • Прокрутка RecyclerView может выполняться вместе со сдвигом вверх по BottomSheet;

Минусы:

  • Не чистый XML, требуется кодирование;
  • Чтобы сохранить исходное заполнение, требуются дополнительные изменения (но это все еще возможно и относительно легко сделать)
person Nikita Khlebushkin    schedule 19.09.2018
comment
Это правильная идея, но формула неполная (как указано в минусах), и заполнение не влияет на полосу прокрутки ReyclerView. См. Мой ответ о модифицированном решении с учетом этих двух замечаний. - person Ronan; 09.03.2019

В принятом ответе Никиты есть правильная идея, но есть две проблемы:

  • Предлагаемая формула заполнения верна только тогда, когда нижний лист полностью развернут, потому что slideOffset, предоставленный в качестве аргумента для обратного вызова onSlide, пропорционален разнице между высотой взгляда и полной высотой нижнего листа, а не только его полной высотой.

  • Заполнение не применяется к быстрой полосе прокрутки recyclerview, которая продолжает расширяться под нижним листом. С другой стороны, маржа ведет себя так, как задумано.

Вот реализация Kotlin, в которой учтены эти два исправления:

val behavior = BottomSheetBehavior.from(constraintLayout)
behavior.setBottomSheetCallback(object : BottomSheetCallback() {

    override fun onSlide(bottomSheet: View, slideOffset: Float) {
        val margin = behavior.peekHeight + (bottomSheet.height - behavior.peekHeight) * slideOffset
        recyclerview.updateLayoutParams<FrameLayout.LayoutParams> { bottomMargin = margin.toInt() }
    }

    override fun onStateChanged(bottomSheet: View, newState: Int) { /* Nothing to do */ }
})

Тип ссылочного LayoutParams должен быть изменен в соответствии с родительским элементом RecyclerView в вашем собственном макете.

Обратите внимание, что нижнее поле RecyclerView должно быть установлено на высоту взгляда нижнего листа в макете xml, чтобы получить правильный макет при загрузке активности до того, как произойдет скольжение.

Приведенный выше пример относится к нижнему листу, который нельзя скрыть, что означает, что аргумент slideOffset принимает значение только от 0 до 1. Для скрытого нижнего листа следует использовать другую формулу, когда slideOffset находится между -1 и 0: behavior.peekHeight * (1 + slideOffset)

person Ronan    schedule 09.03.2019

Попробуйте поместить этот атрибут в RecyclerView:

app:layout_behavior="@string/appbar_scrolling_view_behavior"
person sebasira    schedule 11.09.2018
comment
К сожалению, никаких улучшений. - person Henste; 11.09.2018

Вы можете сделать 2 решения

1 - установить для верхнего поля RecyclerView

Поскольку ваш CoordinatorLayout не выравнивается RecyclerView ниже Toolbar. Итак, установите верхнее поле actionBarSize.

android:layout_marginTop="?actionBarSize"

2 Если хотите, RecyclerView подойдет Toolbar

Установите android:elevation на RecyclerView, потому что Toolbar имеет 4dp отметку, а макеты более 21 версии располагаются по высоте. Итак, установите

<android.support.v7.widget.RecyclerView
    ...
    android:elevation="@dimen/design_appbar_elevation"/>

design_appbar_elevation значение 16dp.

person Khemraj Sharma    schedule 19.09.2018