Как я могу реализовать BottomSheetDialogFragment с фиксированной высотой

Мне нужно реализовать BottomSheetDialogFragment и столкнуться с проблемой. Мне нужно, чтобы мой BottomSheetDialogFragment имел фиксированную высоту. Кто-нибудь знает, как это сделать?

Вот мой xml фрагмент содержимого

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/bottom_sheet_height"
    android:background="@android:color/white"
    android:orientation="vertical">

    <TextView
        android:id="@+id/drag_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:textColor="#FF0000"
        android:text="Title"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@android:color/white"
        android:layout_weight="1"/>

    <TextView
        android:id="@+id/ok_button"
        android:layout_width="match_parent"
        android:layout_height="54dp"
        android:background="@android:color/holo_blue_dark"
        android:gravity="center"
        android:text="Hello"
        android:textColor="@android:color/white"
        android:textSize="24sp"/>

</LinearLayout>

А в setupDialog() я делаю так:

@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    View contentView = View.inflate(getContext(), R.layout.bottom_sheet_dialog_content_view, null);
    dialog.setContentView(contentView);
    CoordinatorLayout.LayoutParams layoutParams = ((CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams());
    CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
    if (behavior != null && behavior instanceof BottomSheetBehavior) {
        ((BottomSheetBehavior) behavior).setBottomSheetCallback(bottomSheetCallback);
        ((BottomSheetBehavior) behavior).setPeekHeight(getResources().getDimensionPixelSize(R.dimen.bottom_sheet_height));
    }

    initRecyclerView(contentView);
}

И поведение вполне обычное:

private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        if (newState == BottomSheetBehavior.STATE_HIDDEN) {
            dismiss();
        }
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
    }
};

UPD: Решено установкой фиксированной высоты RecyclerView. Кто-нибудь знает лучший подход?


person Vladislav Sazanovich    schedule 08.07.2016    source источник


Ответы (3)


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

in styles.xml

<style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/bottomSheetStyleWrapper</item>
</style>

<style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
    <item name="behavior_peekHeight">500dp</item>
</style>

Обновление:

BottomSheetDialog dialog = new BottomSheetDialog(this, R.style.BottomSheetDialog);
dialog.setContentView(R.layout.layout_bottom_sheet);
dialog.show();

Или второй подход:

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

    if( behavior != null && behavior instanceof BottomSheetBehavior ) {
        ((BottomSheetBehavior) behavior).setBottomSheetCallback(mBottomSheetBehaviorCallback);
        ((BottomSheetBehavior) behavior).setPeekHeight(300);
    }
person Harshad Pansuriya    schedule 08.07.2016
comment
Как я могу использовать эти стили? - person Vladislav Sazanovich; 08.07.2016
comment
Да, я вижу. Но, к сожалению, это не сработало для меня. Я думаю, что проблема в RecyclerView, кажется, что он заставляет изменять размер родительского макета, даже если он имеет фиксированную высоту. - person Vladislav Sazanovich; 08.07.2016
comment
@VladislavSazanovich Это то же самое, что я предлагаю вам установить высоту recylcerView, и я говорю вам установить BottomSheet Height в Style. но я не понимаю, почему это не работает в вашем случае.. - person Harshad Pansuriya; 08.07.2016
comment
@VladislavSazanovich использовал этот способ ((BottomSheetBehavior) behavior).setPeekHeight(300); - person Harshad Pansuriya; 08.07.2016
comment
Я тоже не знаю, у меня это не работает) Но спасибо за ответ. - person Vladislav Sazanovich; 08.07.2016
comment
Любое обновление любого решения? Параметр @ironman peekheight хорошо показывает начальный диалог, но его все еще можно перетащить на полную высоту. Кстати, я знаю о STATE_DRAGGING и хакерском решении, чтобы установить его как расширенное, что не работает. Я ищу нижний лист, который остается на фиксированной высоте. - person Steve McMeen; 29.03.2017

Если содержимое RecyclerView заполнено внутри initRecyclerView(contentView);, то при отображении BottomSheet его высота хорошо известна. Чтобы установить высоту BottomSheet динамически и обернуть содержимое, добавьте прослушиватель глобального макета внутри функции onResume BottomSheetDialogFragment:

@Override
public void onResume() {
    super.onResume();
    addGlobaLayoutListener(getView());
}

private void addGlobaLayoutListener(final View view) {
    view.addOnLayoutChangeListener(new OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            setPeekHeight(v.getMeasuredHeight());
            v.removeOnLayoutChangeListener(this);
        }
    });
}

public void setPeekHeight(int peekHeight) {
    BottomSheetBehavior behavior = getBottomSheetBehaviour();
    if (behavior == null) {
        return;
    }
    behavior.setPeekHeight(peekHeight);
}

private BottomSheetBehavior getBottomSheetBehaviour() {
    CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) ((View) getView().getParent()).getLayoutParams();
    CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
    if (behavior != null && behavior instanceof BottomSheetBehavior) {
        ((BottomSheetBehavior) behavior).setBottomSheetCallback(mBottomSheetBehaviorCallback);
        return (BottomSheetBehavior) behavior;
    }
    return null;
}
person R. Zagórski    schedule 08.07.2016
comment
Мне действительно не нравится идея удаления OnLayoutChangeListener. Это может привести к проблемам в будущем. - person Vladislav Sazanovich; 08.07.2016
comment
Он используется только один раз, чтобы получить реальную высоту BottomSheet во время инициализации. Затем он удаляется на свободные ресурсы. Если бы вы добавили его, а не удалили, каждый раз, когда отображается BottomSheet, будет присоединяться новый OnLayoutChangeListener. Быстро у вас закончатся ресурсы (память). - person R. Zagórski; 08.07.2016
comment
@ R.Zagórski, есть ли способы полностью удалить перетаскивание BottomSheetDialogFragment ?? при использовании recyclerview ?? github.com/rubensousa/BottomSheetExample/issues/3 - person LOG_TAG; 23.02.2017
comment
Вы имеете в виду это: stackoverflow.com/questions/ 37757240/ ? - person R. Zagórski; 23.02.2017
comment
что здесь mBottomSheetBehaviorCallback? - person Lenin Raj Rajasekaran; 28.05.2018

Попробуйте следующий код

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

  1. Создать XML-файл макета для фрагмента диалогового окна нижнего листа

layout_bottom_sheet_dialog_fragment.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">


<androidx.appcompat.widget.LinearLayoutCompat
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <androidx.appcompat.widget.LinearLayoutCompat
        android:id="@+id/ll_bottomSheetFrag_userProf"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:padding="5dp">


        <de.hdodenhof.circleimageview.CircleImageView
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"
            android:src="@drawable/ic_profile_icon_nav_d"
            app:civ_border_width="1dp"
            app:civ_border_color="@color/main_white"
            android:layout_height="70dp"
            android:layout_width="70dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:contentDescription="@string/nav_header_desc"
            android:paddingTop="@dimen/nav_header_vertical_spacing"
            android:paddingBottom="@dimen/nav_header_vertical_spacing"
            android:id="@+id/iv_bottomSheetFrag_userPic">
        </de.hdodenhof.circleimageview.CircleImageView>


        <!-- name & email -->
        <androidx.appcompat.widget.LinearLayoutCompat
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:gravity="center"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp">

            <androidx.appcompat.widget.LinearLayoutCompat
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/tv_bottomSheetFrag_userName"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingTop="5dp"
                    android:paddingBottom="5dp"
                    android:gravity="center|start"
                    android:textSize="20sp"
                    android:layout_weight="9"
                    android:theme="@style/styleFontMediumText"
                    android:text="@string/user_name"
                    android:textAppearance="@style/TextAppearance.AppCompat.Body1"
                    android:textColor="@color/black" />

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/iv_bottomSheetFrag_closeDialog"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:visibility="gone"
                    android:contentDescription="@string/app_name"
                    android:src="@drawable/ic_close_black_24dp"
                    />

            </androidx.appcompat.widget.LinearLayoutCompat>



            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/tv_bottomSheetFrag_userEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center|start"
                android:textSize="14sp"
                android:theme="@style/styleFontRegularText"
                android:textColor="@color/primaryLightColor"
                android:text="@string/user_email" />


        </androidx.appcompat.widget.LinearLayoutCompat>



    </androidx.appcompat.widget.LinearLayoutCompat>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="@color/divider_color"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"/>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view_bottomSheetFrag"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:elevation="0dp"
        app:itemTextAppearance="@style/NavDrawerTextStyle"
        app:itemBackground="@android:color/transparent"
        app:itemIconTint="@color/nav_drawer_item_color_state"
        app:itemTextColor="@color/nav_drawer_item_color_state"
        app:menu="@menu/menu_bottom_sheet" />


</androidx.appcompat.widget.LinearLayoutCompat>

  1. Создайте класс для фрагмента нижнего листа, который должен расширять BottomSheetDialogFragment.

BottomSheetFragment.java

public class BottomSheetFragment extends BottomSheetDialogFragment{
@BindView(R.id.iv_bottomSheetFrag_closeDialog) AppCompatImageView iv_closeDialog;
@BindView(R.id.nav_view_bottomSheetFrag_salesPerson) NavigationView nav_view;


private Context context;

//public constructor
public BottomSheetFragment() {

}

//create custom theme for your bottom sheet modal 
@Override
public int getTheme() {
    //return super.getTheme();
    return R.style.AppBottomSheetDialogTheme;
}


@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    //return super.onCreateDialog(savedInstanceState);
    return new BottomSheetDialog(requireContext(), getTheme());  //set your created theme here

}


@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
}


@Override
public void setupDialog(@NonNull Dialog dialog, int style)
{
    super.setupDialog(dialog, style);

    View contentView = View.inflate(getContext(), R.layout.layout_bottom_sheet_dialog_fragment, null);
    context = contentView.getContext();
    ButterKnife.bind(this, contentView);
    dialog.setContentView(contentView);
    //tv_title.setText(getString(R.string.app_name)); R.style.AppBottomSheetDialogTheme

    DisplayMetrics displayMetrics = getActivity().getResources().getDisplayMetrics();
    int width = displayMetrics.widthPixels;
    int height = displayMetrics.heightPixels;
    int maxHeight = (int) (height*0.44); //custom height of bottom sheet

    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
    CoordinatorLayout.Behavior behavior = params.getBehavior();
    ((BottomSheetBehavior) behavior).setPeekHeight(maxHeight);  //changed default peek height of bottom sheet

    if (behavior != null && behavior instanceof BottomSheetBehavior)
    {
        ((BottomSheetBehavior) behavior).setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback()
        {

            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState)
            {
                String state = "";
                switch (newState)
                {
                    case BottomSheetBehavior.STATE_DRAGGING: {
                        //imgBtnClose.setVisibility(View.INVISIBLE);
                        iv_closeDialog.setVisibility(View.GONE);
                        state = "DRAGGING";
                        break;
                    }
                    case BottomSheetBehavior.STATE_SETTLING: {
                        // imgBtnClose.setVisibility(View.INVISIBLE);
                        iv_closeDialog.setVisibility(View.GONE);
                        state = "SETTLING";
                        break;
                    }
                    case BottomSheetBehavior.STATE_EXPANDED: {
                        // imgBtnClose.setVisibility(View.VISIBLE);
                        iv_closeDialog.setVisibility(View.VISIBLE);
                        state = "EXPANDED";
                        break;
                    }
                    case BottomSheetBehavior.STATE_COLLAPSED: {
                        //imgBtnClose.setVisibility(View.INVISIBLE);
                        iv_closeDialog.setVisibility(View.GONE);
                        state = "COLLAPSED";
                        break;
                    }
                    case BottomSheetBehavior.STATE_HIDDEN: {
                        // imgBtnClose.setVisibility(View.INVISIBLE);
                        iv_closeDialog.setVisibility(View.GONE);
                        dismiss();
                        state = "HIDDEN";
                        break;
                    }
                }
                Log.i("BottomSheetFrag", "onStateChanged: "+ state);
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
            }
        });
    }


    //close dialog
    iv_closeDialog.setOnClickListener(view -> dismiss());

}


@Override
public void onDestroyView() {
    super.onDestroyView();
}}
  1. Добавьте эти строки в свой файл styles.xml. styles.xml

<style name="AppBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/AppModalStyle</item>
    </style>
    <style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@drawable/rounded_dialog</item>
    </style>

  1. Округлая форма для нижнего листа. Добавьте этот файл в папку drawables.

rounded_dialog.xml

 <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@android:color/white"/>
    <corners android:topLeftRadius="16dp"
        android:topRightRadius="16dp"/>

</shape>

  1. Наконец, в своей деятельности вызовите этот фрагмент диалога следующим образом. Здесь я вызвал фрагмент onClick прослушивателя элемента bottomNavigationView onClick.

  private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = item ->


{
        Fragment selectedFragment = null;
        switch (item.getItemId()) {
            case R.id.bNav_menu:
                BottomSheetFragment bf = new BottomSheetFragment();
                bf.show(getSupportFragmentManager(), bf.getTag());
                //bf.setArguments(bundle);
                return true;
           
        }
    };

person Rohan Shinde    schedule 20.08.2019