Android Core

Android promoted Actions: Floating action button (FAB)

Recently new design pattern were introduced in Android. One of the most known pattern is the Promoted Actions. They are actions that  are directly visible in the UI instead of using action bar button; for this reason these actions are called promoted, they can be easily accessed and define the main action in the current UI. For example, if we are using an email app and we are listing the inbox folder, one promoted action can be a new mail. The visual pattern is called Float Action Button (or FAB) because the promoted action can be represented using a floating circular button on the UI.

This type of design patterns are available in Android L but we can create a floating button also in previous Android version. Let us suppose we have a list of elements in our UI, the promoted action can be ‘Add new item’, so we can have something like the pic shown below:

android_floating_action_button_1[4]

There are several way we can use to crate a floating action button.

Floating action button using ImageButton

The first way to create a FAB is using the ImageButton, we simply add the ImageButton to our UI layout and exploit some new features provided by Android L.

<ImageButton
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/plus"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:tint="@android:color/white"
android:id="@+id/fab"
android:elevation="1dp"
android:background="@drawable/ripple"
android:stateListAnimator="@anim/fab_anim"
/>

If we look above, we can notice it is a normal ImageButton that we place usually at lower right corner. There are some aspects we should consider:

  • Background
  • Shadow
  • Animation

To make our button more “attractive”, we can implement some effect as background. If we notice we use a ripple drawable defined in this way:

<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
    <item>
        <shape android:shape="oval">
            <solid android:color="?android:colorAccent" />
        </shape>
    </item>
</ripple>

We said it is a floating button, so it means it should float above the UI data as if it was in another plane level. This effect can be obtained using the elevation attribute that “moves”  our UI component on Z-axis. The last thing is the animation effect as we press the button. We want to give to the user the feeling the button is sinking as we press it, so we have to enlarge the shadow around it:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_enabled="true"
        android:state_pressed="true">
        <objectAnimator
            android:duration="@android:integer/config_shortAnimTime"
            android:propertyName="translationZ"
            android:valueFrom="@dimen/start_z"
            android:valueTo="@dimen/end_z"
            android:valueType="floatType" />
    </item>
    <item>
        <objectAnimator
            android:duration="@android:integer/config_shortAnimTime"
            android:propertyName="translationZ"
            android:valueFrom="@dimen/end_z"
            android:valueTo="@dimen/start_z"
            android:valueType="floatType" />
    </item>
</selector>

Floating Action Button using a Custom Component

Another way to create our Floating Button is using a custom component. In this case we don’t use the Android L feature but we code it manually.

As first thing, we create our class that represents the custom component:

public class CustomFAB extends ImageButton {
...
}

The next step is reading some custom attributes that affect the UI behavior, we can do it in the method called init:

private void init(AttributeSet attrSet) {
    Resources.Theme theme = ctx.getTheme();
    TypedArray arr = theme.obtainStyledAttributes(attrSet, R.styleable.FAB, 0, 0);
    try {
        setBgColor(arr.getColor(R.styleable.FAB_bg_color, Color.BLUE));
        setBgColorPressed(arr.getColor(R.styleable.FAB_bg_color_pressed, Color.GRAY));
        StateListDrawable sld = new StateListDrawable();

        sld.addState(new int[] {android.R.attr.state_pressed}, createButton(bgColorPressed));
        sld.addState(new int[] {}, createButton(bgColor));
        setBackground(sld);
    }

    catch(Throwable t) {}
    finally {
         arr.recycle();
    }

}

At the same time we define these attributes in a XML file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FAB">
        <!-- Background color -->
        <attr name="bg_color" format="color|reference"/>
        <attr name="bg_color_pressed" format="color|reference"/>
    </declare-styleable>
</resources>

If you notice we have some attribute that control the background color. At line 8 we define several button states using StateListDrawable and for each state we create our button:

private Drawable createButton(int color) {
    OvalShape oShape = new OvalShape();
    ShapeDrawable sd = new ShapeDrawable(oShape);
    setWillNotDraw(false);
    sd.getPaint().setColor(color);

    OvalShape oShape1 = new OvalShape();
    ShapeDrawable sd1 = new ShapeDrawable(oShape);

    sd1.setShaderFactory(new ShapeDrawable.ShaderFactory() {
        @Override
        public Shader resize(int width, int height) {
            LinearGradient lg = new LinearGradient(0,0,0, height,
                    new int[] {
                            Color.WHITE,
                            Color.GRAY,
                            Color.DKGRAY,
                            Color.BLACK
                    }, null, Shader.TileMode.REPEAT);

            return lg;
        }
    });

    LayerDrawable ld = new LayerDrawable(new Drawable[] { sd1, sd });
    ld.setLayerInset(0, 5, 5, 0, 0);
    ld.setLayerInset(1, 0, 0, 5, 5);

    return ld;
}

Finally we can add this component to our Layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:custom="http://schemas.android.com/apk/res/com.survivingwithandroid.fab"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MyActivity">
...

    <com.survivingwithandroid.fab.CustomFAB
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:src="@android:drawable/ic_input_add"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        custom:bg_color="@color/light_blue"
        android:tint="@android:color/white"
     />
     
</RelativeLayout>
  • Source code available @ github

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button