Android Design Library - Masalah Padding / Margin Tombol Tindakan Mengambang

109

Saya menggunakan FloatingActionButton baru dari Google Design Library dan saya mendapatkan beberapa masalah padding / margin yang aneh. Gambar ini (dengan opsi tata letak pengembang aktif) berasal dari API 22.

masukkan deskripsi gambar di sini

Dan dari API 17.

masukkan deskripsi gambar di sini

Ini adalah 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"/>

Mengapa FAB di API 17 jauh lebih besar dari segi padding / margin?

hitch.united
sumber
1
Saya akan merekomendasikan penggunaan CoordinatorLayoutuntuk menyelaraskannya secara vertikal dan mengamati bantalan ekstra pra-lollipop. Anda dapat mengetahuinya dari sumber FAB yang didekompilasi, tetapi saya lebih suka menunggu Google memperbaikinya seperti yang mereka lakukan CardView.
DariusL

Jawaban:

129

Pembaruan (Okt 2016):

Solusi yang tepat sekarang adalah memasukkan app:useCompatPadding="true"FloatingActionButton Anda. Ini akan membuat padding konsisten di antara berbagai versi API. Namun, ini tampaknya masih membuat margin default sedikit berkurang, jadi Anda mungkin perlu menyesuaikannya. Tapi setidaknya tidak diperlukan lagi gaya khusus API.

Jawaban sebelumnya:

Anda dapat melakukannya dengan mudah menggunakan gaya khusus API. Dalam keadaan normal Anda values/styles.xml, letakkan sesuatu seperti ini:

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

dan kemudian di bawah values-v21 / styles.xml, gunakan ini:

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

dan terapkan gaya ke FloatingActionButton Anda:

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

Seperti yang telah dicatat orang lain, dalam API <20, tombol menampilkan bayangannya sendiri, yang menambah lebar logis keseluruhan tampilan, sedangkan di API> = 20 menggunakan parameter Elevasi baru yang tidak berkontribusi pada lebar tampilan.

Dmitry Brant
sumber
19
buggy Android .. :(
Abdullah Shoaib
3
terlihat sangat jelek, tetapi tampaknya tidak ada solusi lain
sebelas
3
Jawaban bagus, terima kasih. Bolehkah saya menyarankan untuk mengeditnya dan menambahkan gaya juga untuk tablet? Untuk API> = 20, marginnya 24dp; untuk API <20 saya perhatikan bahwa Anda membutuhkan <item name = "android: layout_marginRight"> 12dp </item> <item name = "android: layout_marginBottom"> 6dp </item> . Saya harus menghitungnya sendiri, karena saya membutuhkannya untuk aplikasi saya. Anda juga dapat menggunakan sumber daya dimens sebagai ganti gaya.
JJ86
Nomor margin akan berbeda tergantung pada yang elevationAnda atur di FAB.
Jarett Millard
gunakanapp:useCompatPadding="true"
Kishan Vaghela
51

Tidak perlu lagi mengutak - atikstyles.xml atau dengan .javafile. Biarkan saya membuatnya sederhana.

Anda dapat menggunakan app:useCompatPadding="true"dan menghapus margin kustom untuk mempertahankan margin yang sama di berbagai versi Android

Margin / padding ekstra yang Anda lihat di FAB di gambar kedua Anda disebabkan oleh compatPadding ini pada perangkat pra-lollipop . Jika properti ini tidak disetel, properti ini akan diterapkan di perangkat pra-lollopop dan BUKAN di perangkat lollipop +.

kode studio android

Bukti dari konsep

tampilan desain

Saravanabalagi Ramachandran
sumber
Jika tata letak Induk adalah tampilan kartu maka kita perlu menggunakan "card_view: useCompatPadding =" true "untuk hebat.
Amit Tumkur
Ini berfungsi untuk saya setelah memutakhirkan pustaka dukungan ke versi 23.2. Terima kasih!
Pablo
Saya melihat margin di PoC Anda.
Pedro Oliveira
@PedroOliveira Ya, Anda akan melihat margin yang sama di semua versi Android, yang sebenarnya adalah tujuannya.
Saravanabalagi Ramachandran
1
Anda mengatakan bahwa dia dapat menggunakan "xxx" untuk menghapus margin kustom, namun bukti konsep Anda menunjukkan semua margin, seperti OP bertanya mengapa.
Pedro Oliveira
31

setelah beberapa waktu mencari dan menguji solusi, saya memperbaiki masalah saya dengan menambahkan baris ini ke hanya tata letak xml saya:

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

dan ini adalah seluruh tata letak tombol float saya

<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" />
Mostafa Rostami
sumber
2
Bagus, berhasil untuk saya. Tidak useCompatPaddingada yang lain mendapatkan hasil yang sama.
bgplaya
1
Saya telah menggabungkan jawaban ini dengan vikram
ingkevin
22

Ada masalah dalam Pustaka Dukungan Desain. Gunakan metode di bawah ini untuk memperbaiki masalah ini sampai perpustakaan diperbarui. Coba tambahkan kode ini ke aktivitas atau fragmen Anda untuk menyelesaikan masalah. Jaga agar xml Anda tetap sama. Di lollipop ke atas tidak ada margin tapi di bawah ada margin 16dp.

Perbarui Contoh Kerja

XML - FAB ada di dalam 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"/>

Jawa

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

Ubah dp ke 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);
}

Lollipop

masukkan deskripsi gambar di sini

Pra Lollipop

masukkan deskripsi gambar di sini

Eugene H.
sumber
Ini harus menjadi jawaban yang benar. Namun, itu tidak memperbaiki masalah 100% karena bantalannya juga tidak persegi.
JohnnyLambada
@JnyLambada Saya rasa tidak. RelativeLayout.LayoutParamstidak dapat dilemparkan ke LayoutParamsdari FloatingActionButtondan saya tidak melihat margin 16 dp pada pra Lollipop. Lebar FloatingActionButtonpre Lollipop adalah 84dp bukan 56dp dan tingginya 98dp.
Markus Rubey
@MarkusRubey Saya akan memperbarui jawaban saya dengan contoh kerja dan gambar yang menampilkan versi pra lollipop dan lollipop.
Eugene H
1
@MarkusRubey - View.getLayoutParamsmengembalikan LayoutParamsjenis Viewis in - dalam kasus Eugene, ini adalah a RelativeLayout.LayoutParams.
JohnnyLambada
1
@EugeneH Saya mengubah RelativeLayout.LayoutParams menjadi ViewGroup.MarginLayoutParams. Ini akan membuat kode berfungsi di lebih banyak kasus dan memperbaiki masalah yang dimunculkan MarkusRubey.
JohnnyLambada
8

Pada pra Lollipop FloatingActionButtonbertanggung jawab untuk menggambar bayangannya sendiri. Oleh karena itu, tampilan harus sedikit lebih besar untuk memberi ruang bagi bayangan. Untuk mendapatkan perilaku yang konsisten, Anda dapat mengatur margin untuk memperhitungkan perbedaan tinggi dan lebar. Saya saat ini menggunakan kelas berikut :

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

Pembaruan: Pustaka Dukungan Android v23 mengganti nama fab_size dimens menjadi:

import static android.support.design.R.dimen.design_fab_size_normal;
import static android.support.design.R.dimen.design_fab_size_mini;
Markus Rubey
sumber
Saya baru saja memfaktorkan ulang proyek saya ke AndroidX dan saya tidak dapat membuat konstruktor ke-3 dari contoh Anda. Bagaimana saya bisa melakukannya menggunakan pustaka AndroidX?
Alon Shlider
5

Jawaban Markus bekerja dengan baik untuk saya setelah memperbarui ke v23.1.0 dan membuat beberapa penyesuaian pada impor (dengan plugin gradle terbaru kami menggunakan aplikasi R kami alih-alih R perpustakaan desain). Berikut adalah kode untuk 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);
        }
    }
}
Pelajari OpenGL ES
sumber
Sepertinya saya tidak dapat menggunakan contoh ini setelah melakukan refactoring ke AndroidX - konstruktor ke-3 tidak dapat dikompilasi lagi. Tahukah kamu apa yang salah?
Alon Shlider
3

Dalam file layout, setel elevasi atribut ke 0. karena dibutuhkan elevasi default.

app:elevation="0dp"

Sekarang dalam aktivitas, periksa level api yang lebih besar dari 21 dan setel elevasi jika perlu.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    fabBtn.setElevation(10.0f);
}
Vikram
sumber
Jika Anda menggunakan pustaka compat, gunakan setCompatElevation . Dan akan lebih baik menggunakan dips daripada piksel secara langsung.
ingkevin