Перевод статьи «Ten #AndroidLifeHacks You Can Use Today», опубликованный сайтом tproger.ru.
Вот несколько советов и extensions на Kotlin, которые будут полезны в ежедневной работе Android-разработчика.
Совет 1: Добивайтесь зелёной галочки
Android Studio предоставляет множество встроенных проверок: от юнит-тестов до линтёра. К сожалению, пока нет хорошего способа убедиться, что на CI нет предупреждений, поэтому мы разместили наш профиль для проверки в репозитории, чтобы все видели один и тот же набор предупреждений.
Затем мы взяли за правило ВСЕГДА избавляться от предупреждений (фиксить или заглушать их). При появлении новых предупреждений (из-за рефакторинга или добавления новых проверок), мы следуем правилу бойскаута (всегда оставляй после себя код в лучшем состоянии, чем до тебя).
Однако есть более сложный случай — точки входа, такие как фрагменты, на которые ссылаются только с помощью рефлексии (Android Studio помечает их как неиспользуемые). Чтобы избавиться от таких предупреждений мы сделали TonalEntryPoint.
Используя эту аннотацию в классе или конструкторе, который помечен как неиспользуемый, и нажимая alt+enter, мы получим возможность добавить неиспользуемый код в список точек входа.
Совет 2: Относитесь к предупреждениям Kotlin, как к ошибкам
Для этого добавим следующий код в файл build.gradle:
allprojects { gradle.projectsEvaluated { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { allWarningsAsErrors = true } } } }
Совет 3: Делегат ViewBinding
Мы очень любим View Binding. Он отлично работает, и мы полностью перешли на него с ButterKnife/Kotlin Synthetics. Однако, синтаксис его создания не идеален, поэтому мы создали собственный делегат, чтобы создавать его в соответствии с жизненным циклом, это выглядит следующим образом:
class WifiNetworksFragment : TonalFragment(R.layout.wifi_networks_fragment) { private val binding: WifiNetworksFragmentBinding by viewBinding() ... } class WifiNetworkView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) { private val binding: WifiNetworkViewBinding by viewBinding() ... }
Здесь можно посмотреть полный код.
Совет 4: fadeTo(visible)
Необходимость скрыть/показать view возникает часто. Для улучшения пользовательского опыта мы создали функцию fadeTo() в качестве замены для View.setVisibility() или View.isVisible = true/false.
// Функцию можно вызывать повторно, она не будет прерывать анимацию myView.fadeTo(true) myView.fadeTo(false) myView.fadeTo(true, toAlpha = 0.8f) myView.fadeTo(true, startDelay = 300) myView.fadeTo(true, duration = 500)
Код функции можно найти на GitHub.
Совет 5: mapDistinct()
Это простое сокращение для Flow.map().distinctUntilChanged().
/** * Сокращение map(...).distinctUntilChanged() */ fun <T, V> Flow.mapDistinct(mapper: suspend (T) -> V): Flow = map(mapper).distinctUntilChanged(
Совет 6: uniqueObservable()
Delegates.observable в Kotlin очень полезен. Однако:
- Иногда нам нужно делать что-то только при изменении значения.
- В таком случае писать лямбда-выражение с тремя параметрами и проверку на равенство чересчур шаблонно.
Поэтому мы написали uniqueObservable(), который будет вызывать лямбду только если значение изменится.
class Label @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { var inverted by uniqueObservable(false) { refreshDrawableState() } ... }
Совет 7: Отладка + Корутины и Broadcast Receivers
В настоящее время Broadcast Receivers (широковещательные приёмники) редко используются в Android. Однако они могут быть крайне полезны для тестов в процессе отладки.
Мы используем debug-only широковещательные приемники, которые, по сути, дают нам CLI для установки/обновления параметров в коде без необходимости вызывать меню отладки в UI или пересобирать приложение.
Наш coroutine friendly код будет выглядеть так:
context.registerReceiverInScope(scope, WifiManager.WIFI_STATE_CHANGED_ACTION) { intent -> val state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED) // Use wifi state here }
Совет 8: ConflatedJob
Корутины часто используются для отмены выполнения Job, перед запуском нового (например когда пользователь делает pull to refresh). ConflatedJob автоматически отменяет старый Job, перед запуском нового.
class MyClass(private val scope: CoroutineScope) { private val job = ConflatedJob() fun retry() { retryJob += scope.launch { delay(Long.MAX_VALUE) } } }
Совет 9: Timber Property Delegate
Для логгирования мы используем Timber, однако его синтаксис Timber.tag(TAG).i(…) — слишком громоздкий.
По аналогии с View Binding Delegate, мы написали делегат, который упрощает вызов:
class MyClass { private val log by timber() fun logSomething() { log.i("Hello") log.w(Exception(), "World") } }
Он автоматически создает TAG на каждое свойство (а не на строку журнала, как Timber.DebugTree), сокращает общие суффиксы, такие как «Impl» и «ViewModel» и обрезает теги до 23 символов.
Полный код на GitHub.
Совет 10: Number.dp
Так как в макетах обычно используют dp, а в свойствах View пиксели, часто в коде требуется конвертировать dp в пиксели. Именно поэтому вопрос о том как это сделать пользуется популярностью на StackOverflow. Мы используем для этого следующий extension на Kotlin:
/** * Вызовите эту функцию для dp и она вернет эквивалентное значение * в пикселях для текущего дисплея. * e.g. 8.dp */ val Number.dp get() = toFloat() * (Resources.getSystem().displayMetrics.densityDpi.toFloat()
С помощью этой функции очень просто обновить padding:
recyclerView.updatePadding(top = 14.dp.toInt())
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]