Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Implementation of Android L's floating action button pattern. API 14+
package your_package;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
public class FloatingActionButton extends View {
final static OvershootInterpolator overshootInterpolator = new OvershootInterpolator();
final static AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
Context context;
Paint mButtonPaint;
Paint mDrawablePaint;
Bitmap mBitmap;
boolean mHidden = false;
public FloatingActionButton(Context context) {
super(context);
this.context = context;
init(Color.WHITE);
}
public void setFloatingActionButtonColor(int FloatingActionButtonColor) {
init(FloatingActionButtonColor);
}
public void setFloatingActionButtonDrawable(Drawable FloatingActionButtonDrawable) {
mBitmap = ((BitmapDrawable) FloatingActionButtonDrawable).getBitmap();
invalidate();
}
public void init(int FloatingActionButtonColor) {
setWillNotDraw(false);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mButtonPaint.setColor(FloatingActionButtonColor);
mButtonPaint.setStyle(Paint.Style.FILL);
mButtonPaint.setShadowLayer(10.0f, 0.0f, 3.5f, Color.argb(100, 0, 0, 0));
mDrawablePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
setClickable(true);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getWidth() / 2.6), mButtonPaint);
canvas.drawBitmap(mBitmap, (getWidth() - mBitmap.getWidth()) / 2,
(getHeight() - mBitmap.getHeight()) / 2, mDrawablePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
setAlpha(1.0f);
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
setAlpha(0.6f);
}
return super.onTouchEvent(event);
}
public void hideFloatingActionButton() {
if (!mHidden) {
ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 1, 0);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 1, 0);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(scaleX, scaleY);
animSetXY.setInterpolator(accelerateInterpolator);
animSetXY.setDuration(100);
animSetXY.start();
mHidden = true;
}
}
public void showFloatingActionButton() {
if (mHidden) {
ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 0, 1);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 0, 1);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(scaleX, scaleY);
animSetXY.setInterpolator(overshootInterpolator);
animSetXY.setDuration(200);
animSetXY.start();
mHidden = false;
}
}
public boolean isHidden() {
return mHidden;
}
static public class Builder {
private FrameLayout.LayoutParams params;
private final Activity activity;
int gravity = Gravity.BOTTOM | Gravity.RIGHT; // default bottom right
Drawable drawable;
int color = Color.WHITE;
int size = 0;
float scale = 0;
public Builder(Activity context) {
scale = context.getResources().getDisplayMetrics().density;
size = convertToPixels(72, scale); // default size is 72dp by 72dp
params = new FrameLayout.LayoutParams(size, size);
params.gravity = gravity;
this.activity = context;
}
/**
* Sets the gravity for the FAB
*/
public Builder withGravity(int gravity) {
this.gravity = gravity;
return this;
}
/**
* Sets the margins for the FAB in dp
*/
public Builder withMargins(int left, int top, int right, int bottom) {
params.setMargins(
convertToPixels(left, scale),
convertToPixels(top, scale),
convertToPixels(right, scale),
convertToPixels(bottom, scale));
return this;
}
/**
* Sets the FAB drawable
*/
public Builder withDrawable(final Drawable drawable) {
this.drawable = drawable;
return this;
}
/**
* Sets the FAB color
*/
public Builder withButtonColor(final int color) {
this.color = color;
return this;
}
/**
* Sets the FAB size in dp
*/
public Builder withButtonSize(int size) {
size = convertToPixels(size, scale);
params = new FrameLayout.LayoutParams(size, size);
return this;
}
public FloatingActionButton create() {
final FloatingActionButton button = new FloatingActionButton(activity);
button.setFloatingActionButtonColor(this.color);
button.setFloatingActionButtonDrawable(this.drawable);
params.gravity = this.gravity;
ViewGroup root = (ViewGroup) activity.findViewById(android.R.id.content);
root.addView(button, params);
return button;
}
// The calculation (value * scale + 0.5f) is a widely used to convert to dps to pixel units
// based on density scale
// see developer.android.com (Supporting Multiple Screen Sizes)
private int convertToPixels(int dp, float scale){
return (int) (dp * scale + 0.5f) ;
}
}
}
FloatingActionButton fabButton = new FloatingActionButton.Builder(this)
.withDrawable(getResources().getDrawable(R.drawable.ic_action_add))
.withButtonColor(Color.WHITE)
.withGravity(Gravity.BOTTOM | Gravity.RIGHT)
.withMargins(0, 0, 16, 16)
.create();
@sirvon
Copy link

sirvon commented Sep 6, 2014

awesome!

@edarn
Copy link

edarn commented Oct 10, 2014

Nice work!

Is this code free to use for commercial applications?

@Jogan
Copy link
Author

Jogan commented Nov 4, 2014

@edarn Yes! feel free to modify or use however you like

@junbong
Copy link

junbong commented Nov 6, 2014

Awesome! Thanks a lot

@martzcodes
Copy link

martzcodes commented Nov 10, 2014

Awesome! Just in case anyone else runs into this problem... when you use a color from your resources definitions you need to use

     .withButtonColor(getResources().getColor(R.color.accent))

Otherwise you'll get some weird behavior. Took me 30 minutes to figure out, so I hope it saves someone some time.

@pritikjain
Copy link

pritikjain commented Nov 11, 2014

Awesome! it works Perfectly

@mzapatae
Copy link

mzapatae commented Nov 12, 2014

Nice work, but i have a question, y implement the navbar with 3 element (3 fragments) and when use the builder (in 1 fragment), the fab appear in 3 fragment. How i can use the fab button in one fragment (sorry for my english)

@ashokcs
Copy link

ashokcs commented Nov 20, 2014

Great Work man... Thanks

@hvkale
Copy link

hvkale commented Nov 23, 2014

Thank you. This saved me from writing bunch of code and thanks to @oehokie for pointing that out. I wasted an hour on that and then read your comment.

@patelb200
Copy link

patelb200 commented Nov 27, 2014

This is exactly what i was looking for and it works great onCreate. The issue occurs when the screen orientation changes. I am instantiating the button in a listfragment and when the orientation is changed, the button disappears. It does work if i add the button to onResume but i dont know if this best practice. What do you suggest to resolve this issue.

@naruto42071
Copy link

naruto42071 commented Nov 27, 2014

hi i am getting below error
please help
ic_action_add cannot be resolved or is not a field

@roberdcr
Copy link

roberdcr commented Dec 2, 2014

I have forked this code and I adapted it for fragments, the code is here: https://gist.github.com/roberdcr/ef33471fb8bd47165540

@akhtarpucit
Copy link

akhtarpucit commented Dec 2, 2014

Thanks alot...I wasted my whole day on other solutions. But your code worked with no effort.

@dhaag23
Copy link

dhaag23 commented Dec 10, 2014

Any thoughts on providing the ripple effect on Lollipop?

@mishaelharry95
Copy link

mishaelharry95 commented Dec 11, 2014

I instantiated the floating action button in a single activity but it is showing in all my other fragments attached to my navigation drawer, how can i hid it when i navigate to another fragment?

@Golanhershku
Copy link

Golanhershku commented Dec 13, 2014

Awesome man! Thanks!
It's the simplest way I've seen!
Five stars!!

@ThemeTeam
Copy link

ThemeTeam commented Jan 15, 2015

Thanks alot!

@hiteshsahu
Copy link

hiteshsahu commented Jan 27, 2015

Took 2 days to figure out. That helped thank you.

@hiteshsahu
Copy link

hiteshsahu commented Jan 27, 2015

Hi,

I am trying to make a layout with top and bottom view and FAB on border of both of the layouts.
This FAB converts dp to pixel for setting margin i want to add a FAB between layouts border and it always go slightly up side can you please assist me on that that would be very helpful.

@shahbaz-momi
Copy link

shahbaz-momi commented Jan 28, 2015

Hey, thanks for this, I improved this a little bit by adding a character, settings it's size and color, and also xml support and programmatically adding it to a custom layout. Code:

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;

public class FloatingActionButton extends View {

final static OvershootInterpolator overshootInterpolator = new OvershootInterpolator();
final static AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();

Context context;
Paint mButtonPaint;
Paint mDrawablePaint;
Bitmap mBitmap;
boolean mHidden = false;

private FloatingActionButton(Context context) {
    super(context);
    this.context = context;
    init(Color.WHITE);
}

public FloatingActionButton(Context context, AttributeSet attrs){
    super(context, attrs);
    this.context = context;

    TypedArray a = context.getTheme().obtainStyledAttributes(
            attrs,
            R.styleable.FloatingActionButton,
            0, 0);

    try {
        int color = a.getColor(R.styleable.FloatingActionButton_withColor, Color.WHITE);
        Drawable icon = a.getDrawable(R.styleable.FloatingActionButton_withDrawable);
        char c = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "text").charAt(0);
        float size = a.getDimension(R.styleable.FloatingActionButton_textSize, 24f);
        int tColor = a.getColor(R.styleable.FloatingActionButton_textColor, Color.WHITE);
        init(color);
        if(icon != null)
            setFloatingActionButtonDrawable(icon);
        setCharacterSize(size);
        setCharacterColor(tColor);
        setCharacter(c);
    }finally {
        a.recycle();
    }
}

public void setFloatingActionButtonColor(int FloatingActionButtonColor) {
    init(FloatingActionButtonColor);
}

public void setFloatingActionButtonDrawable(Drawable FloatingActionButtonDrawable) {
    mBitmap = ((BitmapDrawable) FloatingActionButtonDrawable).getBitmap();
    invalidate();
}

private char text= ' ';
private float textY;
public void setCharacter(char c){
    this.text = c;
    Rect r = new Rect();
    mDrawablePaint.getTextBounds(c + "", 0, 1, r);
    int h = r.height();
    textY = (getHeight() + h * 2);
    Log.d("ENV", "TEXTY: " + textY);
    invalidate();
}

public void setCharacterColor(int color){
    mDrawablePaint.setColor(color);
    invalidate();
}

public void setCharacterSize(float size){
    mDrawablePaint.setTextSize(size);
    setCharacter(text);
    invalidate();
}

public void init(int FloatingActionButtonColor) {
    setWillNotDraw(false);
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);

    mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mButtonPaint.setColor(FloatingActionButtonColor);
    mButtonPaint.setStyle(Paint.Style.FILL);
    mButtonPaint.setShadowLayer(10.0f, 0.0f, 3.5f, Color.argb(100, 0, 0, 0));
    mDrawablePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mDrawablePaint.setTextAlign(Paint.Align.CENTER);
    mDrawablePaint.setLinearText(true);

    invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
    setClickable(true);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getWidth() / 2.6), mButtonPaint);
    if(mBitmap != null)
        canvas.drawBitmap(mBitmap, (getWidth() - mBitmap.getWidth()) / 2, (getHeight() - mBitmap.getHeight()) / 2, mDrawablePaint);
    canvas.drawText(text + "", getWidth() / 2, textY, mDrawablePaint);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP) {
        setAlpha(1.0f);
    } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
        setAlpha(0.6f);
    }
    return super.onTouchEvent(event);
}

public void hideFloatingActionButton() {
    if (!mHidden) {
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 1, 0);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 1, 0);
        AnimatorSet animSetXY = new AnimatorSet();
        animSetXY.playTogether(scaleX, scaleY);
        animSetXY.setInterpolator(accelerateInterpolator);
        animSetXY.setDuration(100);
        animSetXY.start();
        mHidden = true;
    }
}

public void showFloatingActionButton() {
    if (mHidden) {
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 0, 1);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 0, 1);
        AnimatorSet animSetXY = new AnimatorSet();
        animSetXY.playTogether(scaleX, scaleY);
        animSetXY.setInterpolator(overshootInterpolator);
        animSetXY.setDuration(200);
        animSetXY.start();
        mHidden = false;
    }
}

public boolean isHidden() {
    return mHidden;
}

static public class Builder {
    private FrameLayout.LayoutParams params;
    private final Activity activity;
    int gravity = Gravity.BOTTOM | Gravity.RIGHT; // default bottom right
    Drawable drawable;
    int color = Color.WHITE;
    int size = 0;
    float scale = 0;

    public Builder(Activity context) {
        scale = context.getResources().getDisplayMetrics().density;
        size = convertToPixels(72, scale); // default size is 72dp by 72dp
        params = new FrameLayout.LayoutParams(size, size);
        params.gravity = gravity;

        this.activity = context;
    }

    /**
     * Sets the gravity for the FAB
     */
    public Builder withGravity(int gravity) {
        this.gravity = gravity;
        return this;
    }

    /**
     * Sets the margins for the FAB in dp
     */
    public Builder withMargins(int left, int top, int right, int bottom) {
        params.setMargins(
                convertToPixels(left, scale),
                convertToPixels(top, scale),
                convertToPixels(right, scale),
                convertToPixels(bottom, scale));
        return this;
    }

    /**
     * Sets the FAB drawable
     */
    public Builder withDrawable(final Drawable drawable) {
        this.drawable = drawable;
        return this;
    }

    /**
     * Sets the FAB color
     */
    public Builder withButtonColor(final int color) {
        this.color = color;
        return this;
    }

    /**
     * Sets the FAB size in dp
     */
    public Builder withButtonSize(int size) {
        size = convertToPixels(size, scale);
        params = new FrameLayout.LayoutParams(size, size);
        return this;
    }

    private char c = ' ';
    public Builder withChar(char c){
        this.c = c;
        return this;
    }

    private float charS = 32f;
    public Builder withCharSize(float size){
        charS = size * scale;
        return this;
    }

    private int charColor = Color.BLACK;
    public Builder withCharColor(int color){
        charColor = color;
        return this;
    }

    public FloatingActionButton create(int layoutId) {
        final FloatingActionButton button = new FloatingActionButton(activity);
        button.setFloatingActionButtonColor(this.color);
        button.setFloatingActionButtonDrawable(this.drawable);
        button.setCharacter(c);
        button.setCharacterColor(charColor);
        button.setCharacterSize(charS);
        params.gravity = this.gravity;
        ViewGroup root = (ViewGroup) activity.findViewById(layoutId);
        root.addView(button, params);
        return button;
    }

    // The calculation (value * scale + 0.5f) is a widely used to convert to dps to pixel units
    // based on density scale
    // see developer.android.com (Supporting Multiple Screen Sizes)
    private int convertToPixels(int dp, float scale){
        return (int) (dp * scale + 0.5f) ;
    }
}

}

And in attrs.xml:

http://pastebin.com/TP5JuZQk

Enjoy!

@SilverFoxA
Copy link

SilverFoxA commented Mar 2, 2015

how to hide the button when we don't need it? i can't call it from other activity

@docprajit
Copy link

docprajit commented Mar 8, 2015

Upon adding a FAB using your technique I see a thin translucent ring around the actual image. I tried removing the margins and shadowlayer but it didn't work. Could you suggest how this can be removed?

@fernetmatt
Copy link

fernetmatt commented Mar 17, 2015

how can i set an OnClickListener?

@18ulhaspatil
Copy link

18ulhaspatil commented Mar 24, 2015

Hey i import these code in my sample project but it didn't work on Pre-Lollipop device, Please tell me how i able to see these button on devices below Lollipop...?
Please reply me ASAP

@Jogan
Copy link
Author

Jogan commented Mar 25, 2015

@MatBenetti the same way you would set an OnClickListener on any other view. fabButton.setOnClickListener

@rkmobileinc
Copy link

rkmobileinc commented Apr 19, 2015

I got the button working. How do you add functionalities like adding things that will appear once clicked?

@bkr32
Copy link

bkr32 commented Apr 20, 2015

fabButton.setOnClickListener(new View.OnClickListener() {
@OverRide
public void onClick(View v) {

            Intent i = new Intent(MainActivity.this, tabs.class);
            startActivity(i);

        }
    });

@bkr32
Copy link

bkr32 commented Apr 20, 2015

i'mm having a problem with FAB overlapping my toolbar and its items

@maxotov
Copy link

maxotov commented May 4, 2015

Hi! How i can add this fab button in layout/activity_simple.xml file?

@hamidsn
Copy link

hamidsn commented May 12, 2015

Very nice and easy to use especially in Eclipse.

@PraveenKHegde
Copy link

PraveenKHegde commented May 13, 2015

Where is the location to put this code.. please help m just a novice...

@AplicanaApps
Copy link

AplicanaApps commented Sep 15, 2015

@shahbaz-man how I can change the color dynamically

@faisal1200
Copy link

faisal1200 commented Oct 24, 2015

This code is awsom , simple and helping.. thanks @Jogan ,
Just need how to add icon , my fab is simple white .

@vijaicv
Copy link

vijaicv commented Feb 14, 2016

builder cannot be resolved

@olaolumusic
Copy link

olaolumusic commented Mar 15, 2016

@Jogan, you are the boss!!! thanks so much, so easy to use and light weight. @faisal find below a code snip that haddles ur request.

FloatingActionButton read = new FloatingActionButton.Builder(this)
            .withDrawable(getResources().getDrawable(R.drawable.ic_read))
            .withButtonColor(Color.YELLOW)
            .withGravity(Gravity.BOTTOM | Gravity.RIGHT)
            .withMargins(0, 0, 16, 145).create();

image

@Jogan
Copy link
Author

Jogan commented Mar 16, 2016

USE THE DESIGN SUPPORT LIBRARY - NOT THIS ANYMORE

@anjali0012
Copy link

anjali0012 commented May 9, 2016

Too good and simple code thanks a lot..

@rijotech
Copy link

rijotech commented Jun 28, 2016

Nice code, question how can I bring up the menu.xml on onClick?

@osamaalboss
Copy link

osamaalboss commented Aug 13, 2016

how to start action button when i click

@manojcbs
Copy link

manojcbs commented Aug 16, 2016

awsome bosss !!!

@manojcbs
Copy link

manojcbs commented Aug 16, 2016

how to add another button after clicking on this button
i need to add more button.

@kaksha1212
Copy link

kaksha1212 commented Oct 19, 2016

Thank you so much for this code.. @Jogan

Copy link

ghost commented May 13, 2017

Your code is very much helpful for us. Thanks 👍

@dh4nil0ver5
Copy link

dh4nil0ver5 commented Mar 17, 2019

thank you from mw. it's very helpful cause i need to create app project mobile offline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment