Полный список
— изучаем drawable теги: , ,
Продолжаем разбирать типы Drawable, которые можно описывать с помощью XML-разметки. Проектов в этом уроке создавать не будем. Я просто буду в своем проекте создавать XML-файлы в папке drawable и ставить их фоном для View. А в тексте урока приведу код и скрины. Иногда буду вешать дополнительно серый фон, чтобы был виден реальный размер View.
Чтобы программно добраться до Drawable, который вы для View повесили как фон, надо просто вызвать метод getBackground.
Bitmap
Тег позволяет получить Drawable обертку для Bitmap. У тега есть несколько атрибутов.
В атрибуте src указываем нужный нам файл-изображение.
Атрибут gravity указывает, как bitmap будет располагаться внутри Drawable. Можно использовать несколько значений, разделенных знаком | . Значения тут стандартные, и некоторые из них мы часто используем при работе с layout. Рассмотрим пример.
Значение атрибута gravity сдвигает изображение влево-вверх
Далее ставим следующие значение атрибута gravity:
fill_horizontal — растянуть по горизонтали
fill — растянуть (используется по умолчанию)
Насколько я понял, значения clip_vertical и clip_horizontal идентичны значениям fill_vertical и fill_horizontal в случае когда Bitmap по размеру больше, чем предоставляемое ему пространство. Т.е. clip_vertical сожмет его по вертикали, так чтобы он влез. А clip_horizontal — по горизонтали.
Атрибут tileMode — это режим «плитки». Позволяет замостить вашим изображением все доступное пространство. По умолчанию он имеет значение disabled.
Для примера я создам такой bitmap.
Четыре разных цвета, внутренние границы — сплошные, внешние — пунктиром.
Если tileMode = repeat, то Bitmap будет размножен и займет все доступное пространство
Далее меняем значение атрибута tileMode.
mirror – Bitmap также будет размножен, но при этом он будет чередоваться со своим отражением
clamp – растягивает края картинки на все свободное пространство
Прочие атрибуты тега :
antialias – сглаживание линий
dither – преобразование цветов, если текущей палитры недостаточно для отображения
filter – фильтр при сжатии или растягивании (пример результата использования есть в Уроке 158)
mipMap – использование mip-текстурирования. Про него можно почитать в википедии. Используйте этот режим, если планируете в процессе отображения уменьшать bitmap более чем в два раза.
Мы рассмотрели XML-описание, но вы всегда можете создать этот объект и программно. Java-реализация – класс BitmapDrawable.
Layer List
Мы можем описать Drawable, который будет состоять из нескольких Drawable-слоев. Для этого используется тег , а внутри него теги .
У нас 4 слоя. Три bitmap со стандартной иконкой и одна фигура. Атрибуты left, top, right, bottom позволяют указывать отступы. А в атрибуте id можно указать id этого Drawable-слоя.
Обратите внимание, что важен порядок тегов item. Каждый последующий слой рисуется поверх предыдущего. Например, на получившемся изображении видно, что прямоугольник проходит «над» верхней иконкой, но «под» нижней.
Мы можем в коде получать доступ к отдельным Drawable внутри LayerDrawable. Для этого сначала получаем LayerDrawable.
А затем вызываем метод findDrawableByLayerId(int id) и указываем id, который вы указывали в атрибуте id тега item. На выходе получим Drawable.
Также у LayerDrawable есть еще несколько интересных методов
getDrawable(int index) — возвращает Drawable по индексу, а не по id
getId(int index) — возвращает id по индексу
getNumberOfLayers() — возвращает кол-во Drawable-слоев
State List
Тег позволяет отображать Drawable в зависимости от состояния View. Возможные состояние View можно посмотреть в хелпе. Рассмотрим пример с двумя из них: checked и pressed. На экране будет ToogleButton. Эта кнопка переходит в состояние checked и обратно, если на нее нажимать. А во время нажатия, пока палец касается экрана, кнопка находится в состоянии pressed.
State List позволит нам использовать три разных Drawable для отображения кнопки в трех состояниях: обычное, checked, pressed. Для этого создадим три файла в папке drawable.
Прямоугольник темно-серого цвета. Этот Drawable будем отображать в обычном состоянии кнопки.
Прямоугольник темно-синего цвета. Этот Drawable будем отображать в нажатом состоянии кнопки.
Прямоугольник светло-синего цвета. Этот Drawable будем отображать когда кнопка находится в состоянии checked.
И еще один файл, button_selector.xml:
Этот последний Drawable является селектором. В нем мы используем теги item, в которых указываем для какого состояния какой Drawable использовать
В первом item мы указали state_pressed=true, а значит этот item будет выбран системой когда кнопка будет в состоянии pressed. И экране мы увидим Drawable из этого item, т.е. toogle_button_pressed.
В втором item мы указали state_checked=true, а значит этот item будет выбран системой когда кнопка будет в состоянии checked. И экране мы увидим toogle_button_checked.
В третьем item мы не указали никакого состояния, этот item будет выбран при обычном состоянии кнопки. И экране мы увидим toogle_button.
Учтите, что здесь важен порядок расположения item внутри selector. Т.е. система идет по ним по порядку и выбирает первый подходящий. Если вы третий item, который без явного указания состояния, поставите первым, то система всегда будет останавливаться на нем.
Состояния можно комбинировать, т.е. в одном item вы можете указать несколько разных состояний.
Ставим этот Drawable, как фон для ToogleButton:
В результате, сначала видим обычное состояние
Нажимаем и держим, т.е. состояние pressed
Отпускаем – включился checked
Еще раз нажмем-отпустим — выключится checked и будет снова обычное состояние. Для каждого состояния отображается свой Drawable.
У View, кстати, есть методы, которые позволяют программно управлять состоянием. Это, например: setPressed и setSelected.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Архитектура Android-приложений… Правильный путь?
От переводчика: Некоторые термины, которые использует автор, не имеют общепринятого перевода (ну, или я его не знаю:), поэтому я решил оставить большинство на языке оригинала — они всё равно понятны и для тех, кто пишет под android, но не знает английский.
Куда писать об ошибках и неточностях, вы знаете.
За последние несколько месяцев, а также после дискуссий на Tuenti с коллегами вроде @pedro_g_s и @flipper83 (кстати говоря, 2 крутых Android-разработчика), я решил, что имеет смысл написать заметку о проектировании Android-приложений.
Цель поста — немного рассказать о подходе к проектированию, который я продвигал в последние несколько месяцев, и также поделиться всем тем, что я узнал во время исследования и реализации этого подхода.
Приступая к работе
Мы знаем, что написание качественного программного обеспечения — сложное и многогранное занятие: программа должна не только соответствовать установленным требованиям, но и быть надёжной, удобной в сопровождении, тестируемой и достаточно гибкой для добавления или изменения функций. И здесь появляется понятие “стройной архитектуры”, которое неплохо бы держать в уме при разработке любого приложения.
Идея проста: стройная архитектура основывается на группе методов, реализующих системы, которые являются:
- Независимыми от фреймворков.
- Тестируемыми.
- Независимыми от UI.
- Независимыми от БД.
- Независимыми от любой внешней службы.
Кругов необязательно должно быть 4 (как на диаграмме), это просто схема; важно учитывать Правило Зависимостей: код должен иметь зависимости только во внутренние круги и не должен иметь никакого понятия, что происходит во внешних кругах.
Вот небольшой словарь терминов, необходимых для лучшего понимания данного подхода:
- Entities (Сущности): это бизнес-логика приложения.
- Use Cases (Методы использования): эти методы организуют поток данных в Entities и из них. Также их называют Interactors (Посредниками).
- Interface Adapters (Интерфейс-Адаптеры): этот набор адаптеров преобразовывает данные из формата, удобного для Методов использования и Сущностей. К этим адаптерами принадлежат Presenter-ы и Controller-ы.
- Frameworks and Drivers: место скопления деталей: UI, инструменты, фреймворки, БД и т. д.
Для лучшего понимания обратитесь к данной статье или к данному видео.
Наш сценарий
Я начал с простого, чтобы понять, как обстоят дела: создал простенькое приложение, которое отображает список друзей, который получает с облака, а при клике по другу отображает более детальную информацию о нём на новом экране.
Я оставлю здесь видео, чтобы вы поняли, о чем идёт речь:
Android-архитектура
Наша цель — разделение задач таким образом, чтобы бизнес-логика ничего не знала о внешнем мире, так, чтобы можно было тестировать её без никаких зависимостей и внешних элементов.
Для достижения этого я предлагаю разбить проект на 3 слоя, каждый из которых имеет свою цель и может работать независимо от остальных.
Стоит отметить, что каждый слой использует свою модель данных, таким образом можно достигнуть необходимой независимости (вы можете увидеть в коде, для выполнения трансформации данных необходим преобразователь данных (data mapper). Это вынужденная плата за то, чтобы модели внутри приложения не пересекались). Вот схема, как это выглядит:
Примечание: я не использовал внешних библиотек (кроме gson для парсинга json и junit, mockito, robolectric и espresso для тестирования) для того, чтобы пример был более наглядным. В любом случае, не стесняйтесь использовать ORM для хранения информации или любой dependency injection framework, да и вообще инструменты или библиотеки, которые делают вашу жизнь легче (помните: изобретать велосипед заново — не лучшая идея).
Presentation Layer (Слой представления)
Здесь логика связывается с Views (Представлениями) и происходят анимации. Это не что иное, как Model View Presenter (то есть, MVP), но вы можете использовать любой другой паттерн вроде MVC или MVVM. Я не буду вдаваться в детали, но fragments и activities — это всего лишь views, там нету никакой логики кроме логики UI и рендеринга этого самого отображения. Presenter-ы на этом слое связываются с interactors (посредниками), что предполагает работу в новом потоке (не в UI-потоке), и передачи через коллбэки информации, которая будет отображена во view.
Если вы хотите увидеть крутой пример эффективного Android UI, который использует MVP и MVVM, взгляните на пример реализации от моего друга Pedro Gómez.
Domain Layer (Слой бизнес-логики)
Вся логика реализована в этом слое. Рассматривая проект, вы увидите здесь реализацию interactor-ов (Use Cases — методы использования).
Этот слой — модуль на чистой джаве без никаких Android-зависимостей. Все внешние компоненты используют интерфейсы для связи с бизнес-объектами.
Data Layer (Слой данных)
Все данные, необходимые для приложения, поставляются из этого слоя через реализацию UserRepository (интерфейс находится в domain layer — слое бизнес-логики), который использует Repository Pattern со стратегией, которая, через фабрику, выбирает различные источники данных, в зависимости от определенных условий.
Например, для получения конкретного пользователя по id источником данных выбирается дисковый кэш, если пользователь уже загружен в него, в противном случае облаку отправляется запрос на получение данных для дальнейшего сохранения в тот же кэш.
Идея всего этого заключается в том, что происхождение данных является понятным для клиента, которого не волнует, поступают данные из памяти, кэша или облака, ему важно только то, что данные будут получены и доступны.
Примечание: что касается кода, помня, что код — это учебный пример, я реализовал очень простой, даже примитивный кэш, используя хранения shared preferences. Помните: НЕ СТОИТ ИЗОБРЕТАТЬ ВЕЛОСИПЕД, если существуют библиотеки, хорошо решающие поставленную задачу.
Обработка ошибок
Это большая тема, в которой всегда есть, что обсудить (и здесь автор предлагает делиться своими решениями). Что до моей реализации, я использовал коллбэки, которые, на случай, если что-то случается, скажем, в хранилище данных, имеют 2 метода: onResponse() и onError(). Последний инкапсулирует исключения во wrapper class (класс-обертку) под названием “ErrorBundle”: Такой подход сопряжен с некоторыми трудностями, потому что бывают цепочки обратных вызовов один за другим, пока ошибка не выходит на presentation layer, чтобы отобразиться. Читаемость кода из-за этого может быть немного нарушена.
С другой стороны, я реализовал систему event bus, которая бросает события, если что-то не так, но такое решение похоже на использование GOTO, и, по-моему, иногда, если не управлять событиями предельно внимательно, можно заблудиться в цепи событий, особенно когда их одновременно бросается несколько.
Тестирование
Что касается тестирования, я применил несколько решений, в зависимости от слоя.
- Presentation Layer: существующий android инструментал и espresso для интеграции и функционального тестирования.
- Domain Layer: JUnit + mockito использовались для юнит-тестов.
- Data Layer: Robolectric (так как этот слой имеет android-зависимости) + junit + для интеграции и юнит-тестов.
Покажите мне код
Думаю, в этом месте вам интересно посмотреть на код. Что ж, вот ссылка на github, где можно найти, что же у меня получилось. Что стоит упомянуть о структуре папок так это то, что разные слои представлены разными модулями:
- presentation: это android-модуль для presentation layer.
- domain: java-модуль без android-зависимостей.
- data: android-модуль, откуда поступают все данные.
- data-test: тесты для data layer. Из-за определённых ограничений, при использовании Robolectric мне пришлось использовать отдельный java-модуль.
Заключение
Дядя Боб сказал: “Архитектура — это намерения, а не фреймворки”, и я полностью с ним согласен. Конечно, есть разные способы реализовать какую-либо вещь, и я уверен, что вы, как и я, каждый день сталкиваетесь с трудностями в этой области, но используя данные приёмы, вы сможете быть уверены, что ваше приложение будет:
- Простым в поддержке.
- Простым в тестировании.
- Составлять единое целое,
- Будучи разделённым.
В заключение я настоятельно рекомендую вам попробовать эти методы, посмотреть на результат и поделиться своим опытом как в отношении этого, так любого другого подхода, который, по вашим наблюдениям, работает лучше: мы уверены, что постоянное совершенствование всегда полезно и положительно.
Источник