Skip to content

Instantly share code, notes, and snippets.

@chanakin
Last active January 12, 2018 12:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chanakin/5bbc0d357ac81718f90a to your computer and use it in GitHub Desktop.
Save chanakin/5bbc0d357ac81718f90a to your computer and use it in GitHub Desktop.
Android Material Floating Action Button (Supports API 14+ with shadows and 21+ with Lollipop elevation)
package com.bigoven.android.widgets;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageButton;
import com.bigoven.android.R;
import com.bigoven.android.utilities.UiHelper;
@SuppressLint("ClickableViewAccessibility")
/**
* This class is a custom view that encompasses the functionality of Google's Material Design "Floating Action Button"
* It supports API 14+ devices.
*
* Copyright (C) 2015 Chantell Osejo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
public class FloatingActionButton extends ImageButton {
public static final int GOOGLE_DESIGN_GUIDELINE_RECOMMENDED_DIAMETER_PX = UiHelper.convertDpToPixel(56);
public static final int GOOGLE_DESIGN_GUIDELINE_RECOMMENDED_MINI_DIAMETER_PX = UiHelper.convertDpToPixel(40);
final static AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
private Paint mBackgroundPaint;
private int mColor = Color.WHITE;
private int visibility = View.VISIBLE;
private ShapeDrawable mCircleBackground;
public FloatingActionButton(Context context) {
super(context);
init();
}
public FloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.FloatingActionButton,
0, 0);
try {
mColor = a.getColor(R.styleable.FloatingActionButton_buttonColor, Color.WHITE);
} finally {
a.recycle();
}
init();
}
private void init() {
setColor(mColor);
setScaleType(ScaleType.CENTER_INSIDE);
visibility = getVisibility();
}
public void setColor(int color) {
this.mColor = color;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
setLayerType(LAYER_TYPE_SOFTWARE, mBackgroundPaint);
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundPaint.setColor(mColor);
mBackgroundPaint.setShadowLayer(10.0f, 0.0f, 3.5f, Color.argb(100, 0, 0, 0));
setBackgroundDrawable(null);
} else {
if (null == mCircleBackground) {
mCircleBackground = new ShapeDrawable(new OvalShape());
setBackground(mCircleBackground);
setElevation(UiHelper.convertDpToPixel(8));
}
mCircleBackground.getPaint().setColor(color);
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getWidth() / 2.6), mBackgroundPaint);
}
super.onDraw(canvas);
}
@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 setVisibilityAfterDelay(final int visibility, long delay) {
if (this.visibility == visibility) {
return;
}
this.visibility = visibility;
boolean hide = this.visibility != View.VISIBLE;
ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, SCALE_X, hide ? 1 : 0, hide ? 0 : 1);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, SCALE_Y, hide ? 1 : 0, hide ? 0 : 1);
ObjectAnimator alpha = ObjectAnimator.ofFloat(this, ALPHA, hide ? 1 : 0, hide ? 0 : 1);
ObjectAnimator rotate = ObjectAnimator.ofFloat(this, ROTATION, 0f, 360f);
final AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(scaleX, scaleY, rotate, alpha);
animSetXY.setInterpolator(accelerateInterpolator);
animSetXY.setDuration(hide ? 200 : 300);
animSetXY.setStartDelay(delay);
animSetXY.start();
}
@Override
/**
* Hijacking the visibility parameter to animate hiding/showing the button
*/
public void setVisibility(final int visibility) {
setVisibilityAfterDelay(visibility, 0);
}
static public class Builder {
private final Context context;
int color = Color.WHITE;
private Drawable drawable;
/**
* Builder of the fab button
*
* @param context should be an Activity context, used to create the view
*/
public Builder(Context context) {
this.context = context;
}
/**
* Sets the FAB drawable
*/
public Builder setDrawable(final Drawable drawable) {
this.drawable = drawable;
return this;
}
/**
* Sets the FAB color
*/
public Builder setColor(final int color) {
this.color = color;
return this;
}
public FloatingActionButton create() {
final FloatingActionButton button = new FloatingActionButton(context);
button.setColor(this.color);
button.setImageDrawable(this.drawable);
return button;
}
}
}
@chanakin
Copy link
Author

    <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent">

          <FrameLayout
              android:id="@+id/container"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />

          <com.bigoven.android.widgets.FloatingActionButton
              android:id="@+id/test_fab"
              android:layout_alignParentBottom="true"
              android:layout_alignParentRight="true"
              android:layout_alignParentEnd="true"
              android:layout_width="56dp"
              android:layout_height="56dp"
              android:src="@drawable/ic_add_white_24dp"
              app:buttonColor="@color/big_oven_red_accent"
              android:scaleType="centerInside"
              android:layout_marginRight="16dp"
              android:layout_marginEnd="16dp"
              android:layout_marginBottom="16dp"/>
     </RelativeLayout>

@chanakin
Copy link
Author

Also, don't forget...in your attrs.xml :

<declare-styleable name="FloatingActionButton">
    <attr name="buttonColor" format="color"/>
</declare-styleable>

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