Мобайл: дизайн и разработка – Telegram
Мобайл: дизайн и разработка
474 subscribers
8 photos
158 links
Android-разработчик и дизайнер делится своим опытом, полезными инструментами, выжимками статей и докладов.
Download Telegram
🚀 switchMap

Returns a new ObservableSource by applying a function that you supply to each item emitted by the source ObservableSource that returns an ObservableSource, and then emitting the items emitted by the most recently emitted of these ObservableSources.

Да, я тоже сначала ничего не понял. @gazedash посоветовал посмотреть это видео, чтобы разобраться, как работает switchMap.

Объясняю на примере:

1. NumbersObservable каждую секунду выдаёт новое число
2. switchMap на каждое число создаёт PowersObservable, которая начинает выдавать степени этого числа
3. Как только NumbersObservable выдаёт новое число, switchMap отписывается от PowersObservable предыдущего числа, и она перестаёт выдавать степени. Вместо этого создаётся PowersObservable уже для нового числа.
4. Все степени, который получаются на выходе у созданных PowersObservable, сливаются в одну итоговую Observable (это называется flattening).

Пример использования switchMap — синхронизация субтитров с видео (https://github.com/mgp/effective-rxjava/blob/master/items/understand-switch-map.md#an-example-trannoscript-highlighting).

Если после моего объяснения всё ещё непонятно — посмотрите видео. Там про switchMap рассказывают наглядно и на очень простом примере.
​​Сегодня в 20:00 по Москве начнётся WWDC 2018.

Ждём iOS-приложения на маках, замену Macbook Air и обновления Xcode, отдельно хочется увидеть усовершенствованные CoreML и ARKit.

На канале я рассказывал о конференциях в двух форматах — прямая трансляция во время кейноута и посты с кратким обзором новинок. Признаюсь, последний формат я попробовал в этом году, потому что вспомнил о Google I/O уже после того, как она началась. Сегодня хочу узнать у вас, какой формат вам удобнее — проголосуйте в опросе ниже, пожалуйста.
Провести трансляцию WWDC или написать пост по итогам?
anonymous poll

⭐️ Большой пост с новинками – 61
👍👍👍👍👍👍👍 78%

🔥 Трансляция – 17
👍👍 22%

👥 78 people voted so far.
В этом году Apple сосредоточились на улучшении своих операционных систем, поэтому для разработчиков нововведений немного:

- maсOS теперь частично поддерживает UIKit — фреймворк для разработки iOS приложений. Портировать приложения с одной платформы на другую можно будет в 2019 году. И нет, эти две системы не собираются объединять.
- Новые инструменты для создания приложений с тёмной темой в Xcode 10
- Swift 4.2 собирает большие приложения в 2 раза быстрее, чем Swift 4.0
- ARKit 2 сохраненяет состояние сцены между разными сеансами использования — можно изменить объект в AR, закрыть приложение, и изменения восстановятся при его запуске
- Поддежрка мультиплеера в AR
- usdz — единый формат для 3D-файлов в iOS
- SiriKit и Shortcuts — позволяет создавать цепочки действий в сторонних приложениях, которые активируются командой, которую задаёт пользователь.
Составил конспект доклада про принципы дизайна интерфейсов, которым следует Apple. Получилось довольно объемно, но там есть вещи, на которые стоит обратить внимание.

Разработчикам cоветую глянуть с 41:05 по 47:50. Там на примере показывают, как работать с ускорением, чтобы перемещать элементы туда, куда ожидает пользователь.

Будет здорово, если вы оставите фидбек реакцией к этому посту. Когда перевожу что-то с английского, текст иногда кажется бездушным. В этот раз старался передавать смысл своими словами, и вроде получилось лучше, чем обычно.
Ставьте лайк, если приятно читать, покерфейс — если некоторые моменты кажутся невнятными, и кучку, если мне есть над чем работать.

Конспект: http://telegra.ph/Designing-Fluid-Interfaces-konspekt-06-09

Продублировал в Notion, потому что Telegraph работает не у всех: https://www.notion.so/Designing-Fluid-Interfaces-a769ed4257354131a67e75e3c2fdba45

Видео: https://developer.apple.com/videos/play/wwdc2018/803/
Сделал заметки по ещё одному докладу, на этот раз про работу с текстом в Android.

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

Конспект в Notion: https://www.notion.so/Best-practices-for-text-on-Android-bbb299ce95694385b6306c96074dafe3
Недавно мне понадобилось создать версию приложения, где данные загружались бы не с сервера, а из JSON-файла, который хранился в ассетах приложения. Класс, который отдаёт данные (сервис), я создаю в одном из Dagger-модулей. Новичкам покажется очевидным, что надо создать новый flavor приложения, создать модуль-наследник и переоределить в нём метод-провайдер этого сервиса. Но проблема в том, что наследоваться от Dagger-модулей нельзя.

Представим, что у нас есть класс App, в котором мы определяем наши Dagger-компоненты, и mock flavor приложения Мы создаём MockApp, который наследуется от App, и переопределяем нужный компонент с новым модулем. Я решил пойти этим путём, но тут появилась новая проблема. Основному сервису нужен Retrofit, а mock-сервису — Gson и Context. Мы уже не сможем провайдить mock-сервиc вместо основного. С точки зрения Dagger это две разные зависимости, и использоваться будет только первая.

Я пошел гуглить и нашёл ответ на StackOverflow, где советовали создать mock и production версии приложения и поместить туда соответствующие модули. Тогда подходящий сервис будет попадать в основной граф зависимостей из выбранного flavor'а .

Но на этом месте я остановился и задумался. У меня проект с парой экранов, но компонентов и модулей уже слишком много, а классы обвешаны кучей аннотаций. Нужно было заменить один сервис, и из-за этого пришлось усложнять этот механизм ещё сильнее. Тогда я решился на отчаянный шаг — сменить фреймворк для dependency injection за неделю до релиза. Спойлер: я об этом не пожалел.

Выбор пал на Koin, про который я уже писал. Его главное преимущество — простота. Я с большим удовольствием удалил кучу классов и заменил их несколькими десятками строк кода.

Чтобы переопределить зависимость, достаточно добавить провайдер с её заменой. Например, у себя я добавляю MockModule, и сервис с тестовыми данными заменяет собой сервис с реальными данными.

В чем отличия от Dagger:

- В Koin нет компонентов, сабкомпонентов и скоупов. Я для себя не нашёл в этом минусов, а плюс — очень простое управление зависимостями. Для маленького проекта это подходит идеально.
- Граф зависимостей Koin можно проверить юнит-тестом, не собирая приложение.
- Все зависимости надо объявлять вручную, так как Koin не генерирует код.

Очень рекомендую этот фреймворк, особенно если вы только начинаете осваивать dependency injection. А если вы уже профи и вам нужно больше возможностей, посмотрите на Kodein.

Код приложения, о котором я говорил, можно посмотреть здесь:
https://github.com/Kondenko/pocketwaka
Стоило мне написать, что в Koin нет скоупов, как их завезли в первой бета-версии этого фреймворка. Скоупы нужны для тех зависимостей, где мы используем элементы Android-фреймворка — например, контекст. Теперь мы можем пересоздать модули вместе с активити или фрагментом и избежать утечек памяти.

Вместе с этим появился способ решить ещё одну проблему. Когда вы создаёте зависимость, которой нужны много других зависимостей, приходится писать что-то наподобие MyPresenter(get(), get(), get(), get(), get()). Такая конструкция нужна, чтобы Koin нашел нужные зависимости и подставил их в подходящие места. Плагин koin-reflect использует рефлексию (которой нет в основном фреймворке), чтобы зависимость можно было создать одним выражением: build<MyPresenter>().

Все изменения и фичи бета-версии: https://medium.com/koin-developers/opening-the-koin-1-0-0-beta-version-99cb8be1c308

Документация по скоупам: https://beta.insert-koin.io/docs/1.0/documentation/reference/index.html#\_scope\_features\_for\_android
Когда нам нужно передавать какие-то события из фрагмента его родителю, приходится делать каст Context к нужному интерфейсу в методе onAttach, а в onDetach обнулять ссылку на него, чтобы избежать утечек памяти:

override fun onAttach(context: Context) {
super.onAttach(context)
recipeClickListener = context as RecipeClickListener
}

override fun onDetach() {
super.onDetach()
recipeClickListener = null
}

Автор этой статьи показывает, как обернуть такую логику в делегат и упростить работу с listener'ами до одной строчки:

private val recipeClickListener: RecipeClickListener by ParentActivityDelegate(this)

За жизненным циклом мы следим внутри самого делегата с помощью LifecycleObserver или RxLifecycle. Благодаря им делегат отвечает за хранение ссылки на нужный контекст и её обнуление.

У этого подхода есть минусы:

- Значение присваивается полю с listener'ом после вызова onCreate и обнуляется перед вызовом onDestroy. То есть, мы не можем использовать его в этих методах.
- Если мы используем LifecycleObserver, у нас нет доступа к onAttach/onDetach.
- Если мы используем RxLifecycle, нам придётся добавить костыли, чтобы получить реактивный поток lifecycle-ивентов.

Реализация: https://gist.github.com/premnirmal/2968e7f83892cdb3bfd8dc6db2581931#file-fragmentdelegates-kt

Статья: https://blog.blueapron.io/delegate-your-lifecycle-to-kotlin-17c1d0d876c9
7 принципов хороших тестов

Выписал главное из неплохой статьи про тестирование. Себе взял на заметку то, что нужно реже мокать data-классы и по возможности писать функции, которые упрощают тест-кейсы. А если вы всё ещё не тестируете код, самое время начать. 😉 Недавно я покрывал свой проект тестами и в процессе обнаружил серьёзную логическую ошибку, которая проявила бы себя только в продакшене, внезапно сломав авторизацию в приложении.

Итак, хорошие тесты:

Быстрые

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

Чтобы ускорить их выполнение:

— Не полагайтесь на Robolectric и PowerMock

— Реже используйте Mockito. Мокать data-классы — явный оверкилл. От себя посоветую библиотеку Pojo Data Mocker, которая заполняет data-классы рандомными данными. Если вам не нужны реальные данные, она очень выручает.

Независимые

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

Исчерпывающие

Предусмотрите все возможные исходы работы кода. На каждое условие в тестируемом методе напишите тест, который покроет срабатывание этого условия.

Воспроизводимые

Тесты должны выдавать один и тот же результат, если запустить их несколько раз подряд. Чтобы этого добиться, не полагайтесь на вещи, состояние которых не зависит от теста — к примеру, сеть или база данных. Замокайте всё, что заставляет тест вести себя непредсказуемо.

Профессиональные

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

Читаемые

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

Автоматизированные

Тесты нужно прогонять часто и регулярно. Чтобы автоматизировать их запуск, можно использовать систему continuous integration, которая будет запускать тесты каждый раз при попытке влить код в ветки master или develop.

https://www.activecampaign.com/blog/inside-activecampaign/seven-principles-great-unit-tests-android/
26 правил цифровой типографики для начинающих

Из этой статьи я узнал много вещей, которые хотел бы знать, когда только начинал заниматься дизайном:

- Когда подбираете шрифтовую пару, используйте комбинацию Serif и Sans Serif (с засечками и без засечек). Высота шрифтов в паре должна быть примерно одинаковой.
- Оптимальный размер текста — 14–18 пикселей.
- А длина строки — 45–75 символов.
- Высота строки — 150% от размера текста.
- Не выделяйте текст капсом. Используйте bold или italic и придерживайтесь выбранного стиля везде в тексте.
- Последний пункт призывает писать числа словами, но в комментах есть более разумная мысль. Пишите словами числа от одного до десяти, и числами — всё, что больше.

Остальные 20 советов про то, как не косячить и выдавать читабельный текст — в статье:

🇺🇸 https://medium.com/product-design-ux-ui/26-digital-typography-rules-for-beginners-a04c6a5aaff3

🇷🇺 http://deadsign.ru/tipografika/26\_typography\_rules/

#дизайн #типографика
——
Я всё время забывал добавлять теги к постам. Сегодня добавил напоминание о тегах в шаблон поста в Notion, теперь точно не буду забывать. 👌
​​Разработчик и дизайнер из Google рассказывает, как делали анимацию в приложении последней Google I/O.

Внутри четыре кейса:

- Анимация обратного отсчета, сделанная с помощью Lottie и кастомной логики переходов между цифрами
- Анимированные переходы между разными состояниями кнопки
- Переход между двумя экранами с общим элементом
- Анимированные фильтры

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

https://medium.com/androiddevelopers/animating-on-a-schedule-8a90d812ae4

#разработка #анимации
Airbnb представили Android-фреймворк MvRx ("мáверикс"). Он помогает избавиться от бойлерплейта и берет на себя работу со стейтом и жизненным циклом. По словам разработчиков, фреймворк сокращает код на 50-75%. MvRx заточен на работу с Kotlin и использует Architecture Components от Google.

В его основе лежат 4 сущности:

State — неизменяемый класс с данными, которые рендерит View

ViewModel — содержит бизнес-логику и хранит стейт

View — LifecycleOwner, который подписывается на изменения стейта

Async — класс, который отражает результат работы Observable (Uninitialized, Loading, Success, или Fail). Когда ViewModel уничтожается, механизм Async он автоматически отписывает ее от всех используемых Observable.

Здесь можно почитать, как пользоваться MvRx и как он устроен:
https://github.com/airbnb/MvRx/wiki

#разработка #архитектура #фреймворк
Привет 👏

Кто-то опять не пишет в канал из-за того, что много работает и не умеет в тайм-менеджмент. Делюсь интересными штуками, пока есть свободная минутка:

1. Подписчик делится классным инструментом

Мы с командой на днях выпустили SDK для мобильных разработчиков — CleverPay.

CleverPay берет на себя все сложности работы с инаппами. Настройка валидации, кроссплатформенность покупок, безопасность и т.д. Все из коробки, и интеграция занимает часы, а не месяцы, как обычно. Поддерживаются Swift/Objective-C и Java/Kotlin.
https://cleverpay.io/developer/

2. В блоге Тобиаса ван Шнайдера вышла заметка про то, как улучшить взаимодействие разработчиков и дизайнеров.
http://www.vanschneider.com/10-tips-make-designers-developers-excel-team

3. AndroidX, замена SupportLibrary, вышел из беты
https://developer.android.com/topic/libraries/support-library/androidx-rn?linkId=57203177

4. Рассказ о Fabulous — приложении, которое помогает заводить полезные привычки. Как аккуратно и эффективно менять поведение пользователей.
https://phase.com/magazine/story-behind-the-fabulous-app/

5. Если во время загрузки картинки с помощью Picasso у пользователя пропадет интернет, вы получите не сообщение об ошибке, а наполовину черное изображение. Выход — использовать Glide или Universal Image Loader с костылями
¯\_(ツ)_/¯
https://proandroiddev.com/using-picasso-watch-out-for-half-black-images-957bc9550888
Классный обзор библиотеки Cicerone, которая помогает вынести логику навигации в отдельный компонент (например, презентер), не завязываясь при этом на Context.

Пользоваться ей просто:

1. Регистрируем Router в наследнике класса Application. Router переводит вызовы методов навигации в сущности Command.
2. Создаём и регистрируем Navigator’ы для нужных экранов. Они реагируют на поступающие Command и открывают активити или фрагменты.
3. В презентере вызываем App.instance.router.forward/back/backTo/replace(screenKey, transitionData).

https://habr.com/company/mobileup/blog/314838/

#разработка #инструмент
18 октября в московском офисе Яндекса пройдет митап Droid Party.

Доклады такие:

- «Сделайте погромче», или Аудио-приложения с точки зрения системы
- История внедрения VectorDrawable в Яндекс.Карты
- Работа с большим объёмом данных в Map Kit

Я тоже там буду, так что подходите пообщаться, особенно если мы вместе участвовали в Мобилизации 2017 ✌️

Регистрация: https://events.yandex.ru/events/meetings/18-oct-2018-1

#разработка #митап
Небольшая заметка про то, как использовать Architecture Components и не косячить:

1. Когда подписываетесь на обновления LiveData во фрагменте, передавайте в метод observe не сам фрагмент (this), а viewLifecycleOwner. Это предотвратит создание новых Observer'ов при пересоздании view фрагмента, если сам фрагмент сохраняется во время смены конфигурации.

2. Загружайте данные для экрана не в методе onCreate/onCreateView, а в конструкторе ViewModel. Благодаря этому данные не будут загружаться заново каждый раз при смене конфигурации.

3. Если вы передаете ссылку на ViewModel объекту, который живет дольше, обнуляйте её в методе onCleared, чтобы Garbage Collector смог её уничтожить.

4. Убедитесь, что View из ViewModel получают LiveData, а не на MutableLiveData. View не должны изменять данные в LiveData.

5. Передавайте во ViewModelProvider не сами зависимости, а их провайдеры (class MyViewModelFactory(private val repository: Provider<MyRepository>)). Тогда зависимости не будут создаваться каждый раз, когда ViewModelFactory пересоздается при смене конфигурации.

https://proandroiddev.com/5-common-mistakes-when-using-architecture-components-403e9899f4cb

#разработка #architecturecomponents
Сделал конспект классного доклада о работе с тачами и жестами в Android.

Один раз заказчик попросил фичу, для которой надо было написать View с кастомной обработкой свайпов. Статьи, которые я прочитал, окончательно меня запутали, и я пошел искать доклады. Первый же найденный доклад поставил всё на свои места. Делюсь этой годнотой с вами, а конспект советую добавить в закладки и пользоваться им, как шпаргалкой.

https://medium.com/@kondenko/mastering-the-android-touch-system-fdcca4468c31

#разработка #ui
Бывает, что рисуешь интерфейс, и он кажется каким-то "не таким". Зачастую это можно исправить без особых усилий. Поменять размер шрифта, увеличить отступы, переставить местами блоки, и вот уже другое дело. Подписчик @vintr_hd поделился списком советов, как сделать интерфейс лучше с помощью таких небольших изменений. 

Когда много читаешь о дизайне, поток таких советов становится даже слишком большим. Хочется сразу начать применять их в работе, но запомнить всё не получается. Мне помогает собирать такие штуки у себя в заметках, по возможности раскидывать по категориям (формы, сообщения об ошибках, онбординг) и заглядывать, когда работаю над соответствующим элементом UI. Со временем они запоминаются и сами всплывают в памяти, когда нужно. 

https://twitter.com/i/moments/994601867987619840
В дополнение к предыдущему посту @product делится чеклистом для принятия выполненных дизайн-задач:
Forwarded from Дэйли 🎙
Когда-нибудь продакт менеджера заменят набором алгоритмов. А пока можно натренировать чеклист готовности дизайн задачи.

Описание
🧯 Какую проблему решаем?
🚪 Как пользователь попадает сюда?

Big picture
🗺 Карта кликов (все кликабельные элементы и переходы)
🌐 Big picture (вставить дизайн решение в весь видимый пользователю UI)

Состояния экранов
🖥 Breakpoints, например, 0+, 600+, 960+, 1280+, 1920+
💻 Дополнительное поведение между брэйкпойнтами при необходимости
📱 Ограничение экрана по высоте, portrait vs landscape

Состояния элементов
🖱 Active, hover, clickable area
☀️ Светлая и тёмная темы
💬 Положительные и отрицательные сценарии, например, при вводе пользователем полей

Состояния контента
Пограничные состояния количества элементов контента (0, 1, несколько, сотня)
💾 Объём контента внутри элемента, например, мало или много текста
🎬 Первый шаг взаимодействия (что произойдёт в самом начале у пользователя без истории взаимодействия)
🏁 Последний шаг взаимодействия (что произойдёт, когда всё, что надо было показать, закончилось)
🦖 Заглушки, когда какая-то часть данных не придёт с сервера, например, изображение

Локализация
🇮🇩 Текст с большим количеством букв, например, индонезийский
🇨🇳 Иероглифы, например, simple chinese
🇦🇪 Right to left, например, арабский

Самоконтроль
📍 Масштабируемость (насколько сложно в дальнейшем будет поменять и добавить будущие решения)
✏️ Дальнейшее наполнение (как будет добавляться контент)
🔗 Консистентность с уже существующими дизайн решениями в продукте
🔧 Проверка технического решения (на реализуемость, на сроки, на переиспользование, caniuse с учетом поддерживаемых платформ)