Улучшите качество своего кода с помощью этих полезных расширений Kotlin для Android

Улучшите качество своего кода с помощью этих полезных расширений 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)
}

Photo by Artur Tumasjan on Unsplash

Не все то золото, что блестит

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

Начнем с бесполезного расширения. На днях я читал статью о расширениях и нашел вот это.

fun Any?.isNull() = this == null

if (something.isNull()) { ... }

  1. При использовании расширения код не сокращается.
  2. При использовании расширения компилятор будет считать его обычным вызовом функции и воздержится от оптимизации нулевого случая, если это возможно.
  3. Это плохое использование ООП, мы добавляем методы к объектам, чтобы «спросить» их о чем-то, нуль по определению означает отсутствие чего-то. Объект никогда не бывает нулевым, нулевым является ссылка на объект.

Будьте осторожны с локализацией

Приведенное ниже расширение может показаться разумным для большинства из нас. Он форматирует 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

:::информация Также опубликовано здесь.

:::


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE