Libreria di progettazione Android - Problemi di riempimento / margine del pulsante di azione mobile


109

Sto usando il nuovo FloatingActionButton dalla Google Design Library e sto riscontrando alcuni strani problemi di riempimento / margine. Questa immagine (con le opzioni di layout sviluppatore attive) proviene dall'API 22.

inserisci qui la descrizione dell'immagine

E dall'API 17.

inserisci qui la descrizione dell'immagine

Questo è l'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"/>

Perché il FAB nell'API 17 è molto più ampio in termini di riempimento / margine?


1
Consiglierei di usarlo CoordinatorLayoutper allinearlo verticalmente e guardare l'imbottitura extra pre-lecca-lecca. Potresti capirlo dalle fonti FAB decompilate, ma preferisco aspettare che Google lo risolva come hanno fatto per CardView.
DariusL

Risposte:


129

Aggiornamento (ottobre 2016):

La soluzione corretta ora è inserire app:useCompatPadding="true"nel tuo FloatingActionButton. Ciò renderà il riempimento coerente tra le diverse versioni dell'API. Tuttavia, questo sembra ancora ridurre leggermente i margini predefiniti, quindi potrebbe essere necessario regolarli. Ma almeno non c'è più bisogno di stili specifici per API.

Risposta precedente:

Puoi farlo facilmente utilizzando stili specifici dell'API. Nella tua normalità values/styles.xml, metti qualcosa del genere:

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

e poi in values-v21 / styles.xml, usa questo:

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

e applica lo stile al tuo FloatingActionButton:

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

Come altri hanno notato, in API <20, il pulsante rende la propria ombra, che si aggiunge alla larghezza logica complessiva della vista, mentre in API> = 20 utilizza i nuovi parametri Elevation che non contribuiscono alla larghezza della vista.


19
buggy Android .. :(
Abdullah Shoaib

3
sembra ridicolmente brutto, ma sembra che non ci sia altra soluzione
undici

3
Buona risposta, grazie. Posso suggerire di modificarlo e aggiungere uno stile anche per tablet? Per API> = 20, il margine è 24dp; per API <20 ho notato che hai bisogno di <item name = "android: layout_marginRight"> 12dp </item> <item name = "android: layout_marginBottom"> 6dp </item> . Ho dovuto calcolarli da solo, perché ne avevo bisogno per la mia app. Puoi anche usare la risorsa Dimens invece dello stile.
JJ86

I numeri di margine saranno diversi a seconda dell'impostazione elevationsul FAB.
Jarett Millard

utilizzareapp:useCompatPadding="true"
Kishan Vaghela

51

Non più giocherellare con styles.xmlo con i .javafile. Lascia che sia semplice.

Puoi utilizzare app:useCompatPadding="true"e rimuovere i margini personalizzati per mantenere gli stessi margini su diverse versioni di Android

Il margine / imbottitura extra che hai visto sul FAB nella tua seconda immagine è dovuto a questo compatPadding sui dispositivi pre-lollipop . Se questa proprietà non è impostata, viene applicata sui dispositivi pre-lollopop e NON nei dispositivi lollipop +.

codice di studio Android

Verifica teorica

vista di progettazione


se il layout principale è la visualizzazione a schede, allora dobbiamo usare "card_view: useCompatPadding =" true "per fab.
Amit Tumkur

Questo funziona per me dopo aver aggiornato la libreria di supporto alla versione 23.2. Grazie!
Pablo

Vedo margini nel tuo PoC.
Pedro Oliveira

@PedroOliveira Sì, vedrai lo stesso margine su tutte le versioni di Android, che in effetti è l'obiettivo.
Saravanabalagi Ramachandran

1
Stai dicendo che può usare "xxx" per rimuovere i margini personalizzati, e tuttavia la tua prova di concetto mostra tutti i margini, come l'OP ha chiesto perché.
Pedro Oliveira

31

dopo un po 'di tempo cercando e testando la soluzione risolvo il mio problema aggiungendo questa riga solo al mio layout xml:

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

e questo è il mio intero layout di pulsanti flottanti

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

2
Fantastico, ha funzionato per me. Né useCompatPaddingnient'altro ottiene lo stesso risultato.
bgplaya

1
Ho combinato questa risposta con quella di
vikram

22

Si è verificato un problema nella libreria di supporto alla progettazione. Utilizzare il metodo seguente per risolvere questo problema fino a quando la libreria non viene aggiornata. Prova ad aggiungere questo codice alla tua attività o frammento per risolvere il problema. Mantieni il tuo xml lo stesso. Su lollipop e sopra non c'è margine ma sotto c'è un margine di 16dp.

Aggiorna esempio di lavoro

XML - FAB è all'interno di un 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"/>

Giava

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);
}

Converti 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);
}

Lecca-lecca

inserisci qui la descrizione dell'immagine

Pre Lollipop

inserisci qui la descrizione dell'immagine


Questa dovrebbe essere la risposta corretta. Tuttavia, non risolve il problema al 100% poiché anche l'imbottitura non è quadrata.
JohnnyLambada

@JohnnyLambada Non la penso così. RelativeLayout.LayoutParamsnon può essere lanciato al LayoutParamsdi FloatingActionButtone non vedo alcun margine di 16dp su pre Lollipop. La larghezza del FloatingActionButtonpre Lollipop è 84dp invece di 56dp e l'altezza è 98dp.
Markus Rubey

@MarkusRubey Aggiornerò la mia risposta con un esempio funzionante e immagini che mostrano la versione pre lollipop e lollipop.
Eugene H

1
@MarkusRubey - View.getLayoutParamsrestituisce a LayoutParamsdel tipo di Viewis in - nel caso di Eugene è a RelativeLayout.LayoutParams.
JohnnyLambada

1
@EugeneH Ho cambiato RelativeLayout.LayoutParams in ViewGroup.MarginLayoutParams. Questo farà funzionare il codice in molti altri casi e risolverà il problema sollevato da MarkusRubey.
JohnnyLambada

8

In pre Lollipop FloatingActionButtonè responsabile del disegno della propria ombra. Pertanto la vista deve essere leggermente più grande per fare spazio all'ombra. Per ottenere un comportamento coerente è possibile impostare i margini per tenere conto della differenza di altezza e larghezza. Attualmente sto utilizzando la seguente classe :

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);
        }
    }
}

Aggiornamento: le librerie di supporto Android v23 rinominate fab_size si riducono a:

import static android.support.design.R.dimen.design_fab_size_normal;
import static android.support.design.R.dimen.design_fab_size_mini;

Ho appena effettuato il refactoring del mio progetto su AndroidX e non sono in grado di creare il terzo costruttore dal tuo esempio. Come posso farlo utilizzando le librerie AndroidX?
Alon Shlider

5

La risposta di Markus ha funzionato bene per me dopo l'aggiornamento alla v23.1.0 e apportato alcune modifiche alle importazioni (con il recente plug-in gradle usiamo la nostra app R invece della libreria di progettazione R). Ecco il codice per la 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);
        }
    }
}

Non riesco a utilizzare questo esempio dopo il refactoring su AndroidX: il terzo costruttore non si compila più. Sai cosa c'è che non va?
Alon Shlider

3

Nel file di layout impostare l'elevazione dell'attributo su 0. perché prende l'elevazione predefinita.

app:elevation="0dp"

Ora in attività controlla il livello API maggiore di 21 e imposta l'elevazione se necessario.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    fabBtn.setElevation(10.0f);
}

Se stai usando la libreria compat , usa setCompatElevation . E sarà meglio usare i tuffi invece dei pixel direttamente.
ingkevin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.