artem
artem

Reputation: 16798

Initially expanded BottomSheetDialog not working

I have the following bottom sheet dialog code:

abstract class BaseMvpBottomSheetFragment : BottomSheetDialogFragment() {

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

        if (isFullscreen) { // true
            dialog.setOnShowListener {
                val bottomSheetDialog = it as BottomSheetDialog
                bottomSheetDialog
                    .findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
                    ?.let { bottomSheetFl ->
                        from(bottomSheetFl).apply {
                            state = STATE_EXPANDED // This code is called, I've checked it with debugger
                        }
                    }
            }
        }

        return dialog
    }

And the following layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/containerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorTransparent"
    tools:background="#000000">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg_bottom_sheet">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/headerLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent">

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/closeIv"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_marginTop="24dp"
                android:layout_marginEnd="24dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_close"
                tools:ignore="ContentDescription" />

            <androidx.appcompat.widget.AppCompatEditText
                android:id="@+id/searchEt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:drawableEnd="@drawable/ic_search_clear"
                android:imeOptions="actionSearch"
                android:inputType="text"
                android:lines="1"
                android:maxLines="1"
                app:layout_constraintTop_toBottomOf="@id/closeIv" />

        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/modelsRv"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/headerLayout"
            tools:listitem="@layout/view_item_model" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

I want dialog to be initially expanded (and layout to take all vertical space), but when RecyclerView is empty, it takes no space at all:

Dialog

I've tried other solutions on SO, but they did not work for me.

Upvotes: 1

Views: 3247

Answers (1)

aminography
aminography

Reputation: 22832

As the height of the RecyclerView is defined MATCH_CONSTRAINTS, I think there is no way to do what you want except set its height explicitly. So, to achieve the desired height we can subtract the height of headerLayout and the device status bar from the height of the screen.

Also, I have changed your code a bit:

  • I set the initialization of the view into the setupDialog and delegate the decision of being a full-screen bottom sheet, to the child class.
  • It is possible to set the transparent background in the setupDialog, so we can remove the root FrameLayout to achieve a smaller view hierarchy in the xml file.


BaseMvpBottomSheetFragment.kt

import android.app.Dialog
import android.content.Context
import android.view.View
import androidx.annotation.LayoutRes
import com.google.android.material.bottomsheet.BottomSheetDialogFragment

abstract class BaseMvpBottomSheetFragment(
    @LayoutRes private val layoutResId: Int
) : BottomSheetDialogFragment() {

    protected val activityContext: Context by lazy { activity!!.applicationContext }
    protected lateinit var rootView: View

    override fun setupDialog(dialog: Dialog, style: Int) {
        super.setupDialog(dialog, style)
        rootView = View.inflate(activityContext, layoutResId, null)
        dialog.setContentView(rootView)
        onInitViews(rootView)
    }

    abstract fun onInitViews(rootView: View)

}


FullScreenBottomSheetDialogFragment.kt

import android.app.Dialog
import android.content.Context
import android.graphics.Point
import android.view.Display
import android.view.View
import android.view.WindowManager
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.android.synthetic.main.full_screen_bottom_sheet.view.*

class FullScreenBottomSheetDialogFragment : BaseMvpBottomSheetFragment(R.layout.full_screen_bottom_sheet) {

    private var isFullscreen = true

    override fun setupDialog(dialog: Dialog, style: Int) {
        super.setupDialog(dialog, style)
        val parentView = rootView.parent as View
        parentView.setBackgroundColor(ContextCompat.getColor(activityContext, R.color.colorTransparent))

        val params = parentView.layoutParams as CoordinatorLayout.LayoutParams
        val behavior = params.behavior
        if (behavior is BottomSheetBehavior<*>) {
            behavior.state = BottomSheetBehavior.STATE_EXPANDED
            if (isFullscreen) {
                behavior.peekHeight = screenSize(activityContext).y
            }
        }
    }

    override fun onInitViews(rootView: View) {
        with(rootView) {
            if (isFullscreen) {
                headerLayout.post {
                    modelsRv.layoutParams.height =
                        screenSize(activityContext).y -
                                headerLayout.measuredHeight - // height of the header view
                                statusBarHeight(activityContext) // height of the status bar
                    modelsRv.requestLayout()
                }
            }

            // do other initializations...
        }
    }

    private fun screenSize(context: Context): Point {
        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val display: Display? = windowManager.defaultDisplay
        val point = Point()
        display?.getSize(point)
        return point
    }

    private fun statusBarHeight(context: Context): Int {
        var result = 0
        val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
        if (resourceId > 0) result = context.resources.getDimensionPixelSize(resourceId)
        return result
    }

}


full_screen_bottom_sheet.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_bottom_sheet">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/headerLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/closeIv"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_marginTop="24dp"
            android:layout_marginEnd="24dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@drawable/ic_close"
            tools:ignore="ContentDescription" />

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/searchEt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:drawableEnd="@drawable/ic_search_clear"
            android:imeOptions="actionSearch"
            android:inputType="text"
            android:lines="1"
            android:maxLines="1"
            app:layout_constraintTop_toBottomOf="@id/closeIv" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/modelsRv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/headerLayout"
        tools:listitem="@layout/view_item_model" />

</androidx.constraintlayout.widget.ConstraintLayout>

Upvotes: 2

Related Questions