Reputation: 1705
I have the following test bottom sheet implementation.
When I set the peekHeight to a value less than 500, it works. After some value, any increase in peek height will not change how the bottom sheet is expanded. It Just remains there to only drag manually. How do we set the peekHeight programmatically to ensure that the bottom sheet is auto expanded to the peek height.
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 code
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);
}
}
Upvotes: 36
Views: 49834
Reputation: 91
By using BottomSheetCallback like this :
class MyBottomSheetDialogFragment() : BottomSheetCallback() {
private val bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheet)
bottomSheetBehavior.addBottomSheetCallback(this)
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
val slideOffset: Float = bottomSheetBehavior.calculateSlideOffset()
if (newState == STATE_EXPANDED && slideOffset < 1f) {
// Enforce to expand the bottom sheet fully.
bottomSheetBehavior.halfExpandedRatio = slideOffset
bottomSheetBehavior.state = STATE_HALF_EXPANDED
bottomSheetBehavior.state = STATE_EXPANDED
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
if (slideOffset > 1f) {
// Reduce the height of the expanded bottom sheet.
bottomSheetBehavior.halfExpandedRatio = BOTTOM_SHEET_SLIDE_OFFSET_ALMOST_FULL
bottomSheetBehavior.state = STATE_HALF_EXPANDED
bottomSheetBehavior.state = STATE_EXPANDED
}
}
companion object {
private const val BOTTOM_SHEET_SLIDE_OFFSET_ALMOST_FULL = 0.9f
}
}
Upvotes: 0
Reputation: 1145
You can just call setFitToContents(true)
method on BottomSheetBehavior
to make it not expand fully and its height will be equal to the height of you bottom sheet layout. Be sure that your layout is height is set to wrap_content
.
Upvotes: 0
Reputation: 1244
This code is combination of answers. Because some of them don't works for me correct.
In XML ids.xml:
<item name="sheet_parent_container" type="id" />
In XML layout:
<?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>
In 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()
}
}
Upvotes: 0
Reputation: 115
This made the trick for me! My class extends the 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();
}
});
}
Upvotes: 0
Reputation: 2583
Kotlin safe way of achieving this is:
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()
}
}
}
NOTE: No need to wrap your layout in coordinator layout.
Works like charm.
Upvotes: 18
Reputation: 132
this is how I set peek_height
and layout_height
to bottom sheet view in 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()
}
}
this method to get percent of screen height
private fun getPopupHeight(percent: Float): Int {
val displayMetrics = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(displayMetrics)
return (displayMetrics.heightPixels * percent).toInt()
}
Upvotes: 1
Reputation: 5770
Solution in Kotlin inspired by Nandish A's post in more detail. First the layout:
<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>
And then put this into your 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()
}
// ...
}
Upvotes: 1
Reputation: 1194
Thanks @athysirus for the neat approach. Here is the version I ended up with, in case somebody wants to have a working kotlin sample.
Important to note is, that you should also remove the global layout listener, once done.
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)
}
})
Upvotes: 6
Reputation: 1414
I've found another solution. Maybe for future readers it can be useful.
@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);
}
});
}
}
As @Anthonyeef mentioned, here ViewTreeObserver
is aimed to get the exact measure height after the view is really measured and the GlobalOnLayoutListener
is removed for better performance.
But please, before using in production, test this solution on different devices and screens, because if your content in bottom sheet is higher than your screen it can produce some strange swipe behavior.
Upvotes: 21
Reputation: 471
Using this code in 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();
}
});
Upvotes: 45
Reputation: 1705
By deeper UI inspection, we find that there is another CoordinatorLayout
that wraps our coordinator layout. The parent CoordinatorLayout
has a FrameLayout
with a BottomSheetBehavior
with the id design_bottom_sheet
. The peek height set from our code above was getting constrained due the match_parent
height of the FrameLayout
with the id design_bottom_sheet
By setting the peek height of the FrameLayout
with the id design_bottom_sheet , this issue was resolved
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();
}
});
Upvotes: 12