Home » Android » android – How can I implement BottomSheetDialogFragment with fixed height

android – How can I implement BottomSheetDialogFragment with fixed height

Posted by: admin June 16, 2020 Leave a comment

Questions:

I need to implement BottomSheetDialogFragment and face with the problem.
I need that my BottomSheetDialogFragment has fixed height. Does anyone has an idea how to do it?

Here is my xml of fragment content

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

And in the setupDialog() I am doing this:

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

And behavior is quite common:

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: Solved by set the fixed height to RecyclerView. Does anyone know the better approach?

How to&Answers:

You can directly give the fix height by Creating it style.

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>

Update :

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

Or Second Approch :

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

Answer:

If the RecyclerView content is filled inside initRecyclerView(contentView); then when showing BottomSheet it’s height is well known. To set the height of BottomSheet dynamically and to wrap the content then add global layout listener inside onResume function of the 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;
}

Answer:

Try following code

enter image description here

  1. Create layout xml file for bottomsheet dialog fragment

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. Create class for bottom sheet fragment that should extends 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. Add these lines in your 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. Rounded shaped drawable for your bottom sheet. Add this file in your drawables folder.

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. Finally in your activity call this dialog fragment as follows. Here i have called fragment onClick of bottomNavigationView item onClick listener.
  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;
           
        }
    };