Android BottomSheetDialogFragment не расширяется полностью

У меня есть следующая реализация тестового нижнего листа.

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

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

bottom_sheet_dialog_main

<?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:id="@+id/locUXCoordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:id="@+id/locUXView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:orientation="vertical"
        app:behavior_hideable="false"
        app:behavior_peekHeight="0dp"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="1 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="2 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="3 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="4 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="5 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="6 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="7 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="8 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="9 Value" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="First Value" />
    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>

Код Java

public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment {

    private static BottomSheetBehavior bottomSheetBehavior;
    private static View bottomSheetInternal;
    private static MyBottomSheetDialogFragment INSTANCE;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;
                CoordinatorLayout coordinatorLayout = (CoordinatorLayout)d.findViewById(R.id.locUXCoordinatorLayout);
                bottomSheetInternal = d.findViewById(R.id.locUXView);
                bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal);
                bottomSheetBehavior.setPeekHeight(bottomSheetInternal.getHeight());
                bottomSheetInternal.requestLayout();
                coordinatorLayout.getLayoutParams().height = bottomSheetInternal.getHeight();
                Toast.makeText(getActivity(), "Height is" + bottomSheetInternal.getHeight() + "  " + coordinatorLayout.getLayoutParams().height, Toast.LENGTH_LONG).show();

            }
        });
        INSTANCE = this;
        return inflater.inflate(R.layout.bottom_sheet_dialog_main, container, false);
    }
}

person Nandish A    schedule 31.05.2016    source источник
comment
Вы понимаете, что высота просмотра — это высота свернутого листа, который просто показывает его часть? Если вы установите высоту просмотра на высоту самого листа (или больше)... тогда весь просмотр бесполезен. Не могли бы вы объяснить, что вы пытаетесь сделать?   -  person David Medenjak    schedule 11.06.2016
comment
Спасибо за это. Да, я понял, что peekHeight был установлен для свернутого представления. В любом случае, я смог исправить проблему   -  person Nandish A    schedule 13.06.2016
comment
эта строка сделала мне трюк: coordinatorLayout.getLayoutParams().height = bottomSheetInternal.getHeight();   -  person Andrii Kovalchuk    schedule 17.08.2016


Ответы (9)


Используя этот код в onCreateView.

getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.design_bottom_sheet);
            CoordinatorLayout coordinatorLayout = (CoordinatorLayout) bottomSheet.getParent();
            BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
            bottomSheetBehavior.setPeekHeight(bottomSheet.getHeight());
            coordinatorLayout.getParent().requestLayout();
        }
    });
person athysirus    schedule 09.03.2017
comment
В моем случае этот код должен вызываться в onActivityCreated, тогда он отлично работает - person acntwww; 21.09.2017
comment
У меня сработало хорошее решение, я поместил код в onViewCreated() - person Mohamed ElSawaf; 01.01.2019

Я нашел другое решение. Возможно, будущим читателям это будет полезно.

@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    final View root = View.inflate(getContext(), R.layout.fragment_bottom_sheet_choose_time, null);
    dialog.setContentView(root);
    initView(root);

    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) root.getParent()).getLayoutParams();
    CoordinatorLayout.Behavior behavior = params.getBehavior();

    if (behavior != null && behavior instanceof BottomSheetBehavior) {
        mBottomSheetBehavior = (BottomSheetBehavior) behavior;
        mBottomSheetBehavior.setBottomSheetCallback(mBottomSheetBehaviorCallback);

        root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                int height = root.getMeasuredHeight();
                mBottomSheetBehavior.setPeekHeight(height);
            }
        });
    }
}

Как упоминал @Anthonyeef, здесь ViewTreeObserver предназначен для получения точной высоты измерения после того, как представление действительно измерено, а GlobalOnLayoutListener удалено для повышения производительности.

Но, пожалуйста, перед использованием в продакшене протестируйте это решение на разных устройствах и экранах, потому что, если ваш контент на нижнем листе выше, чем ваш экран, это может привести к странному поведению при пролистывании.

person Denshov    schedule 10.08.2016
comment
Этот ответ действительно решает мою проблему. Если бы можно было еще что-то объяснить, было бы неплохо. Например, я ищу и тестирую его сам, наконец выясняю, что ViewTreeObserver предназначен для получения точной высоты измерения после того, как представление действительно измеряется, а GlobalOnLayoutListener удален для лучшей производительности. - person Anthonyeef; 27.11.2016
comment
Я получаю несколько отчетов о сбоях, в которых происходит сбой приведения к CoordinatorLayout.LayoutParams. Кажется, что на некоторых устройствах в некоторых ситуациях view.getParent() возвращает LinearLayout вместо CoordinatorLayout. На данный момент отчеты о сбоях доступны только для некоторых устройств Android 8.0. - person Catalin Morosan; 17.07.2018
comment
В чем разница между setupDailog и onCreateDialog? - person ysfcyln; 14.11.2019

При более глубоком изучении пользовательского интерфейса мы обнаруживаем, что есть еще один элемент CoordinatorLayout, обертывающий наш макет координатора. У родителя CoordinatorLayout есть FrameLayout с BottomSheetBehaviorс идентификатором design_bottom_sheet. Высота просмотра, установленная из нашего кода выше, была ограничена из-за высоты match_parent FrameLayout с идентификатором design_bottom_sheet.

Установив высоту просмотра FrameLayout с идентификатором design_bottom_sheet, эта проблема была решена.

    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            coordinatorLayout = (CoordinatorLayout) d.findViewById(R.id.locUXCoordinatorLayout);
            bottomSheetInternal = d.findViewById(R.id.locUXView);
            bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal);
            bottomSheetBehavior.setHidable(false);
            BottomSheetBehavior.from((View)coordinatorLayout.getParent()).setPeekHeight(bottomSheetInternal.getHeight());
            bottomSheetBehavior.setPeekHeight(bottomSheetInternal.getHeight());
            coordinatorLayout.getParent().requestLayout();

        }
    });
person Nandish A    schedule 13.06.2016
comment
Это решение выдает ошибки в R.id.locUXCoordinatorLayout, не удается разрешить символ. Какой R мы должны использовать здесь ..? - person Amritpal Singh; 23.06.2016
comment
locUXCoordinatorLayout , это был идентификатор, который у меня был. Вы можете не иметь его в своем представлении. Смотрите код, который я прикрепил к вопросу. Если это решит вашу проблему, оцените, если вы проголосуете за ответ - person Nandish A; 24.06.2016

Спасибо @athysirus за аккуратный подход. Вот версия, с которой я столкнулся, на случай, если кто-то захочет получить рабочий образец kotlin.

Важно отметить, что вы также должны удалить прослушиватель глобального макета, как только это будет сделано.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            val bottomSheet = (dialog as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
            BottomSheetBehavior.from<View>(bottomSheet).apply {
                state = BottomSheetBehavior.STATE_EXPANDED
                peekHeight = 0
            }
            view.viewTreeObserver.removeOnGlobalLayoutListener(this)
        }
    })
person Mike T    schedule 06.08.2019
comment
Но это решение создает другую проблему - после интерактивного перемещения BottomSheetDialog вниз фоновое окно остается затемненным. - person Gregory; 10.04.2020
comment
Да, он остается затемненным - person Happy Singh; 01.11.2020

Kotlin безопасный способ добиться этого:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    dialog.setOnShowListener {
        val dialog = it as BottomSheetDialog
        val bottomSheet = dialog.findViewById<View>(R.id.design_bottom_sheet)
        bottomSheet?.let { sheet ->
            dialog.behavior.peekHeight = sheet.height
            sheet.parent.parent.requestLayout()
        }
    }
}

ПРИМЕЧАНИЕ. Нет необходимости оборачивать макет в макет координатора.

Работает как шарм.

person Muhammad Umair Shafique    schedule 16.11.2019
comment
Я изо всех сил пытался найти это! Спасибо! - person Ricardo; 07.08.2020

вот как я установил peek_height и layout_height для просмотра нижнего листа в BottomSheetDialogFragment

dialog?.setOnShowListener {
                val dialog = dialog as BottomSheetDialog
                val bottomSheet = dialog.findViewById<FrameLayout>(R.id.design_bottom_sheet)
                val coordinatorLayout = bottomSheet?.parent as? CoordinatorLayout
                val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
                bottomSheet?.viewTreeObserver?.addOnGlobalLayoutListener {
                    bottomSheet.viewTreeObserver.removeOnGlobalLayoutListener {}
                    bottomSheetBehavior.peekHeight = getPopupHeight(.5f)
                    val params = bottomSheet.layoutParams
                    params.height = getPopupHeight(1f)
                    bottomSheet.layoutParams = params
                    coordinatorLayout?.parent?.requestLayout()
                }
            }

этот метод, чтобы получить процент высоты экрана

 private fun getPopupHeight(percent: Float): Int {
        val displayMetrics = DisplayMetrics()
        activity?.windowManager?.defaultDisplay?.getMetrics(displayMetrics)
        return (displayMetrics.heightPixels * percent).toInt()
    }
person Saba Khutuashvili    schedule 17.10.2019

Решение на Kotlin вдохновлено более подробной записью Nandish A. Сначала макет:

<androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/container_root"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/bottom_sheet"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:behavior_hideable="false"
            app:behavior_peekHeight="0dp"
            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

                  <!-- content of the bottom sheet -->

        </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

А затем вставьте это в свой BottomSheetDialogFragment:

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        // ...        

        dialog.setOnShowListener {

            val root = dialog.find<CoordinatorLayout>(R.id.container_root)
            val bottomSheetInternal = root.find<ConstraintLayout>(R.id.bottom_sheet)

            val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal)
            bottomSheetBehavior.isHideable = false
            BottomSheetBehavior.from(root.parent as View).peekHeight = root.height
            bottomSheetBehavior.peekHeight = root.height
            root.parent.requestLayout()
        }

        // ...
    }
person Paul Spiesberger    schedule 30.09.2019

Это сделало трюк для меня! Мой класс расширяет BottomSheetDialogFragment

@Override
public void onStart()
{
    super.onStart();
    Dialog dialog = getDialog();

    if (dialog != null)
    {
        View bottomSheet = dialog.findViewById(R.id.bottom_sheet);
        bottomSheet.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
    }
    View view = getView();
    view.post(() -> {
        View bottomSheet = dialog.findViewById(R.id.bottom_sheet);
        View parent = (View) view.getParent();
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) (parent).getLayoutParams();
        CoordinatorLayout.Behavior behavior = params.getBehavior();
        BottomSheetBehavior bottomSheetBehavior = (BottomSheetBehavior) behavior;
        bottomSheetBehavior.setPeekHeight(view.getMeasuredHeight());
        ((View) bottomSheet.getParent()).setBackgroundColor(Color.TRANSPARENT);
    });
}

@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.screen_delivery_type, container, false);

    getDialog().setOnShowListener(new DialogInterface.OnShowListener()
    {
        @Override
        public void onShow(DialogInterface dialog)
        {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.bottom_sheet);
            CoordinatorLayout coordinatorLayout = (CoordinatorLayout) bottomSheet.getParent();
            BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
            bottomSheetBehavior.setPeekHeight(bottomSheet.getHeight());
            bottomSheetBehavior.setFitToContents(true);
            bottomSheetBehavior.setExpandedOffset(0);
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            coordinatorLayout.getParent().requestLayout();
        }
    });
}
person seinta    schedule 25.05.2020

Этот код представляет собой комбинацию ответов. Потому что некоторые из них не работают для меня правильно.

В XML ids.xml:

<item name="sheet_parent_container" type="id" />

В макете XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@id/sheet_parent_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom">

    **Dialog content here!**

</androidx.coordinatorlayout.widget.CoordinatorLayout>

В BottomSheetDialogFragment.kt:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    ...

    setPeekHeight(view)
}

/**
 * Function for disable half height display after screen rotation.
 */
private fun setPeekHeight(view: View) {
    val parentContainer = view.findViewById<CoordinatorLayout>(R.id.sheet_parent_container)

    dialog?.setOnShowListener {
        val dialogParent = parentContainer.parent as View
        BottomSheetBehavior.from(dialogParent).peekHeight = parentContainer.height
        dialogParent.requestLayout()
    }
}
person SerjantArbuz    schedule 28.02.2021