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

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

Когда я устанавливаю peehHight на значение меньше 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);
    }
}

Ответ 1

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

Установив высоту peek 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();

        }
    });

Ответ 2

Использование этого кода в 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();
        }
    });

Ответ 3

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

@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 удалено для лучшей производительности.

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

Ответ 4

Спасибо @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)
        }
    })

Ответ 5

Решение в 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()
        }

        // ...
    }

Ответ 6

вот как я установил peek_height и layout_height в нижний вид листа

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