Парсинг ботокоманд
/add_thing Name DescriptionКак распарсить эти команды и не сойти с ума? Внутри JDK периодически используется
/add_thing Name 'Long description'
StringTokenizer
, но это просто String.split()
образца 1995 года. А нужный класс, который понимает кавычки, называется StreamTokenizer
, и, как следует из названия, он ещё и стрим разобрать может.Forwarded from Android Broadcast (Кирилл Розов)
🔴 Стартует собеседование в прямом эфире
Проводит Михаил Горюнов @Harmonizr, разработчик Android-приложений, библиотек и инструментов.
Миша будет собеседовать на позицию Android разработчика без уровня (требования здесь)
P.S. Запись останется на канале
#AndroidBroadcast #собеседование
Проводит Михаил Горюнов @Harmonizr, разработчик Android-приложений, библиотек и инструментов.
Миша будет собеседовать на позицию Android разработчика без уровня (требования здесь)
P.S. Запись останется на канале
#AndroidBroadcast #собеседование
Интерфейсы коллекций
Вчера задали мне этот вопрос на собесе. Ну я и перечисляю:
Оказывается, это почти всё. Для пущего эффекта можно выучить:
Вчера задали мне этот вопрос на собесе. Ну я и перечисляю:
Iterable
, Collection
, List
, Set
, SortedSet
, NavigableSet
, Queue
, Deque
; Map
, ConcurrentMap
— тут остановился в ожидании реакции собеседующего. Глазами хлопаю, мол, что, надо ещё?Оказывается, это почти всё. Для пущего эффекта можно выучить:
BlockingQueue
, TransferQueue
, BlockingDeque
; SortedMap
, NavigableMap
, ConcurrentNavigableMap
.Недавно меня посетила красивая #идея — вот бы приложения карт умели показывать слои, предоставленные другими приложениями! Открываешь Organic, 2GIS или что там сейчас есть из картографии — а туда сразу подтягиваются такси, самокаты, каршеринг, общественный транспорт, парковки — в зависимости от того, какие провайдеры установлены. Философия UNIX во всей красе.
Как и большинство других идей, я не первый, к кому они приходят. Например, есть вот такая закрытая/коммерческая интеграция. А хочется — публичный интерфейс для всех.
Как и большинство других идей, я не первый, к кому они приходят. Например, есть вот такая закрытая/коммерческая интеграция. А хочется — публичный интерфейс для всех.
Редактируемый список —
Рассмотрим для начала очевидно нерабочий способ: for-each по вьюшечкам. Как ни странно, я видел такое решение в продакшене. Чтобы оно работало, ресайклер был завёрнут в скроллвью!
Следующий сомнительный вариант: нацепить
Название
Итоговый gist и ответ на SO.
EditText
внутри RecyclerView
Очень много лулзов на StackOverflow по этому вопросу. Написать адаптер с эдиттекстами под силу каждому, а вот получить назад отредактированные данные могут не только лишь все.Рассмотрим для начала очевидно нерабочий способ: for-each по вьюшечкам. Как ни странно, я видел такое решение в продакшене. Чтобы оно работало, ресайклер был завёрнут в скроллвью!
Следующий сомнительный вариант: нацепить
TextWatcher
и записывать результат Editable.toString()
на каждый тык пользователя по клавиатуре.Название
Editable
, кстати, намекает нам на мутабельность редактируемого буфера. Так возникает ещё одна стратегия — дёрнуть Editable getText()
, а не слушать каждое изменение. Тогда байндинг будет выглядеть так:editText.setText(editables[position] ?: strings[position])
editables[position] = editText.text
EditText
создаст мутабельную копию строки. А можно сделать это самостоятельно, потому что в editables
уже может лежать нужный буфер. Только нужно отвадить EditText
от копирования, заменив Editable.Factory
.Итоговый gist и ответ на SO.
Gist
Another approach to EditTexts inside RecyclerView
Another approach to EditTexts inside RecyclerView. GitHub Gist: instantly share code, notes, and snippets.
Привет, я подсяду? Поговорим о корпоративной коррупции?
Давайте разберёмся, как в опенсорс попадают всякие технологии. Вот мне кусочек рационального мышления подсказывает, что компания решает свои задачи, свои проблемы, выпускает что-то для себя, внедряет технологию в свои процессы, извлекает из этого коммерческую выгоду, и — почему бы нет — делится с другими, собирая багрепорты от широкой аудитории и повышая качество — а это снова выгода. Ну, логично, мне кажется. Вроде как-то так должно работать.
Теперь давайте прикола ради глянем библиотеку, которая не работает: она должна помогать следить за внешним видом приложения, но даже стандартную кнопочку со стандартным stateListAnimator'ом в дефолтной теме скриншотит неправильно, не говоря уже про скруглённые аватарки, которые у них в собственном приложении таки есть, судя по нагугленным скриншотам.
Чистая прибыль в 23 гигабакса даёт о себе знать, нужно срочно стартовать новый проект, обосновать его целесообразность, выбить финансирование. Написанный код потом можно выкинуть — не прижился.
Ах да.
Давайте разберёмся, как в опенсорс попадают всякие технологии. Вот мне кусочек рационального мышления подсказывает, что компания решает свои задачи, свои проблемы, выпускает что-то для себя, внедряет технологию в свои процессы, извлекает из этого коммерческую выгоду, и — почему бы нет — делится с другими, собирая багрепорты от широкой аудитории и повышая качество — а это снова выгода. Ну, логично, мне кажется. Вроде как-то так должно работать.
Теперь давайте прикола ради глянем библиотеку, которая не работает: она должна помогать следить за внешним видом приложения, но даже стандартную кнопочку со стандартным stateListAnimator'ом в дефолтной теме скриншотит неправильно, не говоря уже про скруглённые аватарки, которые у них в собственном приложении таки есть, судя по нагугленным скриншотам.
Чистая прибыль в 23 гигабакса даёт о себе знать, нужно срочно стартовать новый проект, обосновать его целесообразность, выбить финансирование. Написанный код потом можно выкинуть — не прижился.
Ах да.
PixelCopy.request
. Шах и мат, аферисты.GitHub
View clipping to outline doesn't work on screenshots · Issue #276 · facebook/screenshot-tests-for-android
When make a screenshot test for some layout, and some custom view inside contains outline clipping like that: view.outlineProvider = object : ViewOutlineProvider() { override fun getOutline(v: View...
Один мой коллега постоянно забывал, как кого зовут, из-за чего в нашем лексиконе появились два собирательных персонажа — Кирилл Гладков и Алексей Розов.
Дошутились таки!
Дошутились таки!
Telegram
Mobile Developer
Очень важный анонс! 🔥🔥
Mobile Developer и Android Broadcast идут в оффлайн!
После нашего с Кириллом турне по России мы поняли, что во многих городах есть люди, которые хотели бы встречаться, общаться и обмениваться знаниями без деления на платформы, а просто…
Mobile Developer и Android Broadcast идут в оффлайн!
После нашего с Кириллом турне по России мы поняли, что во многих городах есть люди, которые хотели бы встречаться, общаться и обмениваться знаниями без деления на платформы, а просто…
Пару слов про зерокодинг и ни одной шутки про зерозарплатинг
Да, из готовых компонентов можно собрать сайт, приложение или круд. Оно даже будет работать. Я смотрю на это просто как на программирование на более высоком уровне.
Что здесь примечательно для нас, разработчиков? А то, что компоненты «среднего» уровня написаны такими же программистами. И если менеджер мышкой натыкивает приложение быстрее, чем профессиональный разработчик, то у нас, коллеги, наблюдается проблема сjob security качеством и доступностью компонентов. Если OAuth из конструктора интегрируется быстрее и работает лучше, чем решение с гитхаба, значит, мы залайкали не тот репозиторий, автор его недодокументировал или вообще бросил на полпути. Если нам данные приходят по одной схеме, и мы, чтобы трансформировать их в другую, удобную нам, выражаем обе через ДТОшки и пишем маппер между ними, значит, библиотека, которой мы намазываем данные на классы, не решает нашу задачу. Если прокладка для работы с БД заставляет нас… ну и так далее.
Зерокодинг никогда не станет мощнее и гибче, чем «настоящее» программирование. А наша задача — делать так, чтобы последнее не отставало по скорости и качеству. Хочу надеяться, что конкуренция со стороны зерокодинга поспособствует повышению качества нашего родного программирования.
Что здесь примечательно для нас, разработчиков? А то, что компоненты «среднего» уровня написаны такими же программистами. И если менеджер мышкой натыкивает приложение быстрее, чем профессиональный разработчик, то у нас, коллеги, наблюдается проблема с
Зерокодинг никогда не станет мощнее и гибче, чем «настоящее» программирование. А наша задача — делать так, чтобы последнее не отставало по скорости и качеству. Хочу надеяться, что конкуренция со стороны зерокодинга поспособствует повышению качества нашего родного программирования.
Размножение массивов
String::repeat
— произведение искусства.Arrays.fill
же написан банально и буднично, но я подозреваю, что он успешно векторизуется.Раздельная компиляция, min, compile, target
В целом Java-технологии привычные к раздельной компиляции. Серверные приложения компилируются с Java Class Library, которая подкладывается в compile classpath при сборке, а запускаются с другим экземпляром JCL, которая лежит на сервере.
Однако, эта раздельность почти не чувствуется. Она незримо присутствует и заключается в том, что в итоговом jar у нас не лежит весь java.lang, java.util и что мы там ещё любим. Мы запускаем приложение с той же версией JDK, с которой собирали, и всё просто работает.
Таким образом, приложение разрабатывается под одну версию платформы, без оглядки на более старые и с надеждой на совместимость с новыми.
В desktop-приложениях (кто-то их ещё пишет?) всё ещё проще: зачастую JVM приносят с собой, и раздельной компиляции как не бывало.
В Android же, как известно, зоопарк: на конечных устройствах много разных версий. И вот тут система сборки спроектирована очень удачно:
• compile SDK — последняя версия, известная разработчику приложения на момент сборки. Она подкладывается в compile classpath, за счёт чего разработчик видит все новые фичи платформы;
• min SDK — минимальная поддерживаемая версия. Старой платформе, которую разработчик не хочет поддерживать, позволяет отклонять установку приложений. Инструментарию позволяет подсказать программисту, что те или иные declarations недоступны в min и могут отсутствовать в runtime classpath, поэтому нужно обернуть их использование в if;
• target SDK — версия, на поведение которой рассчитывает приложение. Позволяет более новой версии платформы сохранить поведение старой в старом приложении.
Таким образом, в быстро меняющемся мире Android-приложение компилируется для свежей версии платформы (compile SDK), ифами поддерживает более старые версии (вплоть до min SDK), а платформа, если она новее, чем ожидаемая (target SDK), сохраняет старое совместимое поведение (тоже ифами).
Это прекрасно. Это шедевр.
Для сравнения, ситуация в мире плагинов для IntelliJ: выбираешь одну версию, которая и будет твоим compile и min. Хочешь поддержать постарше — опускаешь версию, перестаёшь видеть новые declarations. Если в новых версиях что-то deprecated — ты об этом узнаешь на этапе валидации плагина, где-то после компиляции и упаковки.
Именно поэтому при обновлении IDE плагины часто либо отключаются (разработчик указал максимальную поддерживаемую версию), либо разваливаются (мой вариант:).
Зависишь от других плагинов? Вообще страдай. Там может быть установлена любая версия. Например, если зависишь от Android-плагина для IDE, то при компиляции видишь версию, которой полгода, а в бетах Android Studio уже много раз переименовали классы, поменяли на интерфейсы, переместили в другой пакет. (И сделали это в стенах той же компании, где придумали min, compile, target.) Удачи!
В целом Java-технологии привычные к раздельной компиляции. Серверные приложения компилируются с Java Class Library, которая подкладывается в compile classpath при сборке, а запускаются с другим экземпляром JCL, которая лежит на сервере.
Однако, эта раздельность почти не чувствуется. Она незримо присутствует и заключается в том, что в итоговом jar у нас не лежит весь java.lang, java.util и что мы там ещё любим. Мы запускаем приложение с той же версией JDK, с которой собирали, и всё просто работает.
Таким образом, приложение разрабатывается под одну версию платформы, без оглядки на более старые и с надеждой на совместимость с новыми.
В desktop-приложениях (кто-то их ещё пишет?) всё ещё проще: зачастую JVM приносят с собой, и раздельной компиляции как не бывало.
В Android же, как известно, зоопарк: на конечных устройствах много разных версий. И вот тут система сборки спроектирована очень удачно:
• compile SDK — последняя версия, известная разработчику приложения на момент сборки. Она подкладывается в compile classpath, за счёт чего разработчик видит все новые фичи платформы;
• min SDK — минимальная поддерживаемая версия. Старой платформе, которую разработчик не хочет поддерживать, позволяет отклонять установку приложений. Инструментарию позволяет подсказать программисту, что те или иные declarations недоступны в min и могут отсутствовать в runtime classpath, поэтому нужно обернуть их использование в if;
• target SDK — версия, на поведение которой рассчитывает приложение. Позволяет более новой версии платформы сохранить поведение старой в старом приложении.
Таким образом, в быстро меняющемся мире Android-приложение компилируется для свежей версии платформы (compile SDK), ифами поддерживает более старые версии (вплоть до min SDK), а платформа, если она новее, чем ожидаемая (target SDK), сохраняет старое совместимое поведение (тоже ифами).
Это прекрасно. Это шедевр.
Для сравнения, ситуация в мире плагинов для IntelliJ: выбираешь одну версию, которая и будет твоим compile и min. Хочешь поддержать постарше — опускаешь версию, перестаёшь видеть новые declarations. Если в новых версиях что-то deprecated — ты об этом узнаешь на этапе валидации плагина, где-то после компиляции и упаковки.
Именно поэтому при обновлении IDE плагины часто либо отключаются (разработчик указал максимальную поддерживаемую версию), либо разваливаются (мой вариант:).
Зависишь от других плагинов? Вообще страдай. Там может быть установлена любая версия. Например, если зависишь от Android-плагина для IDE, то при компиляции видишь версию, которой полгода, а в бетах Android Studio уже много раз переименовали классы, поменяли на интерфейсы, переместили в другой пакет. (И сделали это в стенах той же компании, где придумали min, compile, target.) Удачи!
Телефон как USB-хаб с периферией
Когда-то давно мне срочно понадобился микрофон. Он есть в телефоне, но не было в компьютере. Накопал тогда mic over mumble, который после долгих уговоров заработал. Микрофон я потом купил, а потом ещё один 😌
Когда я ходил на стрим к Кириллу, он мне рассказал про Iriun webcam — приложение, которое пробрасывает камеру телефона на компьютер. Очень правильная штука, потому что в телефонах сейчас хорошие камеры, а веб-камера и телефон одновременно использовать мне не нужно.
Пробросить экран телефона можно с помощью scrcpy.
А почему так сложно? Я могу в настройках раздать вайфай, могу использовать телефон как USB-модем. Могу включить передачу файлов, отдав флешку в распоряжение компьютера.
Где тогда переключатель, который превратит телефон в веб-камеру и USB-микрофон? Удивительно, что это нужно как-то отдельно прокидывать через adb или Wi-Fi с помощью стороннего софта. Хочется, чтобы эти возможности были доступны не только гикам, но и бабушкам из глубинки.
И ещё: во всех мессенджерах можно показать экран, то есть пробросить сигнал с выхода на вход. Но почему нельзя пошарить выход аудиокарты? Хочу поставить музычку своим собеседникам!
Когда-то давно мне срочно понадобился микрофон. Он есть в телефоне, но не было в компьютере. Накопал тогда mic over mumble, который после долгих уговоров заработал. Микрофон я потом купил, а потом ещё один 😌
Когда я ходил на стрим к Кириллу, он мне рассказал про Iriun webcam — приложение, которое пробрасывает камеру телефона на компьютер. Очень правильная штука, потому что в телефонах сейчас хорошие камеры, а веб-камера и телефон одновременно использовать мне не нужно.
Пробросить экран телефона можно с помощью scrcpy.
А почему так сложно? Я могу в настройках раздать вайфай, могу использовать телефон как USB-модем. Могу включить передачу файлов, отдав флешку в распоряжение компьютера.
Где тогда переключатель, который превратит телефон в веб-камеру и USB-микрофон? Удивительно, что это нужно как-то отдельно прокидывать через adb или Wi-Fi с помощью стороннего софта. Хочется, чтобы эти возможности были доступны не только гикам, но и бабушкам из глубинки.
И ещё: во всех мессенджерах можно показать экран, то есть пробросить сигнал с выхода на вход. Но почему нельзя пошарить выход аудиокарты? Хочу поставить музычку своим собеседникам!
Как передать любой View произвольный AttributeSet
А зачем?
Есть атрибуты, которые можно выставить только из XML. Но не все вью удобно инстанцировать из XML, особенно когда это анонимный класс:
Сразу расстрою: программно создать
В этом месте я и прекратил свои поиски, когда в 2016 начал верстать из кода.
Но можно любой вьюшке навязать атрибуты из layout-файла.
Например, такого:
Теперь парсим XML, пропускаем открывающий тэг и забираем атрибуты:
Исходник LayoutInflater::advanceToRootNode().
А зачем?
Есть атрибуты, которые можно выставить только из XML. Но не все вью удобно инстанцировать из XML, особенно когда это анонимный класс:
addView(object : SomeView(context) { /* тут какие-то оверрайды */ })
.Сразу расстрою: программно создать
AttributeSet
, который будет работать с obtainStyledAttributes
, не получится, если не прибегать к приватным API андроида.В этом месте я и прекратил свои поиски, когда в 2016 начал верстать из кода.
Но можно любой вьюшке навязать атрибуты из layout-файла.
Например, такого:
<?xml version="1.0" encoding="utf-8"?>
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:scrollbars="horizontal" />
Теперь парсим XML, пропускаем открывающий тэг и забираем атрибуты:
val parser = resources.getLayout(R.layout.scrollbars)
val attrs = Xml.asAttributeSet(parser)
advanceToRootNode(parser)
addView(object : SomeView(context, attrs) { /* тут какие-то оверрайды */ })
parser.close()
Исходник LayoutInflater::advanceToRootNode().
GitHub
platform_frameworks_base/core/java/android/view/LayoutInflater.java at ac5f755472e02f039f947ccfd5f5282e0ac80fe3 · aosp-mirror/…
Contribute to aosp-mirror/platform_frameworks_base development by creating an account on GitHub.
:slowpoke: шикарный доклад про нутрянку корутин и способы их применения помимо асинхронности.
🟢 Разбор примитивов корутин из Kotlin stdlib
🟢 Как устроены билдеры вида
🟢 Глубокая рекурсия без отрыва стека
🟢 Парсеры
Friendly reminder разработчикам Kotlin:
⚫️ сиквенс — это бесполезная обёртка над итератором (в Rust её нет — компилятору легче оптимизировать и разворачивать длинные конструкции вида iter.map.filter.etc)
⚫️
⚫️ в очередной раз не хватает «:: наоборот» —
Friendly reminder автору:
🟡 скоуп билдера должен быть
🟡 пожалуйста, не надо
Friendly reminder для организаторов:
💡 существуют нейросеточные плагины для убирания чвякания из речи
🟢 Разбор примитивов корутин из Kotlin stdlib
🟢 Как устроены билдеры вида
sequence { yield() }
🟢 Глубокая рекурсия без отрыва стека
🟢 Парсеры
Friendly reminder разработчикам Kotlin:
⚫️ сиквенс — это бесполезная обёртка над итератором (в Rust её нет — компилятору легче оптимизировать и разворачивать длинные конструкции вида iter.map.filter.etc)
⚫️
DeepRecursiveScope.callRecursive
стоило бы назвать invokeRecursive
, консистентно с invoke
и invokeSuspend
, зашитыми в язык⚫️ в очередной раз не хватает «:: наоборот» —
fun smth() by DeepRecursiveFunction {}
Friendly reminder автору:
🟡 скоуп билдера должен быть
@RestrictsSuspension
🟡 пожалуйста, не надо
data
-классовFriendly reminder для организаторов:
💡 существуют нейросеточные плагины для убирания чвякания из речи
YouTube
Coroutines Beyond Concurrency by Alex Semin
Recording brought to you by American Express. https://americanexpress.io/kotlin-jobs
Kotlin coroutines are most known as an elegant and modern solution for managing asynchronous work. But due to their versatile design, coroutines can also be helpful for…
Kotlin coroutines are most known as an elegant and modern solution for managing asynchronous work. But due to their versatile design, coroutines can also be helpful for…
Об устройстве Gravity
Начнём с определения:грава эт короч в какую сторону вьюха липнет гравити описывает способ размещения одного прямоугольника внутри другого.
В константах гравити присутствуют любопытные танцы вокруг битиков, в которые с первого раза можно и не въехать, но на деле всё просто.
Для начала, задано две оси:на какую сам сядешь, на какую джуна посадишь?
таким образом, флаги для обеих запечатываются в младший байт в форме
В каждой половинке размещается по четыре флага:
0001) указана ли вообще гравити для этой оси (экзотика)
0010) привязываться ли к началу оси
0100) привязываться ли к концу оси
1000) обрезать ли слишком большой прямоугольник
Основные значения гравити формулируются таким образом:
LEFT — липнуть к началу оси X,
RIGHT — липнуть к концу оси X,
TOP — к началу Y,
BOTTOM — к концу Y.
Одна константа для примера:
У CENTER в жизни отсутствует стремление к какой бы то ни было оси, FILL же склоняется к обеим. На примере горизонтали:
Для тех, кто впервые видит FILL, есть ещё один сюрприз:
Это размещение по центру с обрезкой краёв.
Если скомбинировать это значение, например, с LEFT, то размещение будет уже слева, с обрезкой правого края.
FILL и CLIP по обеим осям просто содержат оба набора флагов:
(Бесполезный факт:
Остались START и END — начало и конец строки с учётом направления письма. Нет, прости, шупай, татэгаки и сероссыги, но это работает только по горизонтали.
Получается добавлением к LEFT и RIGHT флажка «ось X задана относительно layout direction»:
Итого:
Здесь я не упоминаю про флаги DISPLAY_CLIP, потому что обычно оно не надо.
В классе Gravity есть методы apply, которые разбирают этот набор флагов и выполняют размещение — в простых случаях этого хватает с головой.
Для случаев, когда нужно потрогать гравити самостоятельно — наконец-то, раздел с практическими советами!
1. Фильтруем входные данные линтом.
2. Иногда нужно просто выбрать сторону, поэтому недавно у меня появилось такое:
3. Потенциально относительную граву можно легко превратить в абсолютную посредством
Нет, можно, конечно, написать
Это будет корректно и безопасно. Но громоздко и лень!
Начнём с определения:
В константах гравити присутствуют любопытные танцы вокруг битиков, в которые с первого раза можно и не въехать, но на деле всё просто.
Для начала, задано две оси:
public static final int AXIS_X_SHIFT = 0;
public static final int AXIS_Y_SHIFT = 4;
таким образом, флаги для обеих запечатываются в младший байт в форме
0bYYYYXXXX
.В каждой половинке размещается по четыре флага:
0001) указана ли вообще гравити для этой оси (экзотика)
0010) привязываться ли к началу оси
0100) привязываться ли к концу оси
1000) обрезать ли слишком большой прямоугольник
public static final int AXIS_SPECIFIED = 0x0001;
public static final int AXIS_PULL_BEFORE = 0x0002;
public static final int AXIS_PULL_AFTER = 0x0004;
public static final int AXIS_CLIP = 0x0008;
Основные значения гравити формулируются таким образом:
LEFT — липнуть к началу оси X,
RIGHT — липнуть к концу оси X,
TOP — к началу Y,
BOTTOM — к концу Y.
Одна константа для примера:
public static final int TOP =
(AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
У CENTER в жизни отсутствует стремление к какой бы то ни было оси, FILL же склоняется к обеим. На примере горизонтали:
public static final int CENTER_HORIZONTAL =
AXIS_SPECIFIED<<AXIS_X_SHIFT;
public static final int FILL_HORIZONTAL =
LEFT|RIGHT;
Для тех, кто впервые видит FILL, есть ещё один сюрприз:
public static final int CLIP_HORIZONTAL =
AXIS_CLIP<<AXIS_X_SHIFT;
Это размещение по центру с обрезкой краёв.
Если скомбинировать это значение, например, с LEFT, то размещение будет уже слева, с обрезкой правого края.
FILL и CLIP по обеим осям просто содержат оба набора флагов:
public static final int CENTER =
CENTER_VERTICAL|CENTER_HORIZONTAL;
public static final int FILL =
FILL_VERTICAL|FILL_HORIZONTAL;
(Бесполезный факт:
CENTER_VERTICAL == CENTER_HORIZONTAL>>AXIS_X_SHIFT<<AXIS_Y_SHIFT
и наоборот, и так далее, и тому подобное.)Остались START и END — начало и конец строки с учётом направления письма. Нет, прости, шупай, татэгаки и сероссыги, но это работает только по горизонтали.
Получается добавлением к LEFT и RIGHT флажка «ось X задана относительно layout direction»:
public static final int RELATIVE_LAYOUT_DIRECTION =
0x00800000;
public static final int START =
RELATIVE_LAYOUT_DIRECTION | LEFT;
Итого:
struct Gravity {
relative: bool,
axisX: AxisGravity,
axisY: AxisGravity,
}
struct AxisGravity {
specified: bool,
pullBefore: bool,
pullAfter: bool,
clip: bool,
}
Здесь я не упоминаю про флаги DISPLAY_CLIP, потому что обычно оно не надо.
В классе Gravity есть методы apply, которые разбирают этот набор флагов и выполняют размещение — в простых случаях этого хватает с головой.
Для случаев, когда нужно потрогать гравити самостоятельно — наконец-то, раздел с практическими советами!
1. Фильтруем входные данные линтом.
@Gravity.GravityFlags
недоступен, да и DISPLAY_CLIP из него нам не нужóн — но можно пойти и объявить собственный набор разрешённых флагов:@IntDef(flag = true, value = {
FILL, FILL_HORIZONTAL, FILL_VERTICAL,
START, END, LEFT, RIGHT, TOP, BOTTOM,
CENTER, CENTER_HORIZONTAL, CENTER_VERTICAL,
CLIP_HORIZONTAL, CLIP_VERTICAL,
})
public @interface GravityFlags {}
2. Иногда нужно просто выбрать сторону, поэтому недавно у меня появилось такое:
@IntDef({ START, END, LEFT, RIGHT, TOP, BOTTOM })
public @interface SideGravity { }
3. Потенциально относительную граву можно легко превратить в абсолютную посредством
Gravity.getAbsoluteGravity(gravity, layoutDirection)
. Тогда, например, @SideGravity
сузится до простого набора { LEFT, TOP, RIGHT, BOTTOM }
.Нет, можно, конечно, написать
sealed class Gravity {
sealed class Absolute : Gravity()
sealed class Relative : Gravity() {
abstract fun absolute(dir: Int): Absolute
}
object ...
}
Это будет корректно и безопасно. Но громоздко и лень!
Информационная политика наносит ответный кусь
1. Сотрудник компании А делает доклад о технологии Х. Она новая, очень интересная и многообещающая.
2. Сотрудники-энтузиасты компаний Бэ, Цэ и Дэ заинтересовываются технологией Х, выясняют, в каких ещё нишах её можно применить, и оформляют результаты в виде докладов на конференциях, митапах и стримах.
3. Обыкновенные™ работяги замечают волну интереса и делают пет-проекты с использованием технологии Х. Рынок наводняется специалистами, которые умеют и хотят использовать именно эту технологию.
4. Компании А, Бэ, Цэ, Дэ, а также невинные жертвы Йе и Эф вынуждены переписать всё на технологию Х, чтобы заинтересовывать и нанимать разработчиков.
5. Появляется технология Игрек. Сотрудник компании Бэ загорается энтузиазмом и делает доклад…
К слову, https://www.joelonsoftware.com/2002/01/06/fire-and-motion/
1. Сотрудник компании А делает доклад о технологии Х. Она новая, очень интересная и многообещающая.
2. Сотрудники-энтузиасты компаний Бэ, Цэ и Дэ заинтересовываются технологией Х, выясняют, в каких ещё нишах её можно применить, и оформляют результаты в виде докладов на конференциях, митапах и стримах.
3. Обыкновенные™ работяги замечают волну интереса и делают пет-проекты с использованием технологии Х. Рынок наводняется специалистами, которые умеют и хотят использовать именно эту технологию.
4. Компании А, Бэ, Цэ, Дэ, а также невинные жертвы Йе и Эф вынуждены переписать всё на технологию Х, чтобы заинтересовывать и нанимать разработчиков.
5. Появляется технология Игрек. Сотрудник компании Бэ загорается энтузиазмом и делает доклад…
К слову, https://www.joelonsoftware.com/2002/01/06/fire-and-motion/
Joel on Software
Fire And Motion
Sometimes I just can’t get anything done. Sure, I come into the office, putter around, check my email every ten seconds, read the web, even do a few brainless tasks like paying the American E…