Android Design Library - Проблемы с плавающими кнопками действий / поля
Я использую новый FloatingActionButton из библиотеки дизайна Google и получаю некоторые странные проблемы с отступами / полями. Это изображение (с опциями макета для разработчика) взято из 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"/>
Почему FAB в API 17 намного больше для отступов / полей?
7 ответов
Обновление (октябрь 2016 г.):
Правильное решение сейчас поставить 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, которые не влияют на ширину вида.
Нет больше возиться с styles.xml
или с .java
файлы. Позвольте мне сделать это просто.
Ты можешь использовать
app:useCompatPadding="true"
и удалите пользовательские поля для сохранения одинаковых полей в разных версиях Android
Дополнительное поле / отступы, которые вы видели на FAB на втором изображении, связаны с этим compatPadding на устройствах перед леденцом на палочке. Если это свойство не установлено, оно применяется на устройствах до lollopop, а НЕ на устройствах 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" />
Существует проблема в библиотеке поддержки проектирования. Используйте метод ниже, чтобы исправить эту проблему, пока библиотека не будет обновлена. Попробуйте добавить этот код в свою деятельность или фрагмент, чтобы решить эту проблему. Держите свой XML такой же. На леденце и выше нет наценки, но ниже - на 16 дп.
Обновить рабочий пример
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://stackru.com/questions/8309354/formula-px-to-dp-dp-to-px-android
float scale = context.getResources().getDisplayMetrics().density;
return (int) ((dp * scale) + 0.5f);
}
леденец
Pre Lollipop
На пред леденец 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;
Ответ Маркуса работал хорошо для меня после обновления до v23.1.0 и внесения некоторых корректировок в импорт (с последним плагином gradle мы используем наше приложение R вместо библиотеки R). Вот код для v23.1.0:
// Based on this answer: https://stackru.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);
}
}
}
В файле макета установите для атрибута значение 0, потому что для него требуется значение по умолчанию.
app:elevation="0dp"
Теперь в действии проверьте уровень API больше 21 и при необходимости установите высоту.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fabBtn.setElevation(10.0f);
}