Cheok Yan Cheng
Cheok Yan Cheng

Reputation: 42796

Limit the Maximum Expansion Height for BottomSheetDialogFragment

Currently, we have implemented the following UI components:

  1. BottomSheetDialogFragment
  2. NestedScrollView
  3. EditText

The current behavior is illustrated in the screenshot below.

enter image description here


As the user types, the height of the BottomSheetDialogFragment increases. If the user continues to type, the height of the BottomSheetDialogFragment will expand until it fills the full screen.

enter image description here


However, rather than expanding to full screen, we would like the BottomSheetDialogFragment to increase in height only up to a certain point, after which it should stop to prevent it from covering the full screen. This desired behavior is shown in the following screenshot. Beyond this point, if the user continues to type, the NestedScrollView should become scrollable.

enter image description here


Do you have any ideas on how we can achieve this?

Below is our current implementation of the source code.

todo_input_dialog_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">

    <!--
    It appears that setting android:fillViewport to either true or false makes no difference
    in our case. We are following the approach used in the noteplus project.
    -->
    <!--
    Use NestedScrollView, if our bottom sheet dialog is draggable.
    -->
    <androidx.core.widget.NestedScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:fillViewport="true">
        <EditText
            android:id="@+id/edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:fontFamily="@font/open_sans_regular"
            android:hint="Add a Todo"
            android:inputType="textMultiLine"
            android:padding="16dp"
            android:textColor="?attr/primaryTextColor"
            android:textSize="18sp" />
    </androidx.core.widget.NestedScrollView>


    <!--
        If we place the button in bottom sheet, we need to specific app:rippleColor explicitly, to
        prevent ripple color from using colorPrimary
    -->
    <com.google.android.material.button.MaterialButton
        android:elevation="4dp"
        android:translationZ="4dp"
        android:stateListAnimator="@null"

        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="8dp"

        android:id="@+id/submit_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="56dp"
        app:cornerRadius="28dp"
        android:gravity="center"
        android:layout_gravity="end"
        app:backgroundTint="@color/colorAccentLight"

        app:rippleColor="#7fffffff"
        app:iconTint="@color/primaryTextColorDark"
        app:iconGravity="textStart"
        app:iconPadding="0dp"
        app:icon="@drawable/send_24px" />
</LinearLayout>

TodoInputDialogFragment.kt

class TodoInputDialogFragment : BottomSheetDialogFragment() {
    private var _binding: TodoInputDialogFragmentBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    private var keyboardVisible = false

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = TodoInputDialogFragmentBinding.inflate(inflater, container, false)

        val root: View = binding.root

        // https://stackoverflow.com/questions/64947700/how-to-adjust-dialog-layout-when-soft-keyboard-appears-using-the-latest-windowin
        ViewCompat.setOnApplyWindowInsetsListener(root) { _, insets ->

            // https://stackoverflow.com/a/63595830/72437
            val currKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
            if (!currKeyboardVisible && (this.keyboardVisible != currKeyboardVisible)) {
                dismiss()
            }
            this.keyboardVisible = currKeyboardVisible

            insets
        }

        binding.submitButton.setOnClickListener {
            submit()
        }

        return root
    }

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

        focusAndShowKeyboard(requireActivity(), binding.editText)
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = super.onCreateDialog(savedInstanceState)


        // https://stackoverflow.com/questions/64947700/how-to-adjust-dialog-layout-when-soft-keyboard-appears-using-the-latest-windowin
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            dialog.window?.also {
                it.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
            }
        }

        // https://stackoverflow.com/questions/46861306/how-to-disable-bottomsheetdialogfragment-dragging
        //dialog.setOnShowListener { dialogInterface ->
        //    val bottomSheet = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
        //
        //    if (bottomSheet != null) {
        //        val behavior: BottomSheetBehavior<*> = BottomSheetBehavior.from(bottomSheet)
        //        behavior.isDraggable = false
        //    }
        //}

        return dialog
    }

    override fun getTheme(): Int {
        return com.yocto.wetodo.R.style.TodoInputDialogFragmentStyle
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    private fun submit() {
    }
}

styles.xml

<!-- https://stackoverflow.com/a/58107195/72437 -->
<style name="TodoInputDialogFragmentStyle" parent="@style/ThemeOverlay.MaterialComponents.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/BottomSheet_Rounded</item>
    <item name="android:windowSoftInputMode">adjustResize</item>
    <item name="android:windowIsFloating">false</item>
</style>

Upvotes: 0

Views: 430

Answers (4)

user21928976
user21928976

Reputation: 21

Probably you would add layout change listener to register changes to the EditText, and limit the height if exceeded a certain value:

binding.editText.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
    val dialogHeight = requireView().height
    val maxHeight = 700 // rough value
    if (dialogHeight > maxHeight) {
        requireView().setLayoutParams(
            FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, maxHeight)
        )
    }
}

or use binding.editText.addTextChangedListener instead.

Upvotes: 0

Kamal Subhani
Kamal Subhani

Reputation: 91

Yes, you can limit the maximum height of bottomSheet at run time. Here is the medium article about it https://dorianpavetic.medium.com/android-sticky-view-at-the-bottom-of-bottom-sheet-dialog-fragment-ac91242bc1b7

You can also review its sample on github https://github.com/dorianpavetic/StickyBottomSheet?source=post_page-----ac91242bc1b7--------------------------------

Upvotes: 0

Khush Parmar
Khush Parmar

Reputation: 561

You can achieve this by limiting the maximum lines of your Edittext. When your Edittext fills with number of line it will increase the height of the bottom shit and so the behaviour fills like the background views are invisible by the bottom shit. By changing the Edittext to below where i mentioned the maxLines attribute of the Edittext will manage the maximum height of the Edittext and so your bottom shit will never go beyond some height.

<EditText
        android:id="@+id/edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:hint="Add a Todo"
        android:maxLines="3"
        android:inputType="textMultiLine"
        android:padding="16dp"
        android:textSize="18sp" />

You can manage the lines as per your need.

Upvotes: 0

jokuskay
jokuskay

Reputation: 1041

BottomSheetDialogFragment uses BottomSheetDialog internally. You can change its behaviour like:

// TodoInputDialogFragment.kt

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState)
    (dialog as BottomSheetDialog).getBehavior().setMaxHeight(400) // <-- add this
    return dialog
}

Upvotes: 0

Related Questions