В прошлой статье мы рассматривали компоненты и виджеты библиотеки совместимости от Google, позволяющие «старым» (до 5.0) версиям Android прикидываться молодыми и стильными, примеряя меха Material Design'а. Однако, как ты уже знаешь, «корпорация добра» не сразу осчастливила программистов своими библиотеками и долгое время эта ниша оставалась незаполненной. Разумеется, нашлись добрые программисты, предложившие свои варианты реализации Material-компонентов для всех версий Android в соответствии с гайдлайнами Google. Сегодня мы рассмотрим некоторые опенсорсные проекты, проверенные временем, разработчиками и, конечно, пользователями приложений.

 

MaterialDrawer

Ссылка: https://github.com/mikepenz/MaterialDrawer

Автор: Mike Penz

MinSdkVersion: 10

Начнем, пожалуй, с самой известной среди Android-разработчиков библиотеки — MaterialDrawer. Как ясно из названия, она реализует виджет Navigation Drawer, и делает это просто и элегантно (смотри рисунок 1):

drawerResult = new Drawer()
    .withActivity(this)
    .withToolbar(toolbar)
    .withActionBarDrawerToggle(true)
    .withHeader(R.layout.drawer_header)
    .addDrawerItems(
         new PrimaryDrawerItem().withName("Элемент 1").withIcon(FontAwesome.Icon.faw_home).withBadge("99").withIdentifier(1),
         new PrimaryDrawerItem().withName("Элемент 2").withIcon(FontAwesome.Icon.faw_gamepad),
         …
         new SectionDrawerItem().withName("Новая секция"),
         new SecondaryDrawerItem().withName("Элемент 3").withIcon(FontAwesome.Icon.faw_cog),
         new SecondaryDrawerItem().withName("Элемент 4").withIcon(FontAwesome.Icon.faw_question).setEnabled(false),
         …
         new DividerDrawerItem(), // Разделитель
         new SecondaryDrawerItem().withName("Элемент 5").withIcon(FontAwesome.Icon.faw_github).withBadge("666+").withIdentifier(2)
    )
.build();

Метод withIcon определяет иконку слева, withBadge — тестовую метку справа (здесь, например, удобно отображать число новых сообщений или звонков), а setEnabled управляет доступностью элемента.

Рис. 1. Готовый Navigation Drawer за 5 минут
Рис. 1. Готовый Navigation Drawer за 5 минут

Обработка нажатий на элементы меню:

.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id, IDrawerItem drawerItem) {
        if (drawerItem instanceof Nameable) {
            // Выбран обычный элемент меню
        }

        if (drawerItem instanceof Badgeable) {
            // Выбран элемент с текстовой меткой (бейджем)
            drawerResult.updateBadge("+1", position); // Меняем значение
        }
    })
})

Обработкой длинного клика занимается метод withOnDrawerItemLongClickListener с обработчиком onItemLongClick.

В наличии поддержка разных аккаунтов с аватарками и быстрое переключение между ними. Также библиотека может похвастаться нехилым набором иконок для пунктов меню. В репозитории доступен подробнейший пример использования.

 

FloatingActionButton

Пожалуй, Floating Action Button — наиболее популярный объект реализации в сторонних библиотеках. Учитывая, что неплохая поддержка FAB появилась и у Google, большинство из них потеряло актуальность и перестало развиваться. Мы же рассмотрим библиотеку с функционалом, отсутствующим в Support Library от Google.

Ссылка: https://github.com/futuresimple/android-floating-action-button

Автор: Jerzy Chalupski

MinSdkVersion: 14

В Hangouts при нажатии на кнопку с «+» на заглавном экране открываются дополнительные Action-элементы с подписями. Рассматриваемая библиотека реализует подобный функционал (рисунок 2). Вся «магия» готовится в разметке активности или фрагмента:

<com.getbase.floatingactionbutton.FloatingActionsMenu
    android:id="@+id/multiple_actions"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    fab:fab_addButtonColorNormal="@color/white"
    fab:fab_addButtonColorPressed="@color/white_pressed"
    fab:fab_addButtonPlusIconColor="@color/half_black"
    fab:fab_labelStyle="@style/menu_labels_style">

    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/action_a"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        fab:fab_colorNormal="@color/white"
        fab:fab_title="Action A"
        fab:fab_colorPressed="@color/white_pressed"/>

    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/action_b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        fab:fab_colorNormal="@color/white"
        fab:fab_title="Action B"
        fab:fab_colorPressed="@color/white_pressed"/>

</com.getbase.floatingactionbutton.FloatingActionsMenu>

Элемент FloatingActionsMenu содержит две вложенные кнопки с подписями слева, появляющиеся при нажатии исходной. В коде же остается написать только обработчики нажатия:

final FloatingActionButton actionA = (FloatingActionButton) findViewById(R.id.action_a);
actionA.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        actionA.setTitle("Action A clicked");
    }
});
Рис. 2. Три выпадающих FAB
Рис. 2. Три выпадающих FAB

Доступен также форк этой библиотеки, рассчитанный на устройства с API 4.

Замечу, что данный элемент (дополнительные Action'ы, выпадающие из FAB) пока не является парадигмой Material Design'а, скорее, это эксперимент Google исключительно для Hangouts, что, кстати, было озвучено на Droidcon 2015.

 

DiscreteSeekBar

Ссылка: https://github.com/AnderWeb/discreteSeekBar

Автор: Gustavo Claramunt

MinSdkVersion: 7

Библиотека реализует компонент Discrete Slider, активно показывающий изменение прогресса в виде «капли» с текстовой меткой (рисунок 3). Для версий Android до 5.0 имитируется эффект «ряби» при нажатии на слайдер. Добавление компонента в разметку тривиально:

<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
    android:id="@+id/seekBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:dsb_min="2"
    app:dsb_max="15"
/>
Рис. 3. Слайдер и его «капля»
Рис. 3. Слайдер и его «капля»

Для написания кода доступны обработчики: onProgressChanged — при изменении прогресса, onStartTrackingTouch — при первом прикосновении, onStopTrackingTouch — при убирании пальца.

sb = (DiscreteSeekBar) view.findViewById(R.id.seekBar);
sb.setOnProgressChangeListener(new DiscreteSeekBar.OnProgressChangeListener() {
    @Override
    public void onProgressChanged(DiscreteSeekBar discreteSeekBar, int i, boolean b) {}

    @Override
    public void onStartTrackingTouch(DiscreteSeekBar discreteSeekBar) { }

    @Override
    public void onStopTrackingTouch(DiscreteSeekBar discreteSeekBar) {}
});

Кроме того, с помощью обширных свойств компонента можно менять внешний вид и поведение. Например, чтобы задать основные цвета (окантовки, капли и «ряби»), достаточно указать в разметке:

app:dsb_indicatorColor="@color/accent"
app:dsb_progressColor="@color/accent"
app:dsb_rippleColor="@color/divider"
 

Android-swipe-to-dismiss-undo

Ссылка: https://github.com/hudomju/android-swipe-to-dismiss-undo

Автор: Hugo Doménech Juárez

MinSdkVersion: 15

Данная библиотека реализует эффект свайпа для элементов списков ListView и ReсyclerView. После смахивания вместо элемента появляется один или несколько Action'ов, как правило, удаляющих элемент, но с возможностью отмены (рисунок 4).

Рис. 4. Отмена удаления элемента RecyclerView
Рис. 4. Отмена удаления элемента RecyclerView

Сокращенный вариант разметки элемента списка представлен ниже:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout…
    …
    <LinearLayout>
     <!-- Разметка элемента -->
    </LinearLayout>

    <LinearLayout…
        android:orientation="horizontal"
        android:visibility="gone"
        android:weightSum="3">
        <TextView…
            android:id="@+id/txt_deleted"
            android:gravity="center_vertical"
            android:text="@string/deleted">
        <TextView…
            android:id="@+id/txt_undo"
            android:gravity="center"
            android:text="@string/undo">
    </LinearLayout>
</FrameLayout>

Здесь к разметке добавляется невидимый слой LinearLayout (visibility="gone"), который появится после свайпа. В данном случае он содержит две тестовые метки — «Удалено» и «Отмена».

Очевидно, для отмены удаления элемента необходимо обработать нажатие на соответствующую метку:

final SwipeToDismissTouchListener<RecyclerViewAdapter> touchListener =
    new SwipeToDismissTouchListener<>(
        new RecyclerViewAdapter(recyclerView),
        new SwipeToDismissTouchListener.DismissCallbacks<RecyclerViewAdapter>() {
            @Override
            public boolean canDismiss(int position) {
                // Любой элемент можно удалить
                return true;
            }

            @Override
            public void onDismiss(RecyclerViewAdapter view, int position) {
                // Окончательное удаление элемента
                adapter.remove(position);
            }
        });

recyclerView.setOnTouchListener(touchListener);
recyclerView.setOnScrollListener((RecyclerView.OnScrollListener)touchListener.makeScrollListener());
recyclerView.addOnItemTouchListener(new SwipeableItemClickListener(this,
    new OnItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
            // Нажали "Отмена"?
            if (view.getId() == R.id.txt_undo) {
                // Отменяем удаление
                touchListener.undoPendingDismiss();
            } else {
                // Обрабатываем нажатие на элемент списка
                Toast.makeText(context, "Position " + position, LENGTH_SHORT).show();
            }
        }
    }));

Метод canDismiss() определяет, может ли быть удален элемент по индексу, а onDismiss() окончательно его удаляет. Для обработки нажатий используется специальный обработчик SwipeableItemClickListener, в методе onItemClick которого отлавливается нажатие на метку «Отмена» с последующим вызовом undoPendingDismiss().

Данная библиотека в силу простоты и лаконичности хорошо подходит для расширения функционала свайпов. Но есть небольшой подводный булыжник — пример из репозитория крашится при использовании RecyclerView, так как не находит обработчик onRequestDisallowInterceptTouchEvent. Фикс тривиален до неприличия:

public class FixSwipeableItemClickListener extends SwipeableItemClickListener {
    public FixSwipeableItemClickListener(Context context, com.hudomju.swipe.OnItemClickListener listener){
        super(context, listener);
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean b) { }
}

Теперь вместо SwipeableItemClickListener используем приведенный класс:

recyclerView.addOnItemTouchListener(new FixSwipeableItemClickListener(this,…

Возможно, когда ты прочтешь эти строки, данное исправление уже будет в репозитории проекта.

 

Custom-rating-bar

Ссылка: https://github.com/kanytu/custom-rating-bar

Автор: Pedro Oliveira

MinSdkVersion: 4

RatingBar можно по праву назвать самым печальным виджетом всего Android SDK. Казалось бы, паттерн взаимодействия с пользователем в виде выбора звездочек рейтинга интуитивно понятен, удобен и не требует от пользователя лишних телодвижений вроде необходимости печатать текст на экранной клавиатуре или выбирать из списка подходящий вариант… Но нет, стандартный компонент — это нечто!

Во-первых, он масштабируется чуть менее, чем никак, то есть если, например, вставить его в диалоговое окно, то на смартфоне с диагональю 4.65' в портретном режиме пять звезд уже не влезут и число автоматически уменьшится до четырех, потроллив и пользователя, и разработчика. Во-вторых, как подсказывает Stack Overflow, у RatingBar'а все-таки можно установить style="?android:attr/ratingBarStyleSmall", уменьшающий размер звездочек до… точек (!), что также озадачивает. В общем, использовать этот компонент в адаптивной разметке чрезвычайно неудобно, если вообще возможно.

Однако найти сторонние реализации RatingBar'a почему-то не так-то просто. Я остановился на готовом классе (именно классе, не библиотеке) — сustom-rating-bar, который нужно скопировать в проект вручную.

Виджет добавляется в разметку вполне стандартно (рисунок 5):

<com.poliveira.apps.CustomRatingBar
    app:maxStars="5"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:id="@+id/ratingBar"
    app:halfStars="false"/>
Рис. 5. Material RatingBar
Рис. 5. Material RatingBar

Размеры учитываются корректно, и указанная ширина layout_width="match_parent" гарантирует, что этот RatingBar впишется в отведенное для него место. Использовать также просто:

crb = (CustomRatingBar) dialog.findViewById(R.id.ratingBar);
crb.setOnScoreChanged(new CustomRatingBar.IRatingBarCallbacks() {
    @Override
    public void scoreChanged(float score) {
        // В переменной score содержится выбранное число звезд
    }
});

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

Кстати, в репозитории автора есть несколько других полезных классов, не пропусти.

 

Material Dialogs

Ссылка: https://github.com/afollestad/material-dialogs

Автор: Aidan Follestad

MinSdkVersion: 8

Данная библиотека специализируется исключительно на Material-диалогах (рисунок 6). Взору разработчиков представлены самые разнообразные варианты — простые (Basic), с кнопками (Action), со списком (List), с единственным (Single Choice) и множественным (Multi Choise) выбором элементов, с нестандартной разметкой и адаптером (Custom Adapter), с выбором цветов и темой оформления, с круговым и линейным прогрессом (Progress Bar). К сожалению, не хватает диалога выбора даты и времени.

Рис. 6. Так и хочется куда-нибудь нажать
Рис. 6. Так и хочется куда-нибудь нажать

В качестве примера рассмотрим вариант диалога с произвольным (нестандартным) содержимым:

boolean wrapInScrollView = true;
new MaterialDialog.Builder(this)
    .title(R.string.title)
    .customView(R.layout.custom_view, wrapInScrollView)
    .positiveText(R.string.positive)
    .callback(new MaterialDialog.ButtonCallback() {
        @Override
        public void onPositive(MaterialDialog dialog) {}
    });
    .show();

С помощью метода customView указывается xml-файл с необходимой разметкой (Layout), а логический параметр wrapInScrollView определяет, нужно ли поместить содержимое внутрь ScrollView для скроллинга на маленьком экране смартфона. Очевидно, что при использовании компонентов, изначально поддерживающих скроллинг (ListView или RecyclerView), значение нужно установить в false. Метод positiveText определяет заголовок так называемого положительного действия ("OK", "Yes", "Agree" и так далее), а callback задает обработчик нажатия действия — onPositive. По аналогии используются negativeText (onNegative) и neutralText (onNeutral).

Библиотека постоянно обновляется, в Google Play доступен полный пример.

 

Material

Ссылка: https://github.com/rey5137/material

Автор: Rey Pham

MinSdkVersion: 9

Выше мы познакомились с отдельными виджетами Material Design'а, теперь пришло время подключить тяжелую артиллерию. Material — библиотека, как говорится, на все случаи жизни. В ней есть все необходимые материальные виджеты — Navigation Drawer, круговые и линейные Progress Bar, разнообразные кнопки (FAB, с окантовкой и без), переключатели (Checkbox, Radio Button, Switch), слайдеры (Continuous, Discrete), выпадающие списки (Spinner), поля ввода с фильтрацией (Textfields), всплывающие информационные панели (Snackbars), разные виды диалогов (Simple, Choice, Multi Choice), поддерживающие нестандартную разметку (Custom Layout), а также компоненты выбора даты и времени. Библиотека поддерживает кастоматизацию всех элементов — цвета, темы, эффекта «ряби» при нажатии, анимации и тому подобного (рисунки 7–8).

Рис. 7. Кнопки
Рис. 7. Кнопки
Рис. 8. Переключатели
Рис. 8. Переключатели

Единственное, что откровенно не понравилось, — это слайдеры, которые настолько неохотно реагируют на прикосновения, что отбивают всякое желание их использовать. Но не беда — ранее мы уже упомянули Discrete Seekbar, его и советую взять на замену. Остальные компоненты работают без нареканий. Автор библиотеки на официальном форуме всегда отвечает на вопросы и подсказывает пути решения тех или иных проблем.

 

Material Design Android Library

Ссылка: https://github.com/navasmdc/MaterialDesignLibrary

Автор: Ivan Navas

MinSdkVersion: 8

Еще одна комплексная библиотека, но с меньшим числом виджетов. В наличии все виды кнопок (с поддержкой анимации), переключатели (Switch), слайдеры (Slider), индикаторы Progress Bar (круговые, линейные), панели Snackbar и простые диалоги (рисунки 9–10). Дополнительно реализован готовый диалог выбора цвета по значениям красного, зеленого, синего (R,G,B).

Рис. 9. Готовый пример
Рис. 9. Готовый пример
Рис. 10. Выбираем цвет
Рис. 10. Выбираем цвет

Если это все, что тебе нужно, — можешь смело использовать данный вариант, особенно для совсем уж старых устройств с Android 2.2 на борту.

Шрифт Roboto

В Material Design широко используется новый шрифт Roboto. Он не входит в библиотеку совместимости, но его можно использовать в предыдущих версиях Android с помощью библиотеки typerlib.

Добавляем в build.gradle зависимость:

dependencies {
    compile 'io.github.enzokie:typerlib:1.0.0'
}

Меняем шрифт:

txt = (TextView) findViewById(R.id.textView);
…
txt.setTypeface(Typer.set(this).getFont(Font.ROBOTO_THIN));
txt2.setTypeface(Typer.set(this).getFont(Font.ROBOTO_BLACK));
txt3.setTypeface(Typer.set(this).getFont(Font.ROBOTO_CONDENSED_ITALIC));

На рисунке 11 приведен пример использования нового шрифта. Нужно сказать, что разница не сильно заметна. Кроме того, не у всех элементов (например, стандартного заголовка) таким образом можно сменить шрифт.

Рис. 11. Найди 10 отличий
Рис. 11. Найди 10 отличий

 

Выводы

Сегодня мы рассмотрели ряд полезных библиотек, помогающих вдохнуть толику Material Design в ранние версии Андроида. Наверняка у тебя возник вопрос: стоит ли все это использовать или лучше остановиться на библиотеке совместимости Google? Однозначного ответа, естественно, не существует. Логика подсказывает, что как только тот или иной компонент появится в AppCompat или Design Support Library, необходимость в сторонней библиотеке отпадет сама собой. Впрочем, вряд ли стандартный компонент Navigation Drawer когда-нибудь сравнится по удобству работы с тем же MaterialDrawer Mike Penz. Лично я не вижу ничего криминального в использовании сторонних библиотек при условии, что они не противоречат гайдам Google и здравому смыслу.

WWW


В Google Play существуют приложения-сборники, наглядно демонстрирующие компоненты множества библиотек для разработчиков. Используя подобные сборки, можно сразу же оценить функциональность виджетов без скачивания и компиляции тестовых проектов.

Ищи по названиям — Libraries for Developers, API Demos for Android, Android Libraries.

1 комментарий

  1. Аватар

    Int

    04.04.2016 в 03:12

    Нафиг. Такой. Дизайн.
    Верните нам дизайнеров, которые что-то умеют, а не прячут свою бездарность за модой.

Оставить мнение