Search code examples
androidmaterial-designfloating-action-buttonmaterial-components-androidmaterial-components

How to shape a FAB to be rounded corners button (pill shape), similar to in contact-details screen of Contacts app?


Background

Initially, a FAB (floating action button) only had one shape : circle, and it only had the option to have an icon in it.

The problem

In the past year or so, Google added various screens in its apps, that show a FAB that changes its size. For example, if you open its Contacts app and choose a contact, you a FAB as such:

enter image description here

And if you scroll a bit, it gets to be a circle, with just the icon in it:

enter image description here

I want to know how to make such a FAB, and also use similar one with just an icon in the middle (without text), meaning a rounded corners one.

Something like that:

enter image description here

What I've tried and found

At first, I tried to just set the width of the FAB to be larger, but this didn't help, as it insisted on being a perfect circle (fit in a square, and without edges).

Seeing that maybe FAB can't handle this currently, I used MaterialCardViewinstead:

                <com.google.android.material.card.MaterialCardView

android:layout_width="60dp" android:layout_height="32dp" android:clickable="true" android:focusable="true" app:cardBackgroundColor="..." app:cardCornerRadius="16dp" >

                    <androidx.appcompat.widget.AppCompatImageView
                        android:layout_width="wrap_content" android:layout_height="wrap_content"
                        android:layout_gravity="center" app:srcCompat="..."/>
                </com.google.android.material.card.MaterialCardView>

This works, but the clicking effect is gone.

I also tried MaterialButton, but this doesn't support an icon in the middle, only on the various of the text inside.

Later, I've noticed that FAB actually does have a way to customize the shape of itself :

https://developer.android.com/reference/com/google/android/material/floatingactionbutton/FloatingActionButton#setshapeappearance

https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/floatingactionbutton/FloatingActionButton.java

Also, as for the expanding FAB, I've found this:

https://developer.android.com/reference/com/google/android/material/floatingactionbutton/ExtendedFloatingActionButton

https://material.io/develop/android/components/extended-floating-action-button/

So looking at its code, I've found that it uses a shape of a pill, and I tried to do a similar thing to the normal FAB :

class PillFab @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    FloatingActionButton(context, attrs, defStyleAttr) {
    init {
        val shapeAppearanceModel =
            ShapeAppearanceModel(context, attrs, defStyleAttr, DEF_STYLE_RES, ShapeAppearanceModel.PILL)
        shapeAppearance = shapeAppearanceModel
    }

    companion object {
        private val DEF_STYLE_RES = R.style.Widget_MaterialComponents_FloatingActionButton
    }
}

Sadly, this didn't work at all. It's still in a circle shape.

The question

How can I set a FAB (with clicking effect) that has rounded corners and a single icon inside?


Solution

  • It is not the perfect answer because you are looking for an ExtendedFAB without text.

    However you can obtain something similar using a MaterialButton changing the component’s shape using the attribute shapeAppearanceOverlay.

    enter image description here

    <com.google.android.material.button.MaterialButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MyApp.Button.Rounded"
                app:icon="@drawable/outline_cached_black_18dp"
                app:iconPadding="0dp"
                app:iconGravity="textStart"
                />
    

    using:

      <style name="ShapeAppearanceOverlay.MyApp.Button.Rounded" parent="">
        <item name="cornerFamily">rounded</item>
        <item name="cornerSize">50%</item>
      </style>
    

    Currenty the app:iconGravity doesn't support the center value. A workaround to center the icon is using these attrs:

                app:iconPadding="0dp"
                app:iconGravity="textStart"
    

    If you would like to override some theme attributes from a default style now you can use new materialThemeOverlay attribute.

    Something like:

    <style name="MtButtonStyle"
     parent="Widget.MaterialComponents.Button">
       <item name=“materialThemeOverlay”>@style/MyButtonThemeOverlay</item>
    </style>
    
    <style name="MyButtonThemeOverlay">
      <item name="colorPrimary">@color/...</item>
      <item name="colorOnSurface">@color/....</item>
    </style>