Android Design Library - Floating Action Button Padding / Randprobleme

109

Ich verwende den neuen FloatingActionButton aus der Google Design Library und habe einige seltsame Probleme mit dem Auffüllen / Rand. Dieses Bild (mit aktivierten Entwicklerlayoutoptionen) stammt von API 22.

Geben Sie hier die Bildbeschreibung ein

Und von API 17.

Geben Sie hier die Bildbeschreibung ein

Dies ist das 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"/>

Warum ist das FAB in API 17 in Bezug auf Polsterung / Rand so viel größer?

hitch.united
quelle
1
Ich würde empfehlen CoordinatorLayout, es vertikal auszurichten und den zusätzlichen Polster vor dem Lutscher zu betrachten. Sie könnten es aus den dekompilierten FAB-Quellen herausfinden, aber ich würde lieber warten, bis Google es so repariert, wie sie es getan haben CardView.
DariusL

Antworten:

129

Update (Oktober 2016):

Die richtige Lösung besteht nun darin, sie app:useCompatPadding="true"in Ihren FloatingActionButton einzufügen. Dadurch wird das Auffüllen zwischen verschiedenen API-Versionen konsistent. Dies scheint jedoch die Standardränder immer noch ein wenig zu verringern, sodass Sie diese möglicherweise anpassen müssen. Zumindest sind API-spezifische Stile nicht mehr erforderlich.

Vorherige Antwort:

Sie können dies einfach mit API-spezifischen Stilen erreichen. Geben Sie in Ihrem normalen values/styles.xmlZustand Folgendes ein:

<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>

Verwenden Sie dann unter values-v21 / styles.xml Folgendes:

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

und wenden Sie den Stil auf Ihren FloatingActionButton an:

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

Wie andere angemerkt haben, rendert die Schaltfläche in API <20 einen eigenen Schatten, der zur logischen Gesamtbreite der Ansicht beiträgt, während in API> = 20 die neuen Höhenparameter verwendet werden, die nicht zur Ansichtsbreite beitragen.

Dmitry Brant
quelle
19
Buggy Android .. :(
Abdullah Shoaib
3
sieht lächerlich hässlich aus, aber es scheint, dass es keine andere Lösung gibt
elf
3
Gute Antwort, danke. Darf ich vorschlagen, es zu bearbeiten und einen Stil auch für Tablets hinzuzufügen? Für API> = 20 beträgt der Spielraum 24 dp; Für API <20 habe ich festgestellt, dass Sie <item name = "android: layout_marginRight"> 12dp </ item> <item name = "android: layout_marginBottom"> 6dp </ item> benötigen . Ich musste sie selbst berechnen, weil ich sie für meine App brauchte. Sie können auch die Dimens- Ressource anstelle des Stils verwenden.
JJ86
Die Randnummern unterscheiden sich je nachdem, was elevationSie auf dem FAB eingestellt haben.
Jarett Millard
Verwendungapp:useCompatPadding="true"
Kishan Vaghela
51

Kein Fummeln mehr mit styles.xmloder mit .javaDateien. Lassen Sie es mich einfach machen.

Sie können app:useCompatPadding="true"benutzerdefinierte Ränder verwenden und entfernen, um dieselben Ränder für verschiedene Android-Versionen beizubehalten

Der zusätzliche Rand / die zusätzliche Polsterung, die Sie in Ihrem zweiten Bild auf dem FAB gesehen haben, ist auf diese kompatible Polsterung auf Pre-Lollipop- Geräten zurückzuführen. Wenn diese Eigenschaft nicht festgelegt ist, wird sie auf Pre-Lollopop- Geräte und NICHT auf Lollipop + -Geräte angewendet .

Android Studio Code

Konzeptioneller Beweiß

Designansicht

Saravanabalagi Ramachandran
quelle
Wenn das übergeordnete Layout die Kartenansicht ist, müssen wir "card_view: useCompatPadding =" true "für fab.
Amit Tumkur
Dies funktioniert bei mir nach dem Upgrade der Support-Bibliothek auf Version 23.2. Vielen Dank!
Pablo
Ich sehe Ränder in Ihrem PoC.
Pedro Oliveira
@PedroOliveira Ja, Sie werden in allen Android-Versionen den gleichen Spielraum sehen, was in der Tat das Ziel ist.
Saravanabalagi Ramachandran
1
Sie sagen, dass er "xxx" verwenden kann, um benutzerdefinierte Ränder zu entfernen, und dennoch zeigt Ihr Proof of Concept alle Ränder, wie das OP gefragt hat, warum.
Pedro Oliveira
31

Nach ein paar Zeit Suche und Testlösung behebe ich mein Problem mit dem Hinzufügen dieser Zeile nur zu meinem XML-Layout:

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

und das ist mein ganzes 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" />
Mostafa Rostami
quelle
2
Großartig, hat für mich gearbeitet. Weder useCompatPaddingnoch irgendetwas anderes erzielt das gleiche Ergebnis.
Bgplaya
1
Ich habe diese Antwort mit Vikrams
Ingkevin
22

Es gibt ein Problem in der Design Support Library. Verwenden Sie die folgende Methode, um dieses Problem zu beheben, bis die Bibliothek aktualisiert wird. Versuchen Sie, diesen Code zu Ihrer Aktivität oder Ihrem Fragment hinzuzufügen, um das Problem zu beheben. Behalte deine XML gleich. Auf Lutschern und darüber gibt es keinen Rand, aber darunter gibt es einen Rand von 16dp.

Arbeitsbeispiel aktualisieren

XML - FAB befindet sich in einem 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);
}

Konvertiere dp in 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);
}

Lutscher

Geben Sie hier die Bildbeschreibung ein

Pre Lollipop

Geben Sie hier die Bildbeschreibung ein

Eugene H.
quelle
Dies sollte die richtige Antwort sein. Das Problem wird jedoch nicht zu 100% behoben, da die Polsterung ebenfalls nicht quadratisch ist.
Johnny Lambada
@ JohnnyLambada Ich glaube nicht. RelativeLayout.LayoutParamskann nicht auf das LayoutParamsvon geworfen werden FloatingActionButtonund ich sehe keine Ränder von 16dp auf vor Lollipop. Die Breite von FloatingActionButtonPre Lollipop beträgt 84 dp anstelle von 56 dp und die Höhe 98 dp.
Markus Rubey
@ MarkusRubey Ich werde meine Antwort mit einem funktionierenden Beispiel und Bildern aktualisieren, die die Pre-Lollipop- und Lollipop-Version anzeigen.
Eugene H
1
@MarkusRubey - View.getLayoutParamsgibt ein LayoutParamsvom Typ des Viewis in zurück - in Eugenes Fall ist es ein RelativeLayout.LayoutParams.
Johnny Lambada
1
@EugeneH Ich habe RelativeLayout.LayoutParams in ViewGroup.MarginLayoutParams geändert. Dadurch funktioniert der Code in vielen weiteren Fällen und das von MarkusRubey angesprochene Problem wird behoben.
Johnny Lambada
8

Vor Lollipop FloatingActionButtonist er dafür verantwortlich, seinen eigenen Schatten zu zeichnen. Daher muss die Ansicht etwas größer sein, um Platz für den Schatten zu schaffen. Um ein konsistentes Verhalten zu erzielen, können Sie Ränder festlegen, um den Unterschied in Höhe und Breite zu berücksichtigen. Ich benutze derzeit die folgende Klasse :

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 umbenannt in fab_size dimension in:

import static android.support.design.R.dimen.design_fab_size_normal;
import static android.support.design.R.dimen.design_fab_size_mini;
Markus Rubey
quelle
Ich habe gerade mein Projekt auf AndroidX umgestaltet und kann den 3. Konstruktor aus Ihrem Beispiel nicht erstellen. Wie kann ich das mit AndroidX-Bibliotheken machen?
Alon Shlider
5

Markus 'Antwort funktionierte gut für mich, nachdem ich auf Version 23.1.0 aktualisiert und einige Anpassungen an den Importen vorgenommen hatte (mit dem aktuellen Gradle-Plugin verwenden wir unsere App R anstelle des R der Designbibliothek). Hier ist der Code für v23.1.0:

// Based on this answer: https://stackoverflow.com/a/30845164/1317564
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);
    }

    @SuppressLint("PrivateResource")
    public CustomFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.FloatingActionButton, defStyleAttr,
                R.style.Widget_Design_FloatingActionButton);
        mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, 0);
        a.recycle();
    }

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

    @SuppressLint("PrivateResource")
    private int getSizeDimension() {
        switch (mSize) {
            case 1:
                return getResources().getDimensionPixelSize(R.dimen.design_fab_size_mini);
            case 0:
            default:
                return getResources().getDimensionPixelSize(R.dimen.design_fab_size_normal);
        }
    }
}
Lernen Sie OpenGL ES
quelle
Ich kann dieses Beispiel nach dem Refactoring auf AndroidX nicht mehr verwenden - der 3. Konstruktor wird nicht mehr kompiliert. Weißt du was los ist?
Alon Shlider
3

Setzen Sie in der Layoutdatei die Attributhöhe auf 0, da die Standardhöhe verwendet wird.

app:elevation="0dp"

Überprüfen Sie jetzt in der Aktivität die API-Stufe größer als 21 und stellen Sie bei Bedarf die Höhe ein.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    fabBtn.setElevation(10.0f);
}
Vikram
quelle
Wenn Sie die kompatible Bibliothek verwenden, verwenden Sie setCompatElevation . Und es ist besser, Dips anstelle von Pixeln direkt zu verwenden.
Ingkevin