Android 设计库 - 浮动操作按钮 Padding/Margin 问题
Android Design Library - Floating Action Button Padding/Margin Issues
我正在使用 Google 设计库中的新 FloatingActionButton,我遇到了一些奇怪的 padding/margin 问题。此图片(开发者布局选项打开)来自 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 大得多 padding/margin 明智?
设计支持库中存在问题。在更新库之前,请使用以下方法解决此问题。尝试将此代码添加到您的 activity 或片段以解决问题。保持你的 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"/>
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);
}
将 dp 转换为 px
public static int dpToPx(Context context, float dp) {
// Reference
float scale = context.getResources().getDisplayMetrics().density;
return (int) ((dp * scale) + 0.5f);
}
棒棒糖
前棒棒糖
在 Lollipop 之前 FloatingActionButton
负责绘制自己的阴影。因此,视图必须稍微大一些才能使 space 成为阴影。为了获得一致的行为,您可以设置边距以说明高度和宽度的差异。我目前正在使用以下 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);
}
}
}
更新: 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;
更新(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 中,它使用不影响视图宽度的新高程参数。
在更新到 v23.1.0 并对导入进行了一些调整后对我来说效果很好(使用最近的 gradle 插件,我们使用我们的应用程序 R 而不是设计库的 R)。这是 v23.1.0 的代码:
// Based on this answer:
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);
}
}
}
不再摆弄 styles.xml
或 .java
文件。让我简单点。
You can use app:useCompatPadding="true"
and remove custom margins to maintain same margins across different versions of android
您在第二张图片的 FAB 上看到的额外 margin/padding 是由于 pre-lollipop[=33 上的 compatPadding =] 设备。如果此 属性 未设置,它将应用于 pre-lollopop 设备和 NOT 在 lollipop+ 设备中。
概念验证
经过几次搜索和测试解决方案后,我通过将此行添加到我的 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" />
在布局文件中将属性高度设置为 0。因为它采用默认高度。
app:elevation="0dp"
现在在 activity 中检查 api 级别大于 21 并根据需要设置高度。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fabBtn.setElevation(10.0f);
}
我正在使用 Google 设计库中的新 FloatingActionButton,我遇到了一些奇怪的 padding/margin 问题。此图片(开发者布局选项打开)来自 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 大得多 padding/margin 明智?
设计支持库中存在问题。在更新库之前,请使用以下方法解决此问题。尝试将此代码添加到您的 activity 或片段以解决问题。保持你的 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"/>
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);
}
将 dp 转换为 px
public static int dpToPx(Context context, float dp) {
// Reference
float scale = context.getResources().getDisplayMetrics().density;
return (int) ((dp * scale) + 0.5f);
}
棒棒糖
前棒棒糖
在 Lollipop 之前 FloatingActionButton
负责绘制自己的阴影。因此,视图必须稍微大一些才能使 space 成为阴影。为了获得一致的行为,您可以设置边距以说明高度和宽度的差异。我目前正在使用以下 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);
}
}
}
更新: 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;
更新(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 中,它使用不影响视图宽度的新高程参数。
// Based on this answer:
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);
}
}
}
不再摆弄 styles.xml
或 .java
文件。让我简单点。
You can use
app:useCompatPadding="true"
and remove custom margins to maintain same margins across different versions of android
您在第二张图片的 FAB 上看到的额外 margin/padding 是由于 pre-lollipop[=33 上的 compatPadding =] 设备。如果此 属性 未设置,它将应用于 pre-lollopop 设备和 NOT 在 lollipop+ 设备中。
概念验证
经过几次搜索和测试解决方案后,我通过将此行添加到我的 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" />
在布局文件中将属性高度设置为 0。因为它采用默认高度。
app:elevation="0dp"
现在在 activity 中检查 api 级别大于 21 并根据需要设置高度。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fabBtn.setElevation(10.0f);
}