Бібліотека дизайну Android - Проблеми з накладкою / маржами плаваючих дій


109

Я використовую новий 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 має настільки великі накладки / запаси?


1
Я рекомендую використовувати його CoordinatorLayoutдля вирівнювання по вертикалі та підведення очного яблука додатковими накладками попереднього льодяника. Ви можете це зрозуміти з розкомпільованих джерел FAB, але я б краще зачекати, коли Google виправить це так, як вони це зробили CardView.
ДарійL

Відповіді:


129

Оновлення (жовтень 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>

а потім у значенні-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 .. :(
Абдулла Шойб

3
виглядає смішно негарно, але, схоже, іншого рішення немає
одинадцять

3
Гарна відповідь, дякую. Можна запропонувати відредагувати його та додати стиль також для планшета? Для API> = 20, маржа становить 24dp; для API <20 я помітив, що вам потрібно <item name = "android: layout_marginRight"> 12dp </item> <item name = "android: layout_marginBottom"> 6dp </item> . Мені довелося їх обчислити самостійно, бо мені це було потрібно для мого додатка. Ви також можете використовувати ресурс розмірів замість стилю.
JJ86

Номери маржі будуть різними залежно від встановленого elevationвами FAB.
Джаретт Міллард

використанняapp:useCompatPadding="true"
Кішань Вагела

51

Немає більше возитися з styles.xmlабо з .javaфайлами. Дозвольте зробити це просто.

Ви можете використовувати app:useCompatPadding="true"та видаляти власні поля, щоб підтримувати однакові поля в різних версіях Android

Додатковий запас / накладка, яку ви побачили на FAB на своєму другому малюнку, обумовлений цим compatPadding на пристроях до льодяника . Якщо ця властивість не встановлена, вона застосовується на пристроях, що попередньо використовуються, а НЕ - на пристроях льодяника +.

андроїд код студії

Доказ концепції

вид дизайну


якщо батьківський макет - це перегляд карт, тоді нам потрібно використовувати "card_view: useCompatPadding =" true "для файлу.
Amit Tumkur

Це працює для мене після оновлення бібліотеки підтримки до версії 23.2. Дякую!
Пабло

Я бачу поля у вашому ПК.
Педро Олівейра

@PedroOliveira Так, ви побачите однаковий запас у всіх версіях Android, що насправді є ціллю.
Сараванабалагі Рамачандран

1
Ви говорите, що він може використовувати "xxx" для видалення користувацьких націнок, і все ж ваш доказ концепції показує всі поля, як, наприклад, ОП запитав чому.
Педро Олівейра

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
Я поєднав цю відповідь з вікрам
ingkevin

22

У бібліотеці підтримки підтримки існує проблема. Використовуйте метод нижче, щоб вирішити цю проблему, поки бібліотека не буде оновлена. Спробуйте додати цей код до своєї діяльності або фрагменту, щоб вирішити проблему. Зберігайте xml однаково. На льодянику і вище немає маржі, але внизу є маржа в 16dp.

Оновіть робочий приклад

XML - FAB знаходиться в межах відносного макета

<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 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не може бути приведені до LayoutParamsпро FloatingActionButtonі я не бачу ніякої маржі 16dp на попередньо льодяники. Ширина FloatingActionButtonпопереднього льодяника 84dp замість 56dp, а висота 98dp.
Маркус Рубей

@MarkusRubey Я оновлю свою відповідь робочим прикладом та зображеннями, що відображають попередню версію льодяника та льодяник.
Євген Х

1
@MarkusRubey - View.getLayoutParamsповертає LayoutParamsтип типу Viewin - у випадку Євгена це a RelativeLayout.LayoutParams.
JohnnyLambada

1
@EugeneH Я змінив RelativeLayout.LayoutParams на ViewGroup.MarginLayoutParams. Це змусить код працювати в багатьох інших випадках і виправить проблему, яку висуває MarkusRubey.
JohnnyLambada

8

Попередньо 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;

Я просто переробив свій проект на AndroidX і не можу створити 3-й конструктор з вашого прикладу. Як це зробити за допомогою бібліотек AndroidX?
Алон Шлайдер

5

Відповідь Маркуса спрацювала для мене добре після оновлення до v23.1.0 та внесення деяких коригувань імпорту (за останнім плагіном 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-й конструктор вже не збирається. Ви знаєте, що не так?
Алон Шлайдер

3

У файлі макета встановіть атрибут висоти на 0, оскільки він займає висоту за замовчуванням.

app:elevation="0dp"

Тепер у активності перевіряють рівень api більше 21 та встановлюють висоту, якщо потрібно.

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

Якщо ви використовуєте бібліотеку compat, використовуйте setCompatElevation . І краще буде використовувати краплі замість пікселів безпосередньо.
ingkevin
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.