Home > other >  Unreliable DialogFragment layout
Unreliable DialogFragment layout

Time:01-12

I have noticed that sometimes the DialogFragment is displayed horrendously. For example, given the following XML:

<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/spacing_large">

    <ImageView
        android:id="@ id/close"
        android:layout_width="@dimen/icon_size_medium"
        android:layout_height="@dimen/icon_size_medium"
        android:layout_gravity="end"
        android:src="@drawable/ic_close"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        style="@style/Text.Subtitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is a test with 'match_parent'"
        android:textAlignment="center"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@ id/close" />

</androidx.constraintlayout.widget.ConstraintLayout>

It displays this:

enter image description here

On the other hand, the following XML is broken:

<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/spacing_large">

    <ImageView
        android:id="@ id/close"
        android:layout_width="@dimen/icon_size_medium"
        android:layout_height="@dimen/icon_size_medium"
        android:layout_gravity="end"
        android:src="@drawable/ic_close"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        style="@style/Text.Subtitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="This is a test with '0dp'"
        android:textAlignment="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@ id/close" />


</androidx.constraintlayout.widget.ConstraintLayout>

enter image description here

I have only made these changes:

android:layout_width="match_parent" --> android:layout_width="0dp"
add --> app:layout_constraintEnd_toEndOf="parent"

I have experienced this with other layouts (LinearLayout, FrameLayout) as well. Why is this ?

I have seen many questions about settings the sizes of dialogs and I believe it's due to this bug. Most set new layout params in onResume but it feels hacky and I feel like there is an explanation.

Here the DialogFragment implementation used:

class SomeFragment : DialogFragment() {

    private lateinit var binding: FragmentSomeBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        binding = FragmentSomeBinding.inflate(inflater, container, false)
        return binding.root
    }
}

EDIT:

This is an attempt with LinearLayout with similar unexpected behaviour.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/spacing_large">

    <ImageView
        android:layout_width="@dimen/icon_size_medium"
        android:layout_height="@dimen/icon_size_medium"
        android:layout_gravity="end"
        android:src="@drawable/ic_close"/>

    <TextView
        style="@style/Text.Subtitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is a test with 'LinearLayout'"
        android:textAlignment="center"/>

</LinearLayout>

enter image description here

CodePudding user response:

The issue you're experiencing with the DialogFragment layout is likely related to how the layout is being constrained. The ConstraintLayout, which you're using as the root view, uses constraints to determine the position and size of the child views within it.

In the first XML layout, the TextView's width is set to "match_parent", which means that it should take up the full width of the parent ConstraintLayout. This works as expected because the other dimensions of the view (height and position) are also defined by constraints.

In the second XML layout, the TextView's width is set to "0dp", which means that it will have no intrinsic width and will only be as wide as the constraints allow it to be. The problem here is that the view is not being constrained on both sides (start and end). Instead, it's only constrained on the end. That's why it is displayed the way it is. To fix it, you should constrain the view on both sides by adding

app:layout_constraintStart_toStartOf="parent"

Regarding the hacky solution you're mentioning about, it is related to how DialogFragments are created and displayed. Because DialogFragments are not part of the normal view hierarchy, their lifecycle and layout handling are slightly different from regular fragments. One of the solutions to fix the layout is to call requestLayout() on the dialog's root view in the onResume() callback. This makes the dialog recalculate its layout and apply the correct size and position to the view.

I would suggest trying the first solution, adding the constraintStart_toStartOf to your TextView, as the layout_width being set to 0dp doesn't affect the position, but only the width, so that should fix your problem.

CodePudding user response:

the following XML is broken

Both are broken.

Your ConstraintLayout has android:layout_width="match_parent", and you have no control over the parent. The parent's width presumably is wrap_content, given your screenshots, but you should not be relying upon that, as it could vary. Either give the ConstraintLayout a specific width or use wrap_content for its width.

  • Related