Android Design Library - Problemas de preenchimento / margem do botão de ação flutuante


109

Estou usando o novo FloatingActionButton da Biblioteca de projetos do Google e estou tendo problemas estranhos de preenchimento / margem. Esta imagem (com opções de layout do desenvolvedor ativadas) é da API 22.

insira a descrição da imagem aqui

E da API 17.

insira a descrição da imagem aqui

Este é o 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"/>

Por que o FAB na API 17 é muito maior em termos de preenchimento / margem?


1
Eu recomendo usar CoordinatorLayoutpara alinhá-lo verticalmente e observar o preenchimento extra pré-pirulito. Você poderia descobrir a partir das fontes FAB descompiladas, mas prefiro esperar que o Google conserte como fez para CardView.
— DariusL

Respostas:


129

Atualização (outubro de 2016):

A solução correta agora é colocar app:useCompatPadding="true"em seu FloatingActionButton. Isso tornará o preenchimento consistente entre as diferentes versões da API. No entanto, isso ainda parece diminuir um pouco as margens padrão, então você pode precisar ajustá-las. Mas pelo menos não há necessidade de estilos específicos de API.

Resposta anterior:

Você pode fazer isso facilmente usando estilos específicos de API. Em seu normal values/styles.xml, coloque algo assim:

<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 em values-v21 / styles.xml, use isto:

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

e aplique o estilo ao seu FloatingActionButton:

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

Como outros notaram, na API <20, o botão renderiza sua própria sombra, o que adiciona à largura lógica geral da visualização, enquanto na API> = 20 ele usa os novos parâmetros de elevação que não contribuem para a largura da visualização.


19
Android com erros .. :(
— Abdullah Shoaib

3
parece ridiculamente feio, mas parece que não há outra solução
— onze de

3
Boa resposta, obrigado. Posso sugerir editá-lo e adicionar um estilo também para tablet? Para API> = 20, a margem é de 24 dp; para API <20, percebi que você precisa de <item name = "android: layout_marginRight"> 12dp </item> <item name = "android: layout_marginBottom"> 6dp </item> . Tive de calculá-los sozinho, porque precisava disso para o meu aplicativo. Você também pode usar recursos dimens em vez de estilo.
— JJ86 de

Os números das margens serão diferentes dependendo do que elevationvocê definir no FAB.
— Jarett Millard de

usoapp:useCompatPadding="true"
— Kishan Vaghela

51

Chega de mexer com styles.xmlou com .javaarquivos. Deixe-me simplificar.

Você pode usar app:useCompatPadding="true"e remover margens personalizadas para manter as mesmas margens em diferentes versões do Android

A margem extra / preenchimento que você viu no FAB em sua segunda foto é devido a este compatPadding em dispositivos pré-pirulito . Se esta propriedade não for definida, ela será aplicada em dispositivos pré-lollopop e NÃO em dispositivos lollipop +.

código do android studio

Prova de conceito

vista de design


se o layout pai for de visualização de cartão, então precisamos usar "card_view: useCompatPadding =" true "para fab.
— Amit Tumkur

Isso funciona para mim depois de atualizar a biblioteca de suporte para a versão 23.2. Obrigado!
— Pablo

Eu vejo margens em seu PoC.
— Pedro Oliveira

@PedroOliveira Sim, você verá a mesma margem em todas as versões do Android, o que na verdade é o objetivo.
— Saravanabalagi Ramachandran

1
Você está dizendo que ele pode usar "xxx" para remover as margens personalizadas, mas sua prova de conceito mostra todas as margens, como o OP perguntou por quê.
— Pedro Oliveira

31

depois de algum tempo pesquisando e testando a solução, resolvo meu problema ao adicionar esta linha somente ao meu layout xml:

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

e este é todo o meu layout de botão flutuante

<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
Ótimo, funcionou para mim. Nem useCompatPaddingnem qualquer outra coisa obtém o mesmo resultado.
— bgplaya


22

Há um problema na Biblioteca de Suporte de Design. Use o método abaixo para corrigir esse problema até que a biblioteca seja atualizada. Tente adicionar este código à sua atividade ou fragmento para resolver o problema. Mantenha o seu xml igual. No pirulito e acima não há margem, mas abaixo há uma margem de 16 dp.

Atualizar Exemplo de Trabalho

XML - FAB está dentro de um 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);
}

Converter dp em 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);
}

Pirulito

insira a descrição da imagem aqui

Pré Lollipop

insira a descrição da imagem aqui


Esta deve ser a resposta correta. No entanto, isso não corrige o problema 100%, pois o preenchimento também não é quadrado.
— JohnnyLambada de

@JohnnyLambada Acho que não. RelativeLayout.LayoutParamsnão pode ser convertido para o LayoutParamsde FloatingActionButtone não vejo nenhuma margem de 16 dp no pré Lollipop. A largura do FloatingActionButtonpré Lollipop é 84 dp em vez de 56 dp e sua altura é 98 dp.
— Markus Rubey

@MarkusRubey Vou atualizar minha resposta com um exemplo de trabalho e imagens exibindo a versão pré e pirulito.
— Eugene H

1
@MarkusRubey - View.getLayoutParamsretorna um LayoutParamsdo tipo de Viewestá in - no caso de Eugene é a RelativeLayout.LayoutParams.
— JohnnyLambada

1
@EugeneH Eu alterei RelativeLayout.LayoutParams para ViewGroup.MarginLayoutParams. Isso fará com que o código funcione em muitos outros casos e corrigirá o problema que MarkusRubey traz à tona.
— JohnnyLambada de

8

No pré Lollipop FloatingActionButtoné responsável por desenhar sua própria sombra. Portanto, a visualização deve ser ligeiramente maior para dar espaço para a sombra. Para obter um comportamento consistente, você pode definir as margens para compensar a diferença de altura e largura. Atualmente, estou usando a seguinte 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);
        }
    }
}

Atualização: Bibliotecas de suporte Android v23 renomeado fab_size dimens para:

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

Acabei de refatorar meu projeto para AndroidX e não consigo criar o terceiro construtor de seu exemplo. Como posso fazer isso usando bibliotecas AndroidX?
— Alon Shlider

5

A resposta de Markus funcionou bem para mim depois de atualizar para a v23.1.0 e fazer alguns ajustes nas importações (com o plugin gradle recente, usamos nosso aplicativo R em vez do R da biblioteca de design). Aqui está o código da 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);
        }
    }
}

Não consigo usar este exemplo após a refatoração para AndroidX - o terceiro construtor não compila mais. Você sabe o que está errado?
— Alon Shlider

3

No arquivo de layout, defina a elevação do atributo como 0. porque leva a elevação padrão.

app:elevation="0dp"

Agora, em atividade, verifique o nível de API maior que 21 e defina a elevação, se necessário.

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

Se você estiver usando a biblioteca compat , use setCompatElevation . E será melhor usar quedas em vez de pixels diretamente.
— ingkevin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.