Необходимость, существующие решения и вещи, которые следует помнить при создании собственного

Приложение Facebook для Android стало законодателем мод в одном отношении - нижней навигации. Они смело портировали его с iOS, и это имело смысл - у них было несколько уровней навигации. Мы столкнулись с этим около 20 месяцев назад во время мозгового штурма дизайна нашего продукта в Practo.

Затем появился TabLayout с набором раскладок координатора. Что приятно в нем, так это возможность разместить TabLayout, к которому прикреплен ваш пейджер просмотра, даже в нижней части экрана.
Единственная проблема, которую я вижу с TabLayout, - это глубоко вложенные фрагменты. Я считаю необходимость прикрепления фрагмента к окну просмотра принудительным шаблоном проектирования. Есть способы, которыми люди объяснили, как использовать viewpager без фрагментов, хотя, опять же, если вы KISS, вы, возможно, никогда не воспользуетесь ими.

TabLayout не решает нашу проблему разрешения нескольких уровней навигации, поскольку для этого вам потребуются вложения вьюпейджеры, что, в свою очередь, означает вложение фрагментов, возможно, до трех уровней. Я предпочитаю избегать вложенных фрагментов и вместо этого упростить дизайн по причинам, указанным здесь. Другая важная причина, не указанная в ссылке, для случая фрагментов - это ограничение, что CoordinatorLayout для прослушивания поведения прокрутки не может применяться в одном. Таким образом, вам потребуется согласовать действие с вашим координатором, чтобы сообщить ему, в каком фрагменте следует слушать прокрутку, а какой фрагмент прокрутки не слушать.

У Android теперь есть собственный BottomNavigationView, но многие приложения все еще его не используют. Создавая один из своих, я понял, что в нем нет нескольких незначительных функций, которые я считал необходимыми.

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

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

В списке сообщений на носителе, каждый из которых в основном текстовый, есть элементы с броским изображением и заголовком сообщения, который служит подписью к нему. Площадка для чтения, основанная на изображениях, чтобы привлечь внимание к имеющемуся текстовому содержанию. Я почти уверен, что люди, читающие это, должны были читать книги с обложкой без изображений. Дело в том, если вам нужно просмотреть список элементов, изображения облегчают их просмотр, чем текст (просмотр буквально означает беглый просмотр чего-либо). Это очевидно.

Возвращаясь к нашему BottomNavigationView, на момент написания этой статьи в API не было формальной поддержки вкладок без текста.

1.2 Программный выбор вкладки.

Хотя я изначально думал, что это проблема, каждый поймет, что если это станет проблемой, в вашем дизайне должен быть изъян. Нижняя панель навигации должна быть точкой входа в различные сегменты верхнего уровня приложения. Не следует ожидать, что пользователь перейдет из одного сегмента верхнего уровня в другой, щелкнув внутри сегмента, а не на панели навигации верхнего уровня. Такая «телепортация» может оставить у пользователей невежество и головокружение, как и любая другая телепортация. Давайте посмотрим на хороший пример для этого.

1.3 Drop Shadow

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

Если вы хотите жить с API BottomNavigationView, но при этом иметь тень, как и другие приложения, вы можете попробовать часть 2.3 ниже. Если вы вместо этого хотите использовать стороннюю библиотеку для того же, есть несколько хороших, которые вы можете найти в арсенале Android. Перечисление двух моих выборов:

Однако, если, возможно, ради обучения, вы хотите реализовать собственное нижнее навигационное представление - с тенью, есть несколько способов получить эту верхнюю тень.

2.1 Высота

Установив большую высоту, вы можете получить тень наверху. Проблема в том, что это, конечно, будет широко распространено.
Согласованность - это принцип дизайна, который действительно очень важен. Более важно, чем можно выделить курсивом. Если вы хотите быть последовательными, вы либо воспользуетесь руководством по материалу верхнего света, отбрасывающего тень, либо более заметной тенью, которая согласуется с тенью панели инструментов (как только панели инструментов также будет присвоена высота). Это означает, что нам нужно имитировать эту верхнюю тень, а не увеличивать высоту, чтобы рассеять ее. Рассмотрим два способа сделать это.

2.2 Использование просмотра карточек

Если вы поверните вид карты на 180 градусов (это легко сделать с помощью атрибута в самом макете xml) и поверните его родительский элемент также на 180 градусов, вы получите вид карты, отбрасывающий тень вверху точно так же, как раньше внизу. вращения. Это опять же проблема. Родитель Cardview должен разместить тень cardview, чтобы она отображалась. Это означает, что родительский элемент не привязан к границам представления карты, этому родительскому элементу необходимо предоставить нижний отступ.

Этот или следующий вариант не будет работать в LinearLayout, поскольку тень не отбрасывается на нижнюю поверхность, которую мы намеревались прокручивать ниже приподнятых поверхностей. Это потому, что LinearLayout размещает дочерние элементы… линейно, и поэтому даже с View.bringToFront () вы не можете заставить братьев и сестер взаимодействовать в LinearLayout. Я называю это теневым провалом.

Кто-то может попробовать этот подход CardView в родительском элементе FrameLayout, RelativeLayout или CoordinatorLayout и сообщить мне, хорошо ли он сработал для них.

2.3 Графический XML-файл для падающей тени

Просто выполните поиск по этому заголовку на SO, чтобы найти его реализации. Чтобы тень выглядела падающей на вид ниже, и во избежание отказа вышеупомянутой тени, установите градиент до альфа 0.

Изучив варианты реализации тени, давайте теперь рассмотрим некоторые вопросы технического проектирования полной реализации.

3.1 Одно действие или несколько действий?
Как обсуждалось ранее, в шаблоне с одним действием и несколькими фрагментами координация с координатором для различных фрагментов должна быть закодирована в действии. В основном функциональные возможности, относящиеся к фрагменту, находятся за пределами объекта фрагмента. С технической точки зрения это звучит не очень хорошо.

3.2 Как должна вести себя обратная навигация?
Если вы перейдете от одного раздела верхнего уровня к другому и нажмете назад, вы можете ожидать, что произойдет одно из двух.
а. Поскольку вы находитесь в разделе верхнего уровня, нажатие на кнопку может вывести вас из приложения.
б. Поскольку вы перешли из предыдущего раздела, нажатие назад может вернуть вас в этот раздел. Это звучит более интуитивно понятно, а также позволяет пользователю дольше оставаться в приложении.

Если мы хотим реализовать более интуитивно понятную навигацию при обратном нажатии 3.2 (b) в дизайне одного действия 3.1, мы должны добавить транзакцию фрагмента в backstack, чтобы позаботиться о обратном нажатии. НО нам также необходимо программно выбрать вкладку в настраиваемом режиме навигации. Как обсуждалось в 1.2, это нежелательно.

Если мы хотим реализовать дизайн нескольких действий 3.1 с выходом 3.2 (a) при навигации назад, следующий код будет работать хорошо.

@Override
public void onBackPressed() {
    ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
    for (ActivityManager.AppTask task : appTasks) {
        task.finishAndRemoveTask();
    }
}

Если мы хотим реализовать дизайн нескольких действий 3.1 с обработкой обратной навигации в 3.2 (b), мы должны начать действия с флага переупорядочения на передний план.

public static void start(Activity activity) {
    Intent intent = new Intent(activity.getApplicationContext(), HomeActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    activity.startActivity(intent);
}

Это выводит на передний план ранее запущенный экземпляр действия, поэтому его состояние остается неизменным. Вот как Instagram реализовал свою настраиваемую нижнюю навигацию - посмотрите, как переключение между вкладками и возврат к какой-либо вкладке меняет порядок навигации в ней при обратном нажатии. Например. Вкладка 1 - ›Вкладка 2 -› Вкладка 3 - ›Вкладка 2 -› Вкладка 4 приведет к вкладке 2 при первом нажатии, вкладка 3 при следующем и выходе из приложения при следующем. Это потому, что вкладка 2 перед вкладкой 3 была переупорядочена в заднем стеке, а вкладка 1 стала висящей родительской.

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