Улучшите качество своего кода с помощью этих полезных расширений Kotlin для Android
3 февраля 2023 г.Kotlin обладает множеством замечательных функций, но одной из них являются расширения. Расширения используются для добавления функциональности к существующим классам.
Они чрезвычайно полезны, помогая сократить шаблонный код. Хорошо, что вы можете добавить их в любой класс, в том числе из сторонних библиотек, которые вы не можете изменить.
В большинстве случаев мы видим функции расширения, но Kotlin также поддерживает свойства расширения.
Полезные функции расширения
Давайте начнем с некоторых расширений, которые облегчают чтение кода и со временем улучшают качество кода за счет сокращения стандартного кода.
Контекст
Android API со временем меняется, и такая простая вещь, как получение цвета для заданного идентификатора, может стать не такой уж простой. Приведенные ниже расширения помогают сократить шаблонный код, связанный с получением ресурсов для заданного идентификатора.
fun Context.getCompatColor(@ColorRes colorId: Int) = ResourcesCompat.getColor(resources, colorId, null)
fun Context.getCompatDrawable(@DrawableRes drawableId: Int) = AppCompatResources.getDrawable(this, drawableId)!!
Использование
// Activity
getCompatColor(R.color.white)
// Fragment
requireContext().getCompatColor(R.color.white)
Мы все знаем, как это повторяется, чтобы проверить, предоставил ли пользователь разрешения, требуемые приложению. Расширение ниже может помочь в этом. Это не решает всей проблемы, но помогает уменьшить размер кода.
fun Context.hasPermissions(vararg permissions: String) = permissions.all { permission ->
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
}
Использование
// Traditional way (Activity)
(ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED)
// Extension (Activity)
hasPermissions(Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION)
Копирование текста или URL-адреса в буфер обмена должно быть чем-то очень простым. Он работает только с обычным текстом, но это то, что мы используем большую часть времени, если вам нужна поддержка других форматов, вы можете изменить его.
fun Context.copyToClipboard(content: String) {
val clipboardManager = ContextCompat.getSystemService(this, ClipboardManager::class.java)!!
val clip = ClipData.newPlainText("clipboard", content)
clipboardManager.setPrimaryClip(clip)
}
Есть ли в вашем приложении URL-адрес, который пользователь должен открыть в браузере для просмотра? Вы занимаетесь случаем, когда браузер или эквивалентное приложение может отсутствовать? Следующее расширение абстрагирует большую часть того, что необходимо. Вам просто нужно дать ему Uri и сделать что-то, например, показать ошибку, если нет приложения для решения намерения.
fun Context.isResolvable(intent: Intent) = intent.resolveActivity(packageManager) != null
fun Context.view(uri: Uri, onAppMissing: () -> Unit) {
val intent = Intent(Intent.ACTION_VIEW, uri)
if (!isResolvable(intent)) {
onAppMissing()
return
}
startActivity(intent)
}
Использование
// Activity
view(uri, onAppMissing = {
// show error message
})
Дата
Это расширение очень полезно, если вам нужно снова и снова регистрировать объект даты или отправлять его через вызов API. Здесь следует отметить, что формат ISO является универсальным, он не меняется от страны к стране, позже я покажу, почему это важно.
val isoFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
fun Date.toISOFormat() : String = isoFormatter.format(this)
Использование
Date(1620862687852).toISOFormat() // 2021-05-12 23:38:07.540
Некоторые другие расширения, которые могут быть полезны
fun Date.plusDays(days: Int): Date {
return Date(this.time + (days * ONE_DAY_MS))
}
fun Date.plusMillis(millis: Long): Date {
return Date(this.time + millis)
}
Не все то золото, что блестит
Многими вещами, включая расширения, можно злоупотреблять. Некоторые из них выглядят хорошо, но на самом деле представляют собой замаскированные запахи кода.
Начнем с бесполезного расширения. На днях я читал статью о расширениях и нашел вот это.
fun Any?.isNull() = this == null
if (something.isNull()) { ... }
- При использовании расширения код не сокращается.
- При использовании расширения компилятор будет считать его обычным вызовом функции и воздержится от оптимизации нулевого случая, если это возможно.
- Это плохое использование ООП, мы добавляем методы к объектам, чтобы «спросить» их о чем-то, нуль по определению означает отсутствие чего-то. Объект никогда не бывает нулевым, нулевым является ссылка на объект.
Будьте осторожны с локализацией
Приведенное ниже расширение может показаться разумным для большинства из нас. Он форматирует Double в десятичный формат.
fun Any?.isNull() = this == null
if (something.isNull()) { ... }
Опасность этого кода заключается в том, что деньги форматируются по-разному во всем мире. В некоторых странах вы увидите 1 200,00 (сначала запятая, после точка), в других 1 200,00 (сначала точка, запятая после). Это проблема только в том случае, если приложение используется/будет использоваться во многих странах.
Мое решение состояло бы в том, чтобы добавить шаблон форматирования в strings.xml
и создать расширение в Context
для обработки этого. Таким образом, вы можете определить денежный формат для каждого языка и по-прежнему использовать одну и ту же функцию.
fun Context.formatPrice(price: Double) : String {
val formatter = DecimalFormat(getString(R.string.price_formatter))
return formatter.format(price)
}
Свойства расширения
Kotlin также позволяет нам добавлять свойства в качестве расширений. Я еще не нашел для них варианта использования, но полезно знать, что их можно использовать.
val <T> List<T>.lastIndex: Int
get() = size - 1
Спасибо, что прочитали. Если есть что-то, с чем вы не согласны, оставьте комментарий ниже, чтобы мы могли обсудить это и найти лучшее решение.
Фото на обложке Фрэн Жакье на Unsplash
:::информация Также опубликовано здесь.
:::
Оригинал