Android promoted Actions: Floating action button (FAB)

Recently new design pattern were introduced in Android called Floating Action Button. This pattern is known as 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.

Android Prometed Action: Android Floating button

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

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

Android 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

Webucator has made a video about this post: