Android 디자인 지원 라이브러리 확장 가능 부동 작업 버튼 (FAB) 메뉴


172

Android 디자인 지원 라이브러리가 출시되었으므로받은 편지함 앱의 팹과 같이 확장 된 Fab 메뉴를 구현하는 방법을 아는 사람이 있습니까?

다음과 같이 보일 것입니다 :

여기에 이미지 설명을 입력하십시오



이미 모든 문서를 확인했지만 FAB 메뉴에 표시가없는 것 같습니다. (
EkKoZ

8
FloatingActionButton 라이브러리를 살펴볼 수 있습니다 .
Markus Rubey 2016 년

3
@MarkusRubey 덕분에 실제로 그 순간 사용하고있는 사람은 네이티브 인으로 만들고 싶었지만 아직 불가능한 것 같습니다.
EkKoZ

작업을 수행 할 수있는 많은 오픈 소스 라이브러리가 있습니다. 이 중 하나를 확인하십시오 : github.com/futuresimple/android-floating-action-button
capt.swag

답변:


143

현재 설계 라이브러리에는 위젯이 제공되지 않습니다. 빠르고 쉽게 할 수있는 유일한 방법 은 타사 라이브러리를 사용하는 것입니다.

분명히 디자인 라이브러리를 사용 하여이 작업을 수행 할 수는 있지만 엄청난 작업이며 많은 시간이 필요합니다. 나는 이것을 달성하는 데 도움이되는 유용한 라이브러리를 언급했다.

  1. 빠른 플로팅 버튼 (wangjiegulu)
  2. 플로팅 액션 버튼 (클랜)
  3. 플로팅 액션 버튼 (makovkastar)
  4. Android 플로팅 액션 버튼 (미래 단순)

나는 네 번째 것을 사용하고 있습니다.


31
글쎄, 아이디어는 기본 디자인 라이브러리로 수행하는 것이 었습니다.이 기능을 이미 수행하는 라이브러리가 많이 있다는 것을 알고 있습니다. 답장을 보내 주셔서 감사합니다.
EkKoZ

네 번째 문제와 관련하여 문제가 있습니까? lollipop + 기기에 광산이 표시되지 않습니다. 또한 배경 오버레이 색상과 텍스트가 작동하지 않습니다
Ajith Memana

@AjithMemana Nope. 내 앱은 1 만 명 이상의 사용자를 대상으로 제작되었으며 언급 한대로 문제가 없었습니다.
Aritra Roy

앱에 대한 링크를 제공해 주시겠습니까? 버튼을 클릭 할 때 화면을 덮는 반투명 배경과 함께 위에 표시된 것과 비슷한 것이 필요합니다.
Ajith Memana

2
참고로 클랜과 makovkastar는 더 이상 지원되지 않습니다. 나는 이것이 FAB 디자인 라이브러리의 개선으로 인해 의심
정직한 반대

162

라이브러리를 사용하지 않고 애니메이션 FAB 메뉴를 구현하거나 애니메이션을위한 거대한 xml 코드를 작성하는 더 좋은 방법이 있습니다. 이것이 간단한 구현 방법이 필요한 사람에게 미래에 도움이되기를 바랍니다.

animate().translationY()함수를 사용 하면 아래 코드에서 한 것처럼 모든 뷰를 위아래로 움직일 수 있습니다 .github에서 완전한 코드를 확인하십시오 . kotlin에서 동일한 코드를 찾고 있다면 kotlin code repo Animating FAB Menu를 확인하십시오 .

먼저 모든 FAB를 동일한 위치에 정의하여 서로 겹치도록하십시오. FAB은 클릭하고 다른 것을 표시해야한다는 점을 기억하십시오. 예 :

    <android.support.design.widget.FloatingActionButton
    android:id="@+id/fab3"
    android:layout_width="@dimen/standard_45"
    android:layout_height="@dimen/standard_45"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/standard_21"
    app:srcCompat="@android:drawable/ic_btn_speak_now" />

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab2"
    android:layout_width="@dimen/standard_45"
    android:layout_height="@dimen/standard_45"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/standard_21"
    app:srcCompat="@android:drawable/ic_menu_camera" />

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab1"
    android:layout_width="@dimen/standard_45"
    android:layout_height="@dimen/standard_45"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/standard_21"
    app:srcCompat="@android:drawable/ic_dialog_map" />

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/fab_margin"
    app:srcCompat="@android:drawable/ic_dialog_email" />

이제 자바 클래스에서 모든 FAB를 정의하고 아래와 같이 클릭을 수행하십시오.

 FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab1 = (FloatingActionButton) findViewById(R.id.fab1);
    fab2 = (FloatingActionButton) findViewById(R.id.fab2);
    fab3 = (FloatingActionButton) findViewById(R.id.fab3);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(!isFABOpen){
                showFABMenu();
            }else{
                closeFABMenu();
            }
        }
    });

를 사용 animation().translationY()하여 FAB에 애니메이션을 적용하십시오. int 만 사용하면 고해상도 또는 저해상도의 디스플레이 호환성에 영향을 미치므로 DP에서이 방법의 속성을 사용하는 것이 좋습니다. 아래 그림과 같이:

 private void showFABMenu(){
    isFABOpen=true;
    fab1.animate().translationY(-getResources().getDimension(R.dimen.standard_55));
    fab2.animate().translationY(-getResources().getDimension(R.dimen.standard_105));
    fab3.animate().translationY(-getResources().getDimension(R.dimen.standard_155));
}

private void closeFABMenu(){
    isFABOpen=false;
    fab1.animate().translationY(0);
    fab2.animate().translationY(0);
    fab3.animate().translationY(0);
}

이제 아래 표시된대로 res-> values-> dimens.xml 안에 위에서 언급 한 차원을 정의하십시오.

    <dimen name="standard_55">55dp</dimen>
<dimen name="standard_105">105dp</dimen>
<dimen name="standard_155">155dp</dimen>

이 솔루션이 간단한 솔루션을 찾고있는 미래의 사람들에게 도움이되기를 바랍니다.

편집

FAB에 레이블을 추가하려면 단순히 가로 LinearLayout을 가져 와서 텍스트보기가있는 레이블을 레이블로 사용 하고이 문제가있는 경우 레이아웃에 애니메이션을 적용하면 github에서 샘플 코드를 확인할 수 있습니다. 모든 하위 호환성을 처리했습니다. 해당 샘플 코드의 문제. Github에서 FABMenu에 대한 샘플 코드를 확인하십시오.

Backpress에서 FAB를 닫으려면 아래 표시된 것처럼 onBackPress ()를 재정의하십시오.

    @Override
public void onBackPressed() {
    if(!isFABOpen){
        this.super.onBackPressed();
    }else{
        closeFABMenu();
    }
}

스크린 샷에는 FAB과 제목이 있습니다 .Ingithub 샘플 앱 에서 가져 오기 때문입니다.

여기에 이미지 설명을 입력하십시오


8
메뉴에 레이블을 추가하는 것조차 간단합니다. 일부 텍스트 뷰를 레이블로 사용하여 Linearlayout 내부에 FAB를 추가하면됩니다. 그런 다음 전체 선형 레이아웃에 애니메이션을 적용하면 선형 레이아웃을 숨기고 표시하는 것을 잊어 버리지 않았습니다.
HAXM

1
그러나 prashant는 애니메이션을 위해 별도의 xml을 만들었지 만 내 솔루션에는 애니메이션을위한 추가 xml이 필요하지 않으므로 뷰에 애니메이션을 적용하기 위해 추가 코드 줄이 필요하지 않기 때문에 내 대답이 더 낫습니다.
HAXM

2
그게 가장 좋은 대답입니다. 디자인 라이브러리에서 실제 FAB를 사용할 수 있지만 그렇게 복잡하지는 않습니다.
스테판 매티스

3
이 접근법에 대해 내가 좋아했던 것은 우리가 안드로이드 FAB를 사용하고 있기 때문에 많은 기초가 이미 완료되었다는 것입니다. 예를 들어 라이브러리가 FAB를 확장하지 않았기 때문에 사용자 정의보기 동작을 처음부터 작성하는 대신 기본 fab 동작을 확장 할 수 있습니다. 이는 이미 사용 가능한 수많은 코드입니다.
RJFares

1
@droidHeaven은 프레임 레이아웃을 가지고 모든 FAB를 그 안에 넣습니다
HAXM

31
  • 먼저 Activity layout xml 파일에서 메뉴 레이아웃을 작성하십시오. 예를 들어 가로 방향의 선형 레이아웃의 경우 레이블에 TextView를 포함시킨 다음 TextView 옆에 부동 작업 단추를 포함하십시오.

  • 필요와 번호에 따라 메뉴 레이아웃을 만듭니다.

  • 기본 부동 동작 버튼을 만들고 클릭하면 메뉴 레이아웃의 가시성이 변경됩니다.

아래 코드를 참조하고 자세한 내용은 github에서 프로젝트를 확인하십시오.

Github의 결제 프로젝트

<android.support.constraint.ConstraintLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context="com.app.fabmenu.MainActivity">

            <android.support.design.widget.FloatingActionButton
                android:id="@+id/baseFloatingActionButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginRight="16dp"
                android:clickable="true"
                android:onClick="@{FabHandler::onBaseFabClick}"
                android:tint="@android:color/white"
                app:fabSize="normal"
                app:layout_constraintBottom_toBottomOf="@+id/activity_main"
                app:layout_constraintRight_toRightOf="@+id/activity_main"
                app:srcCompat="@drawable/ic_add_black_24dp" />

            <LinearLayout
                android:id="@+id/shareLayout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:layout_marginEnd="24dp"
                android:layout_marginRight="24dp"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:visibility="invisible"
                app:layout_constraintBottom_toTopOf="@+id/createLayout"
                app:layout_constraintLeft_toLeftOf="@+id/createLayout"
                app:layout_constraintRight_toRightOf="@+id/activity_main">

                <TextView
                    android:id="@+id/shareLabelTextView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="8dp"
                    android:layout_marginRight="8dp"
                    android:background="@drawable/shape_fab_label"
                    android:elevation="2dp"
                    android:fontFamily="sans-serif"
                    android:padding="5dip"
                    android:text="Share"
                    android:textColor="@android:color/white"
                    android:typeface="normal" />


                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/shareFab"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:clickable="true"
                    android:onClick="@{FabHandler::onShareFabClick}"
                    android:tint="@android:color/white"
                    app:fabSize="mini"
                    app:srcCompat="@drawable/ic_share_black_24dp" />

            </LinearLayout>

            <LinearLayout
                android:id="@+id/createLayout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="24dp"
                android:layout_marginEnd="24dp"
                android:layout_marginRight="24dp"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:visibility="invisible"
                app:layout_constraintBottom_toTopOf="@+id/baseFloatingActionButton"
                app:layout_constraintRight_toRightOf="@+id/activity_main">

                <TextView
                    android:id="@+id/createLabelTextView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="8dp"
                    android:layout_marginRight="8dp"
                    android:background="@drawable/shape_fab_label"
                    android:elevation="2dp"
                    android:fontFamily="sans-serif"
                    android:padding="5dip"
                    android:text="Create"
                    android:textColor="@android:color/white"
                    android:typeface="normal" />

                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/createFab"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:clickable="true"
                    android:onClick="@{FabHandler::onCreateFabClick}"
                    android:tint="@android:color/white"
                    app:fabSize="mini"
                    app:srcCompat="@drawable/ic_create_black_24dp" />

            </LinearLayout>

        </android.support.constraint.ConstraintLayout>

이것들은 애니메이션입니다.

FAB 메뉴의 애니메이션 열기 :

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
    android:duration="300"
    android:fromXScale="0"
    android:fromYScale="0"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="1"
    android:toYScale="1" />
<alpha
    android:duration="300"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />

</set>

FAB 메뉴의 애니메이션 닫기 :

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
    android:duration="300"
    android:fromXScale="1"
    android:fromYScale="1"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="0.0"
    android:toYScale="0.0" />
<alpha
    android:duration="300"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0.0" />
</set>

그런 다음 내 활동에서 위의 애니메이션을 사용하여 FAB 메뉴를 표시하고 숨겼습니다.

Fab 메뉴 표시 :

  private void expandFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(45.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabOpenAnimation);
    binding.shareLayout.startAnimation(fabOpenAnimation);
    binding.createFab.setClickable(true);
    binding.shareFab.setClickable(true);
    isFabMenuOpen = true;

}

Fab 메뉴 닫기 :

private void collapseFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(0.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabCloseAnimation);
    binding.shareLayout.startAnimation(fabCloseAnimation);
    binding.createFab.setClickable(false);
    binding.shareFab.setClickable(false);
    isFabMenuOpen = false;

}

다음은 활동 클래스입니다.

package com.app.fabmenu;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.OvershootInterpolator;

import com.app.fabmenu.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

private ActivityMainBinding binding;
private Animation fabOpenAnimation;
private Animation fabCloseAnimation;
private boolean isFabMenuOpen = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    binding = DataBindingUtil.setContentView(this,    R.layout.activity_main);
    binding.setFabHandler(new FabHandler());

    getAnimations();


}

private void getAnimations() {

    fabOpenAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_open);

    fabCloseAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_close);

}

private void expandFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(45.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabOpenAnimation);
    binding.shareLayout.startAnimation(fabOpenAnimation);
    binding.createFab.setClickable(true);
    binding.shareFab.setClickable(true);
    isFabMenuOpen = true;


}

private void collapseFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(0.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabCloseAnimation);
    binding.shareLayout.startAnimation(fabCloseAnimation);
    binding.createFab.setClickable(false);
    binding.shareFab.setClickable(false);
    isFabMenuOpen = false;

}


public class FabHandler {

    public void onBaseFabClick(View view) {

        if (isFabMenuOpen)
            collapseFabMenu();
        else
            expandFabMenu();


    }

    public void onCreateFabClick(View view) {

        Snackbar.make(binding.coordinatorLayout, "Create FAB tapped", Snackbar.LENGTH_SHORT).show();

    }

    public void onShareFabClick(View view) {

        Snackbar.make(binding.coordinatorLayout, "Share FAB tapped", Snackbar.LENGTH_SHORT).show();

    }


}

@Override
public void onBackPressed() {

    if (isFabMenuOpen)
        collapseFabMenu();
    else
        super.onBackPressed();
}
}

스크린 샷은 다음과 같습니다

Android의 새로운 필기체 글꼴 패밀리에서 레이블 텍스트보기가있는 플로팅 동작 메뉴

Android의 새로운 Roboto 글꼴 패밀리에서 레이블 텍스트보기가있는 플로팅 동작 메뉴



7

받은 편지함 부동 작업 버튼과 비슷한 것을 만들려고 할 때 자체 사용자 정의 구성 요소를 만드는 것에 대해 생각했습니다.

FAB 버튼이 포함 된 고정 높이 (확장 메뉴 포함)와 FAB 아래에 3 개가 추가 된 간단한 프레임 레이아웃입니다. FAB을 클릭하면 FAB 아래에서 다른 버튼 만 애니메이션으로 변환 할 수 있습니다.

이를 수행하는 라이브러리가 있지만 (예 : https://github.com/futuresimple/android-floating-action-button ) 직접 작성하면 항상 더 재미 있습니다.)


훌륭한 설명! 해당 라이브러리를 탐색 중이지만 두 뷰 사이에서 FAB를 정렬하는 데 라이브러리를 사용하는 데 문제가 있습니다. 이 질문에 무엇이 있는지 물어보십시오 stackoverflow.com/questions/24459352/… . 그것을하는 방법에 대한 아이디어가 있습니까? layout_anchor그리고 layout_anchorGravity나를 위해 작동하지 않습니다
acrespo

Futuresimple의 라이브러리는 기본적으로 FloatingActionMenu 요소에 기본적으로 추가되는 고유 한 아이콘을 허용하지 않습니다
Odaym

7

FloatingActionMenu 라이브러리를 사용하거나 단계별 자습서를 보려면 여기클릭하십시오 . 튜토리얼의 출력 :

여기에 이미지 설명을 입력하십시오


간단하고 잘 작동합니다. 나중에 링크가 다운 될 경우를 대비하여 튜토리얼을 링크하는 대신 여기에 세부 정보를 추가하는 것이 좋습니다. 또 다른 점은, 도서관이 더 이상 개발 단계에 있지 않다는 것입니다.
Ahmed salah

3

이 라이브러리를 사용 하여이 작업을 수행합니다 : https://github.com/futuresimple/android-floating-action-button

사용하기 매우 간단합니다.)

여기에 이미지 설명을 입력하십시오


다른 유사한 답변에 대한 의견도 있습니다. 두 뷰 사이에서 FAB를 정렬하는 데 사용하는 데 문제가 있습니다. 이 질문에 무엇이 있는지 물어보십시오 stackoverflow.com/questions/24459352/… . 그것을하는 방법에 대한 아이디어가 있습니까? layout_anchor그리고 layout_anchorGravity나를 위해 작동하지 않습니다
acrespo

0

ConstraintSet 애니메이션과 동일한 결과에 대한 또 다른 옵션 :

팹 애니메이션 예

1) 모든 애니메이션 뷰를 하나의 ConstraintLayout에 넣습니다.

2) 이와 같은 코드에서 애니메이션을 적용하십시오 (더 많은 효과를 원하는 경우 .. 이것은 단지 예일뿐입니다)

menuItem1menuItem2 는 메뉴의 첫 번째 및 두 번째 FAB이고 descriptionItem1descriptionItem2 는 메뉴 왼쪽에 대한 설명이고 parentConstraintLayout 은 루트 ConstraintLayout은 모든 애니메이션 된보기를 포함합니다. isMenuOpened 는 상태에서 열기 / 닫기 플래그를 변경하는 기능입니다.

애니메이션 파일을 확장 파일에 넣었지만 필수는 아닙니다.

fun FloatingActionButton.expandMenu(
    menuItem1: View,
    menuItem2: View,
    descriptionItem1: TextView,
    descriptionItem2: TextView,
    parentConstraintLayout: ConstraintLayout,
    isMenuOpened: (Boolean)-> Unit
) {
    val constraintSet = ConstraintSet()
    constraintSet.clone(parentConstraintLayout)

    constraintSet.setVisibility(descriptionItem1.id, View.VISIBLE)
    constraintSet.clear(menuItem1.id, ConstraintSet.TOP)
    constraintSet.connect(menuItem1.id, ConstraintSet.BOTTOM, this.id, ConstraintSet.TOP, 0)
    constraintSet.connect(menuItem1.id, ConstraintSet.START, this.id, ConstraintSet.START, 0)
    constraintSet.connect(menuItem1.id, ConstraintSet.END, this.id, ConstraintSet.END, 0)

    constraintSet.setVisibility(descriptionItem2.id, View.VISIBLE)
    constraintSet.clear(menuItem2.id, ConstraintSet.TOP)
    constraintSet.connect(menuItem2.id, ConstraintSet.BOTTOM, menuItem1.id, ConstraintSet.TOP, 0)
    constraintSet.connect(menuItem2.id, ConstraintSet.START, this.id, ConstraintSet.START, 0)
    constraintSet.connect(menuItem2.id, ConstraintSet.END, this.id, ConstraintSet.END, 0)

    val transition = AutoTransition()
    transition.duration = 150
    transition.interpolator = AccelerateInterpolator()

    transition.addListener(object: Transition.TransitionListener {
        override fun onTransitionEnd(p0: Transition) {
            isMenuOpened(true)
        }
        override fun onTransitionResume(p0: Transition) {}
        override fun onTransitionPause(p0: Transition) {}
        override fun onTransitionCancel(p0: Transition) {}
        override fun onTransitionStart(p0: Transition) {}
    })

    TransitionManager.beginDelayedTransition(parentConstraintLayout, transition)
    constraintSet.applyTo(parentConstraintLayout)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.