material-components / material-components-android

Modular and customizable Material Design UI components for Android
Apache License 2.0
16.34k stars 3.06k forks source link

[ExtendedFloatingActionButton] Inconsistent size when expanding if the text is short #3285

Open gmikhail opened 1 year ago

gmikhail commented 1 year ago

Description: If ExtendedFloatingActionButton has short text - the size changes a little when expanding:

https://user-images.githubusercontent.com/4838367/222780184-1af7d8e0-e6db-4e43-9ff0-bdaaf1e1e1dd.mp4

Expected behavior: If text long - everything works as expected:

https://user-images.githubusercontent.com/4838367/222783304-3f8adc6d-aa95-40a9-a500-18c40c242a9b.mp4

Source code:

<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="16dp"
            android:text="Create"
            app:icon="@drawable/ic_add" />

fab.extend()

App theme Theme.Material3.*

Android API version: 30

Material Library version: 1.8.0, 1.9.0-alpha02

Device: POCO X3 NFC

imhappi commented 1 year ago

Did some investigating, it looks like this is due to some unintentional behaviour with setting the minimum width and how we calculate the target width for the extend animation.

When extending the floating action button with a wrap_content width, we animate the width to our guess as to what the wrap_content width will be, which is essentially the measured width plus the padding width of the button. When the animation is done, we set the width back to wrap_content so that the button will have wrap_content behaviour as expected.

When the button content is too short, the measured width actually becomes the minimum width instead of the actual width; we are adding the padding width to the minimum width, when instead it should be adding the padding width to the (smaller) original width and taking whichever one is bigger. Eg. max(paddingWidth + originalWidth, minWidth). The button becomes smaller after the animation because we set the button width to wrap_content which becomes the correct width

Unfortunately there isn't an easy way for us to get the original measured width as getMeasuredWidth() only gives us the width after it's turned into the minimum width. Will do some more investigation to see if it's possible, but in the meantime a temporary workaround could be either:

  1. set fixed dimens for the width of the extended fab, and set the extend strategy to auto
  2. set the min width to 0 and use padding to 'manually' ensure a minimum width
ozgurg commented 1 year ago

Same

Did some investigating, it looks like this is due to some unintentional behaviour with setting the minimum width and how we calculate the target width for the extend animation.

When extending the floating action button with a wrap_content width, we animate the width to our guess as to what the wrap_content width will be, which is essentially the measured width plus the padding width of the button. When the animation is done, we set the width back to wrap_content so that the button will have wrap_content behaviour as expected.

When the button content is too short, the measured width actually becomes the minimum width instead of the actual width; we are adding the padding width to the minimum width, when instead it should be adding the padding width to the (smaller) original width and taking whichever one is bigger. Eg. max(paddingWidth + originalWidth, minWidth). The button becomes smaller after the animation because we set the button width to wrap_content which becomes the correct width

Unfortunately there isn't an easy way for us to get the original measured width as getMeasuredWidth() only gives us the width after it's turned into the minimum width. Will do some more investigation to see if it's possible, but in the meantime a temporary workaround could be either:

  1. set fixed dimens for the width of the extended fab, and set the extend strategy to auto
  2. set the min width to 0 and use padding to 'manually' ensure a minimum width

I had the same issue. Setting android:minWidth to 0dp works for me. It doesn't seem to break anything, but It would be better if we didn't have to do this. Thank you for your assistance.