Статьи

Действия, продвигаемые Android: кнопка с плавающим действием (FAB)

Недавно в Android был представлен новый шаблон дизайна. Один из самых известных паттернов — продвигаемые действия . Это действия, которые непосредственно видны в пользовательском интерфейсе вместо использования кнопки панели действий; по этой причине эти действия называются повышенными, к ним можно легко получить доступ и определить основное действие в текущем пользовательском интерфейсе. Например, если мы используем приложение электронной почты и перечисляем папку «Входящие», одно продвигаемое действие может быть новым письмом. Визуальный шаблон называется Float Action Button (или FAB), потому что продвигаемое действие может быть представлено с помощью плавающей круглой кнопки в пользовательском интерфейсе.

Этот тип шаблонов дизайна доступен в Android L, но мы можем создать плавающую кнопку и в предыдущей версии Android. Предположим, у нас есть список элементов в нашем пользовательском интерфейсе, продвигаемое действие может быть «Добавить новый элемент», поэтому у нас может быть что-то вроде рисунка, показанного ниже:

android_floating_action_button_1 [4]

Есть несколько способов, которыми мы можем использовать, чтобы создать плавающую кнопку действия.

Плавающая кнопка действия с использованием ImageButton

Первый способ создания FAB — это использование ImageButton , мы просто добавляем ImageButton в наш макет пользовательского интерфейса и используем некоторые новые функции, предоставляемые Android L.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<ImageButton
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/plus"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:tint="@android:color/white"
android:id="@+id/fab"
android:elevation="1dp"
android:background="@drawable/ripple"
android:stateListAnimator="@anim/fab_anim"
/>

Если мы посмотрим выше, мы можем заметить, что это обычная кнопка ImageButton, которую мы обычно размещаем в правом нижнем углу. Есть некоторые аспекты, которые мы должны рассмотреть:

  • Фон
  • Тень
  • Анимация

Чтобы сделать нашу кнопку более «привлекательной», мы можем реализовать эффект фона. Если мы заметим, мы используем рисование пульсации, определенное следующим образом:

1
2
3
4
5
6
7
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
    <item>
        <shape android:shape="oval">
            <solid android:color="?android:colorAccent" />
        </shape>
    </item>
</ripple>

Мы сказали, что это плавающая кнопка, поэтому она должна плавать над данными пользовательского интерфейса, как если бы она находилась на другом уровне плоскости. Этот эффект можно получить с помощью атрибута высоты, который «перемещает» наш компонент пользовательского интерфейса по оси Z. Последнее — это эффект анимации при нажатии кнопки. Мы хотим дать пользователю ощущение, что кнопка тонет, когда мы нажимаем ее, поэтому мы должны увеличить тень вокруг нее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
    <item
        android:state_enabled="true"
        android:state_pressed="true">
        <objectAnimator
            android:duration="@android:integer/config_shortAnimTime"
            android:propertyName="translationZ"
            android:valueFrom="@dimen/start_z"
            android:valueTo="@dimen/end_z"
            android:valueType="floatType" />
    </item>
    <item>
        <objectAnimator
            android:duration="@android:integer/config_shortAnimTime"
            android:propertyName="translationZ"
            android:valueFrom="@dimen/end_z"
            android:valueTo="@dimen/start_z"
            android:valueType="floatType" />
    </item>
</selector>

Плавающая кнопка действия с использованием пользовательского компонента

Еще один способ создать нашу плавающую кнопку — использование пользовательского компонента. В этом случае мы не используем функцию Android L, но кодируем ее вручную.

Во-первых, мы создаем наш класс, который представляет пользовательский компонент:

1
2
3
public class CustomFAB extends ImageButton {
...
}

Следующим шагом является чтение некоторых пользовательских атрибутов, которые влияют на поведение пользовательского интерфейса, мы можем сделать это с помощью метода init:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private void init(AttributeSet attrSet) {
    Resources.Theme theme = ctx.getTheme();
    TypedArray arr = theme.obtainStyledAttributes(attrSet, R.styleable.FAB, 0, 0);
    try {
        setBgColor(arr.getColor(R.styleable.FAB_bg_color, Color.BLUE));
        setBgColorPressed(arr.getColor(R.styleable.FAB_bg_color_pressed, Color.GRAY));
        StateListDrawable sld = new StateListDrawable();
 
        sld.addState(new int[] {android.R.attr.state_pressed}, createButton(bgColorPressed));
        sld.addState(new int[] {}, createButton(bgColor));
        setBackground(sld);
    }
 
    catch(Throwable t) {}
    finally {
         arr.recycle();
    }
 
}

В то же время мы определяем эти атрибуты в файле XML:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FAB">
        <!-- Background color -->
        <attr name="bg_color" format="color|reference"/>
        <attr name="bg_color_pressed" format="color|reference"/>
    </declare-styleable>
</resources>

Если вы заметили, у нас есть атрибут, управляющий цветом фона. В строке 8 мы определяем несколько состояний кнопки, используя StateListDrawable, и для каждого состояния мы создаем нашу кнопку:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private Drawable createButton(int color) {
    OvalShape oShape = new OvalShape();
    ShapeDrawable sd = new ShapeDrawable(oShape);
    setWillNotDraw(false);
    sd.getPaint().setColor(color);
 
    OvalShape oShape1 = new OvalShape();
    ShapeDrawable sd1 = new ShapeDrawable(oShape);
 
    sd1.setShaderFactory(new ShapeDrawable.ShaderFactory() {
        @Override
        public Shader resize(int width, int height) {
            LinearGradient lg = new LinearGradient(0,0,0, height,
                    new int[] {
                            Color.WHITE,
                            Color.GRAY,
                            Color.DKGRAY,
                            Color.BLACK
                    }, null, Shader.TileMode.REPEAT);
 
            return lg;
        }
    });
 
    LayerDrawable ld = new LayerDrawable(new Drawable[] { sd1, sd });
    ld.setLayerInset(0, 5, 5, 0, 0);
    ld.setLayerInset(1, 0, 0, 5, 5);
 
    return ld;
}

Наконец, мы можем добавить этот компонент в наш макет:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MyActivity">
...
 
    <com.survivingwithandroid.fab.CustomFAB
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:src="@android:drawable/ic_input_add"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        custom:bg_color="@color/light_blue"
        android:tint="@android:color/white"
     />
      
</RelativeLayout>
  • Доступен исходный код @ github