Thư viện thiết kế Android - Các vấn đề về đệm / lề cho nút hành động nổi


109

Tôi đang sử dụng FloatingActionButton mới từ Thư viện thiết kế của Google và tôi đang gặp một số vấn đề về phần đệm / lề lạ. Hình ảnh này (có bật tùy chọn bố cục của nhà phát triển) là từ API 22.

nhập mô tả hình ảnh ở đây

Và từ API 17.

nhập mô tả hình ảnh ở đây

Đây là 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"/>

Tại sao FAB trong API 17 lại có phần đệm / ký quỹ lớn hơn rất nhiều?


1
Tôi khuyên bạn nên sử dụng CoordinatorLayoutđể căn chỉnh nó theo chiều dọc và dán nhãn cầu vào miếng kẹo mút có đệm thêm. Bạn có thể tìm ra nó từ các nguồn FAB đã được dịch ngược, nhưng tôi muốn đợi Google sửa nó giống như họ đã làm CardView.
DariusL

Câu trả lời:


129

Cập nhật (tháng 10 năm 2016):

Giải pháp chính xác bây giờ là đưa app:useCompatPadding="true"vào FloatingActionButton của bạn. Điều này sẽ làm cho phần đệm nhất quán giữa các phiên bản API khác nhau. Tuy nhiên, điều này dường như vẫn làm cho lợi nhuận mặc định bị giảm đi một chút, vì vậy bạn có thể cần phải điều chỉnh chúng. Nhưng ít nhất thì không cần thêm các kiểu dành riêng cho API.

Câu trả lời trước:

Bạn có thể thực hiện điều này dễ dàng bằng cách sử dụng các kiểu dành riêng cho API. Trong thông thường values/styles.xml, hãy đặt một cái gì đó như sau:

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

và sau đó trong các giá trị-v21 / styles.xml, hãy sử dụng điều này:

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

và áp dụng kiểu cho FloatingActionButton của bạn:

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

Như những người khác đã lưu ý, trong API <20, nút hiển thị bóng của chính nó, làm tăng chiều rộng hợp lý tổng thể của chế độ xem, trong khi trong API> = 20, nút này sử dụng các thông số Độ cao mới không đóng góp vào độ rộng của chế độ xem.


19
lỗi Android .. :(
Abdullah Shoaib

3
trông xấu xí một cách kỳ cục, nhưng có vẻ như không có giải pháp nào khác
11

3
Câu trả lời tốt, cảm ơn bạn. Tôi có thể đề xuất chỉnh sửa nó và thêm kiểu cho máy tính bảng không? Đối với API> = 20, ký quỹ là 24dp; đối với API <20, tôi nhận thấy rằng bạn cần <item name = "android: layout_marginRight"> 12dp </item> <item name = "android: layout_marginBottom"> 6dp </item> . Tôi đã phải tự mình tính toán chúng vì tôi cần nó cho ứng dụng của mình. Bạn cũng có thể sử dụng tài nguyên kích thước thay vì kiểu.
JJ86

Số ký quỹ sẽ khác nhau tùy thuộc vào số tiền elevationbạn đặt trên FAB.
Jarett Millard

sử dụngapp:useCompatPadding="true"
Kishan Vaghela

51

Không còn phải loay hoay với styles.xmlhoặc với .javacác tệp. Hãy để tôi làm cho nó đơn giản.

Bạn có thể sử dụng app:useCompatPadding="true"và xóa các lề tùy chỉnh để duy trì lợi nhuận như nhau trên các phiên bản android khác nhau

Lề tắm / đệm bạn đã thấy trên FAB trong bức tranh thứ hai của bạn là do này compatPadding trên pre-kẹo các thiết bị. Nếu thuộc tính này không được đặt, thuộc tính này sẽ được áp dụng trên các thiết bị pre-lollopop chứ KHÔNG phải trong thiết bị lollipop +.

mã studio android

Bằng chứng của khái niệm

Chế độ xem thiết kế


nếu Bố cục chính là chế độ xem thẻ thì chúng ta cần sử dụng "card_view: useCompatPadding =" true "cho fab.
Amit Tumkur

Điều này phù hợp với tôi sau khi nâng cấp thư viện hỗ trợ lên phiên bản 23.2. Cảm ơn!
Pablo

Tôi thấy lợi nhuận trong PoC của bạn.
Pedro Oliveira

@PedroOliveira Có, bạn sẽ thấy cùng một mức lợi nhuận trên tất cả các phiên bản Android, đó thực tế là mục tiêu.
Saravanabalagi Ramachandran

1
Bạn đang nói rằng anh ta có thể sử dụng "xxx" để xóa các lề tùy chỉnh, nhưng bằng chứng về khái niệm của bạn cho thấy tất cả các lề, giống như OP đã hỏi tại sao.
Pedro Oliveira

31

sau một vài thời gian tìm kiếm và thử nghiệm giải pháp, tôi đã khắc phục sự cố của mình bằng cách chỉ thêm dòng này vào bố cục xml của mình:

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

và đây là toàn bộ bố cục nút phao của tôi

<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
Tuyệt vời, đã làm việc cho tôi. Không useCompatPaddinghoặc bất cứ điều gì khác nhận được cùng một kết quả.
bgplaya

1
Tôi đã kết hợp câu trả lời này với vikram của
ingkevin

22

Có một vấn đề trong Thư viện hỗ trợ thiết kế. Sử dụng phương pháp bên dưới để khắc phục sự cố này cho đến khi thư viện được cập nhật. Hãy thử thêm mã này vào hoạt động hoặc phân đoạn của bạn để giải quyết vấn đề. Giữ nguyên xml của bạn. Trên kẹo mút trở lên không có lề nhưng bên dưới có lề 16dp.

Cập nhật ví dụ làm việc

XML - FAB nằm trong 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);
}

Chuyển đổi dp sang 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);
}

Kẹo mút

nhập mô tả hình ảnh ở đây

Pre Lollipop

nhập mô tả hình ảnh ở đây


Đây phải là câu trả lời chính xác. Tuy nhiên, nó không khắc phục được sự cố 100% vì phần đệm cũng không tồn tại.
JohnnyLambada

@JohnnyLambada Tôi không nghĩ vậy. RelativeLayout.LayoutParamskhông thể được đúc vào LayoutParamscủa FloatingActionButtonvà tôi không thấy bất kỳ lợi nhuận của 16dp trên trước Lollipop. Chiều rộng của FloatingActionButtonpre Lollipop là 84dp thay vì 56dp và chiều cao của nó là 98dp.
Markus Rubey

@MarkusRubey Tôi sẽ cập nhật câu trả lời của mình bằng một ví dụ làm việc và hình ảnh hiển thị phiên bản kẹo mút và kẹo mút trước.
Eugene H

1
@MarkusRubey - View.getLayoutParamstrả về một LayoutParamskiểu Viewlà trong - trong trường hợp của Eugene, đó là một RelativeLayout.LayoutParams.
JohnnyLambada

1
@EugeneH Tôi đã thay đổi RelativeLayout.LayoutParams thành ViewGroup.MarginLayoutParams. Điều này sẽ làm cho mã hoạt động trong nhiều trường hợp hơn và khắc phục sự cố mà MarkusRubey đưa ra.
JohnnyLambada

8

Trên pre Lollipop FloatingActionButtonchịu trách nhiệm vẽ bóng của chính nó. Do đó tầm nhìn phải lớn hơn một chút để tạo khoảng trống cho bóng. Để có được một hành vi nhất quán, bạn có thể đặt lề để tính đến sự khác biệt về chiều cao và chiều rộng. Tôi hiện đang sử dụng lớp sau :

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

Cập nhật: Thư viện hỗ trợ Android v23 đã đổi tên fab_sizemens thành:

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

Tôi vừa cấu trúc lại dự án của mình thành AndroidX và tôi không thể tạo phương thức khởi tạo thứ 3 từ ví dụ của bạn. Làm cách nào tôi có thể làm điều đó bằng cách sử dụng các thư viện AndroidX?
Alon Shlider

5

Câu trả lời của Markus đã hoạt động tốt đối với tôi sau khi cập nhật lên v23.1.0 và thực hiện một số điều chỉnh đối với việc nhập (với plugin gradle gần đây, chúng tôi sử dụng ứng dụng R của mình thay vì R của thư viện thiết kế). Đây là mã cho 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);
        }
    }
}

Tôi dường như không thể sử dụng ví dụ này sau khi cấu trúc lại thành AndroidX - hàm tạo thứ 3 không biên dịch nữa. Bạn có biết điều gì là sai?
Alon Shlider

3

Trong tệp bố trí, đặt thuộc tính độ cao bằng 0. vì nó có độ cao mặc định.

app:elevation="0dp"

Bây giờ trong hoạt động, hãy kiểm tra mức api lớn hơn 21 và đặt độ cao nếu cần.

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

Nếu bạn đang sử dụng thư viện compat, hãy sử dụng setCompatElevation . Và sẽ tốt hơn nếu sử dụng dips thay vì pixel trực tiếp.
ingkevin 26/07/18
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.