Android 디자인 라이브러리-플로팅 작업 버튼 패딩 / 여백 문제


109

Google 디자인 라이브러리의 새로운 FloatingActionButton을 사용하고 있는데 이상한 패딩 / 여백 문제가 발생합니다. 이 이미지 (개발자 레이아웃 옵션 사용)는 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가 패딩 / 여백이 훨씬 더 큰 이유는 무엇입니까?


1
나는 CoordinatorLayout그것을 수직으로 정렬하고 여분의 패딩 프리 롤리팝을 눈으로 보는 데 사용 하는 것이 좋습니다 . 디 컴파일 된 FAB 소스에서 알아낼 수는 있지만 Google이 CardView.
DariusL

답변:


129

업데이트 (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에서는 뷰 너비에 영향을주지 않는 새로운 Elevation 매개 변수를 사용합니다.


19
buggy Android .. :(
Abdullah Shoaib

3
터무니 추한 보이지만, 다른 해결책이없는 것 같다
열한

3
좋은 대답입니다. 감사합니다. 편집하고 태블릿에도 스타일을 추가하도록 제안해도됩니까? API> = 20의 경우 여백은 24dp입니다. API <20의 경우 <item name = "android : layout_marginRight"> 12dp </ item> <item name = "android : layout_marginBottom"> 6dp </ item>이 필요하다는 것을 알았습니다 . 내 앱에 필요했기 때문에 직접 계산해야했습니다. 스타일 대신 dimens 리소스를 사용할 수도 있습니다 .
JJ86

여백 번호는 elevationFAB에서 설정 한 내용에 따라 다릅니다 .
Jarett Millard

사용app:useCompatPadding="true"
Kishan Vaghela

51

더 이상 파일을 다루styles.xml 거나 다룰 필요없습니다.java . 간단하게 만들겠습니다.

app:useCompatPadding="true"사용자 지정 여백을 사용 및 제거 하여 여러 버전의 Android에서 동일한 여백 을 유지할 수 있습니다.

당신이 당신의 두 번째 그림에 FAB에서 본 여분의 여백 / 패딩으로 인해이이다 compatPadding사전 롤리팝 장치. 이 속성을 설정하지 않으면 롤리팝 + 장치가 아닌 사전 롤로 팝 장치 에 적용됩니다 .

안드로이드 스튜디오 코드

개념의 증거

디자인 뷰


. 공장에 대한 "진실 : 부모 레이아웃이 카드보기 경우 우리는"useCompatPadding = card_view "사용할 필요가
아 미트 Tumkur

이것은 지원 라이브러리를 버전 23.2로 업그레이드 한 후에 저에게 효과적입니다. 감사!
Pablo

PoC에 마진이 있습니다.
Pedro Oliveira

@PedroOliveira 예, 모든 Android 버전에서 동일한 마진을 볼 수 있습니다. 실제로 목표입니다.
Saravanabalagi Ramachandran

1
사용자 정의 여백을 제거하기 위해 "xxx"를 사용할 수 있지만 개념 증명은 OP가 이유를 묻는 것처럼 모든 여백을 보여줍니다.
Pedro Oliveira

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는 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://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받는 캐스트 할 수없는 LayoutParamsFloatingActionButton와 나는 사전 롤리팝에 16dp의 여백이 표시되지 않습니다. FloatingActionButtonpre Lollipop의 너비 는 56dp가 아닌 84dp이고 높이는 98dp입니다.
Markus Rubey

@MarkusRubey 사전 롤리팝 및 롤리팝 버전을 표시하는 작업 예제와 이미지로 답변을 업데이트 할 것입니다.
Eugene H

1
@MarkusRubey- is in 유형의 View.getLayoutParamsa LayoutParams를 반환합니다 .Eugene View의 경우 RelativeLayout.LayoutParams.
JohnnyLambada

1
@EugeneH RelativeLayout.LayoutParams를 ViewGroup.MarginLayoutParams로 변경했습니다. 이렇게하면 더 많은 경우에서 코드가 작동하고 MarkusRubey가 제기하는 문제가 해결됩니다.
JohnnyLambada

8

On 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 dimens의 이름을 다음과 같이 변경했습니다.

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

방금 내 프로젝트를 AndroidX로 리팩터링했는데 예제에서 세 번째 생성자를 만들 수 없습니다. AndroidX 라이브러리를 사용하여 어떻게 할 수 있습니까?
Alon Shlider

5

Markus의 답변 은 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로 리팩토링 한 후이 예제를 사용할 수없는 것 같습니다. 세 번째 생성자는 더 이상 컴파일되지 않습니다. 무엇이 잘못되었는지 알고 있습니까?
Alon Shlider

3

레이아웃 파일에서 속성 고도를 0으로 설정합니다. 기본 고도를 사용하기 때문입니다.

app:elevation="0dp"

이제 활동에서 21보다 큰 API 레벨을 확인하고 필요한 경우 고도를 설정하십시오.

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

compat 라이브러리를 사용하는 경우 setCompatElevation을 사용하십시오 . 그리고 픽셀 대신 딥을 직접 사용하는 것이 좋습니다.
ingkevin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.