Android设计库-浮动操作按钮填充/页边距问题


109

我正在使用Google设计库中新的FloatingActionButton,但遇到了一些奇怪的填充/边距问题。该图像(启用了开发者布局选项)来自API 22。

在此处输入图片说明

从API 17开始

在此处输入图片说明

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

为什么API 17中的FAB这么大的填充/边距明智的选择?


1
我建议使用CoordinatorLayout垂直对齐,并注意多余的填充棒棒糖。您可以从反编译的FAB来源中找出来,但我宁愿等待Google像他们一样对它进行修复CardView
DariusL

Answers:


129

更新(2016年10月):

现在正确的解决方案是放入app:useCompatPadding="true"您的FloatingActionButton。这将使不同API版本之间的填充保持一致。但是,这似乎仍然会使默认页边距略有减少,因此您可能需要对其进行调整。但是,至少不再需要特定于API的样式。

先前的答案:

您可以使用特定于API的样式轻松完成此操作。在您的正常情况下values/styles.xml,输入以下内容:

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

然后在values-v21 / styles.xml下使用以下命令:

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

并将样式应用于您的FloatingActionButton:

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

正如其他人指出的那样,在API <20中,按钮呈现其自己的阴影,这会增加视图的整体逻辑宽度,而在API> = 20中,按钮会使用新的Elevation参数,而这些参数不会对视图宽度产生影响。


19
越野车Android .. ::
Abdullah Shoaib 2015年

3
看起来丑陋可笑,但似乎没有其他解决方案
2015年

3
好答案,谢谢。我可以建议对平板电脑进行编辑并添加样式吗?如果API> = 20,则裕度为24dp;对于API <20,我注意到您需要<item name =“ android:layout_marginRight”> 12dp </ item> <item name =“ android:layout_marginBottom”> 6dp </ item>。我必须自己计算它们,因为我的应用需要它。您也可以使用dimens资源代替样式。
JJ86

根据elevation您在FAB 上设置的不同,边距数字将有所不同。
贾里特·米拉德

使用app:useCompatPadding="true"
Kishan Vaghela

51

没有更多的摆弄styles.xml.java文件。让我简单点。

您可以使用app:useCompatPadding="true"和删除自定义边距,以在不同版本的android 上保持相同的边距

您在第二张图片中在FAB上看到的多余边距/填充是由于棒棒糖设备上的compatPadding造成的。如果没有设置该属性,它被应用在预lollopop设备和NOT在棒棒糖+设备。

android studio代码

概念证明

设计图


如果家长布局卡片视图那么我们就需要使用“card_view:useCompatPadding =”真”的工厂
阿米特杜姆古尔

将支持库升级到版本23.2后,这对我有用。谢谢!
巴勃罗

我在您的PoC中看到利润。
Pedro Oliveira

@PedroOliveira是的,您将在所有版本的Android上看到相同的边距,这实际上是目标。
Saravanabalagi Ramachandran

1
您是说他可以使用“ xxx”删除自定义边距,但是您的概念证明会显示所有边距,例如OP询问原因。
Pedro Oliveira 2016年

31

经过一段时间的搜索和测试解决方案后,我解决了此问题,仅将此行添加到我的xml布局中:

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

这是我整个浮动按钮的布局

<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
太好了,为我工作。任何useCompatPadding东西都不会得到相同的结果。
bgplaya

1
我已将此答案与vikram
-ingkevin

22

设计支持库中存在一个问题。使用以下方法可解决此问题,直到更新库。尝试将此代码添加到您的活动或片段中以解决问题。保持您的xml不变。在棒棒糖上,没有余量,但在棒糖以下,有16dp的余量。

更新工作示例

XML-FAB在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"/>

爪哇

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

将dp转换为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);
}

棒糖

在此处输入图片说明

棒棒糖前

在此处输入图片说明


这应该是正确的答案。但是,它不能100%解决问题,因为填充也是不平方的。
JohnnyLambada

@JohnnyLambada我不这么认为。RelativeLayout.LayoutParams不能被转换到LayoutParamsFloatingActionButton,我没有看到16DP预棒棒糖任何空白。FloatingActionButton棒棒糖的宽度为84dp,而不是56dp,高度为98dp。
Markus Rubey 2015年

@MarkusRubey我将使用一个有效的示例和显示之前的棒棒糖和棒棒糖版本的图像来更新我的答案。
尤金H

1
@MarkusRubey- View.getLayoutParams返回in中LayoutParams类型的a- View在Eugene的情况下为a RelativeLayout.LayoutParams
JohnnyLambada 2015年

1
@EugeneH我将RelativeLayout.LayoutParams更改为ViewGroup.MarginLayoutParams。这将使代码在更多情况下工作,并解决MarkusRubey提出的问题。
JohnnyLambada 2015年

8

预先棒棒糖FloatingActionButton负责绘制自己的阴影。因此,视图必须稍大些才能为阴影留出空间。为了获得一致的行为,您可以设置边距以说明高度和宽度的差异。我目前正在使用以下课程

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

更新: Android支持库v23重命名为fab_size,将调暗为:

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

我刚刚将项目重构为AndroidX,但无法从您的示例创建第3个构造函数。如何使用AndroidX库执行此操作?
阿隆·史利德

5

在更新到v23.1.0并对导入进行一些调整后,Markus的答案对我来说效果很好(对于最近的gradle插件,我们使用应用程序R而不是设计库的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);
        }
    }
}

重构为AndroidX后,我似乎无法使用此示例-第3个构造函数不再编译。你知道出什么事了吗
阿隆·史利德19/12/22

3

在布局文件中,将属性标高设置为0。因为它采用默认标高。

app:elevation="0dp"

现在在活动中检查api级别是否大于21,并根据需要设置海拔高度。

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

如果您使用compat库,请使用setCompatElevation。而且最好直接使用Dip代替像素。
ingkevin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.