Android Design Library - Floating Action Button Padding/Margin Issues

56,883

Solution 1

Update (Oct 2016):

The correct solution now is to put app:useCompatPadding="true" into your FloatingActionButton. This will make the padding consistent between different API versions. However, this still seems to make the default margins off by a little bit, so you may need to adjust those. But at least there's no further need for API-specific styles.

Previous answer:

You can accomplish this easily using API-specific styles. In your normal values/styles.xml, put something like this:

<style name="floating_action_button">
    <item name="android:layout_marginLeft">0dp</item>
    <item name="android:layout_marginTop">0dp</item>
    <item name="android:layout_marginRight">8dp</item>
    <item name="android:layout_marginBottom">0dp</item>
</style>

and then under values-v21/styles.xml, use this:

<style name="floating_action_button">
    <item name="android:layout_margin">16dp</item>
</style>

and apply the style to your FloatingActionButton:

<android.support.design.widget.FloatingActionButton
...
style="@style/floating_action_button"
...
/>

As others have noted, in API <20, the button renders its own shadow, which adds to the overall logical width of the view, whereas in API >=20 it uses the new Elevation parameters which don't contribute to the view width.

Solution 2

No more fiddling with styles.xml or with .java files. Let me make it simple.

You can use app:useCompatPadding="true" and remove custom margins to maintain same margins across different versions of android

The extra margin/padding you saw on the FAB in your second picture is due to this compatPadding on pre-lollipop devices. If this property is not set, it gets applied on pre-lollopop devices and NOT in lollipop+ devices.

android studio code

Proof of concept

design view

Solution 3

after a few time searching and test solution i fix my problem with add this line to my xml layout only:

app:elevation="0dp"
app:pressedTranslationZ="0dp"

and this is my whole float button layout

<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:src="@drawable/ic_add"
        app:elevation="0dp"
        app:pressedTranslationZ="0dp"
        app:fabSize="normal" />

Solution 4

There is an issue within the Design Support Library. Use the method below to fix this issue until the library is updated. Try adding this code to your activity or fragment to solve the issue. Keep your xml the same. On lollipop and above there is no margin but below there is a margin of 16dp.

Update Working Example

XML - FAB is within a RelativeLayout

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:layout_marginBottom="16dp"
    android:layout_marginRight="16dp"
    android:src="@mipmap/ic_add"
    app:backgroundTint="@color/accent"
    app:borderWidth="0dp"
    app:elevation="4sp"/>

Java

FloatingActionButton mFab = (FloatingActionButton) v.findViewById(R.id.fab);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
    ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
    p.setMargins(0, 0, dpToPx(getActivity(), 8), 0); // get rid of margins since shadow area is now the margin
    mFab.setLayoutParams(p);
}

Convert dp to to px

public static int dpToPx(Context context, float dp) {
    // Reference http://stackoverflow.com/questions/8309354/formula-px-to-dp-dp-to-px-android
    float scale = context.getResources().getDisplayMetrics().density;
    return (int) ((dp * scale) + 0.5f);
}

Lollipop

enter image description here

Pre Lollipop

enter image description here

Solution 5

On pre Lollipop FloatingActionButton is responsible for drawing its own shadow. Therefore the view has to be slightly bigger to make space for the shadow. To get a consistent behavior you can set margins to account for the difference in height and width. I am currently using the following class:

import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.ViewGroup.MarginLayoutParams;

import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;

import static android.support.design.R.styleable.FloatingActionButton;
import static android.support.design.R.styleable.FloatingActionButton_fabSize;
import static android.support.design.R.style.Widget_Design_FloatingActionButton;
import static android.support.design.R.dimen.fab_size_normal;
import static android.support.design.R.dimen.fab_size_mini;

public class CustomFloatingActionButton extends FloatingActionButton {

    private int mSize;

    public CustomFloatingActionButton(Context context) {
        this(context, null);
    }

    public CustomFloatingActionButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, FloatingActionButton, defStyleAttr,
                Widget_Design_FloatingActionButton);
        this.mSize = a.getInt(FloatingActionButton_fabSize, 0);
        a.recycle();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (SDK_INT < LOLLIPOP) {
            int size = this.getSizeDimension();
            int offsetVertical = (h - size) / 2;
            int offsetHorizontal = (w - size) / 2;
            MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
            params.leftMargin = params.leftMargin - offsetHorizontal;
            params.rightMargin = params.rightMargin - offsetHorizontal;
            params.topMargin = params.topMargin - offsetVertical;
            params.bottomMargin = params.bottomMargin - offsetVertical;
            setLayoutParams(params);
        }
    }

    private final int getSizeDimension() {
        switch (this.mSize) {
            case 0: default: return this.getResources().getDimensionPixelSize(fab_size_normal);
            case 1: return this.getResources().getDimensionPixelSize(fab_size_mini);
        }
    }
}

Update: Android Support Libraries v23 renamed fab_size dimens to:

import static android.support.design.R.dimen.design_fab_size_normal;
import static android.support.design.R.dimen.design_fab_size_mini;
Share:
56,883

Related videos on Youtube

Nick H
Author by

Nick H

KC3SPW

Updated on February 08, 2020

Comments

  • Nick H
    Nick H over 4 years

    I'm using the new FloatingActionButton from the Google Design Library and I am getting some strange padding/margin problems. This image (with developer layout options on) is from API 22.

    enter image description here

    And from API 17.

    enter image description here

    This is the XML

    <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_gravity="bottom|right"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="-32dp"
            android:src="@drawable/ic_action_add"
            app:fabSize="normal"
            app:elevation="4dp"
            app:borderWidth="0dp"
            android:layout_below="@+id/header"/>
    

    Why is the FAB in API 17 so much larger padding/margin wise?

    • DariusL
      DariusL almost 9 years
      I'd recommend using CoordinatorLayout to align it vertically and eyeball the extra padding pre-lollipop. You could figure it out from the decompiled FAB sources, but I'd rather wait for Google to fix it like they did for CardView.
  • JohnnyLambada
    JohnnyLambada almost 9 years
    This should be the correct answer. However, it doesn't fix the problem 100% as the padding is also unsquare.
  • Markus Rubey
    Markus Rubey almost 9 years
    @JohnnyLambada I don't think so. RelativeLayout.LayoutParams cannot be cast to the LayoutParams of FloatingActionButton and i don't see any margins of 16dp on pre Lollipop. Width of FloatingActionButton pre Lollipop is 84dp instead of 56dp and it's height is 98dp.
  • Eugene H
    Eugene H almost 9 years
    @MarkusRubey I will update my answer with a working example and images displaying the pre lollipop and lollipop version.
  • Markus Rubey
    Markus Rubey almost 9 years
    @EugeneH I see. I assumed he is using a CoordinatorLayout. But still, the margin part is not correct.
  • Eugene H
    Eugene H almost 9 years
    @MarkusRubey I am actually not using CoordinatorLayout. I will update my answer with a working example with the margin corrected.
  • Eugene H
    Eugene H almost 9 years
    @MarkusRubey Just updated the answer with a working example of the correct margins.
  • JohnnyLambada
    JohnnyLambada almost 9 years
    @MarkusRubey - View.getLayoutParams returns a LayoutParams of the type of the View is in -- in Eugene's case it's a RelativeLayout.LayoutParams.
  • JohnnyLambada
    JohnnyLambada almost 9 years
    @EugeneH FYI, to get it perfectly aligned with another view, I had to move it further south by 4dips. I used mFab.setTranslationY(dpToPx(getActivity(), 4)); to accomplish this.
  • JohnnyLambada
    JohnnyLambada almost 9 years
    @EugeneH I changed RelativeLayout.LayoutParams to ViewGroup.MarginLayoutParams. This will make the code work in many more cases and fixes the problem that MarkusRubey brings up.
  • eleven
    eleven over 8 years
    looks ridiculously ugly, but it seems there is no other solution
  • JJ86
    JJ86 over 8 years
    Good answer, thank you. May i suggest to edit it and add a style also for tablet? For API >=20, the margin is 24dp; for API<20 i noticed that the you need <item name="android:layout_marginRight">12dp</item> <item name="android:layout_marginBottom">6dp</item> . I had to calculate them by myself, because i needed it for my app. You can also use dimens resource instead of style.
  • Jarett Millard
    Jarett Millard about 8 years
    The margin numbers will be different depending on the elevation you set on the FAB.
  • Amit Tumkur
    Amit Tumkur about 8 years
    if Parent layout is card view then we need to use "card_view:useCompatPadding="true" for fab.
  • Pablo
    Pablo almost 8 years
    This works for me after upgrading support library to version 23.2. Thanks!
  • Pedro Oliveira
    Pedro Oliveira almost 8 years
    I see margins in your PoC.
  • Saravanabalagi Ramachandran
    Saravanabalagi Ramachandran almost 8 years
    @PedroOliveira Yes, you shall see the same margin across all versions of Android, which in fact is the objective.
  • Pedro Oliveira
    Pedro Oliveira almost 8 years
    You're saying that he can use "xxx" to remove custom margins, and yet your proof of concept shows all margins, like the OP asked why.
  • Saravanabalagi Ramachandran
    Saravanabalagi Ramachandran almost 8 years
    Let me make it clear, userCompatPadding=true does NOT remove custom margins. You need to REMOVE custom margins and add the property and set it to true so that the result would look like above across different versions of android
  • Saravanabalagi Ramachandran
    Saravanabalagi Ramachandran almost 8 years
    btw the objective is NOT to add or remove margins, but to make the fab have SAME margin across different versions
  • Kishan Vaghela
    Kishan Vaghela over 7 years
    use app:useCompatPadding="true"
  • Kishan Vaghela
    Kishan Vaghela over 7 years
    Just use - app:useCompatPadding="true"
  • whoKnows
    whoKnows over 7 years
    This is fab:useCompatPadding now.
  • k2col
    k2col over 7 years
    If you're going to create a style for a FAB then you should also set the parent of that style accordingly, ie <style name="floating_action_button" parent="Widget.Design.FloatingActionButton">
  • Björn Kechel
    Björn Kechel almost 7 years
    app:useCompatPadding does not work well with Api 21: on long press it creates a rectangular shadow, see stackoverflow.com/questions/44199012/…
  • bgplaya
    bgplaya almost 7 years
    Great, worked for me. Neither useCompatPadding nor anything else gets the same result.
  • ingkevin
    ingkevin almost 6 years
    If you're using the compat library use setCompatElevation. And it will be better to use dips instead of pixels directly.
  • ingkevin
    ingkevin almost 6 years
    I've combine this answer with vikram's
  • saltandpepper
    saltandpepper over 4 years
    @whoKnows I can't find any documentation that says that we should now use fab:useCompatPadding.
  • Alon Shlider
    Alon Shlider over 4 years
    I just refactored my project to AndroidX and I am unable to create the 3rd constructor from your example. How can I do it using AndroidX libraries?
  • Alon Shlider
    Alon Shlider over 4 years
    I can't seem to use this example after refactoring to AndroidX - the 3rd constructor does not compile anymore. Do you know what is wrong?