Функциональное программирование
Одним из строительных блоков программы являются функции. Функция определяет некоторое действие. В Kotlin функция объявляется с помощью ключевого слова fun , после которого идет название функции. Затем после названия в скобках указывается список параметров. Если функция возвращает какое-либо значение, то после списка параметров через запятую можно указать тип возвращаемого значения. И далее в фигурных скобках идет тело функции.
fun имя_функции (параметры) : возвращаемый_тип
Например, определим и вызовем функцию, которая просто выводит некоторую строку на консоль:
fun main() < hello() // вызов функции hello hello() // вызов функции hello hello() // вызов функции hello >// определение функции hello fun hello()
Функции можно определять в файле вне других функций или классов, сами по себе, как например, определяется функция main. Такие функции еще называют функциями верхнего уровня (top-level functions).
Здесь кроме главной функции main также определена функция hello, которая не принимает никаких параметров и ничего не возвращает. Она просто выводит строку на консоль.
Функция hello (и любая другая определенная функция, кроме main) сама по себе не выполняется. Чтобы ее выполнить, ее надо вызвать. Для вызова функции указывается ее имя (в данном случае «hello»), после которого идут пустые скобки.
Таким образом, если необходимо в разных частях программы выполнить одни и те же действия, то можно эти действия вынести в функцию, и затем вызывать эту функцию.
Предача параметров
Через параметры функция может получать некоторые значения извне. Параметры указываются после имени функции в скобках через запятую в формате имя_параметра : тип_параметра . Например, определим функцию, которая просто выводит сообшение на консоль:
fun main() < showMessage("Hello Kotlin") showMessage("Привет Kotlin") showMessage("Salut Kotlin") >fun showMessage(message: String)
Функция showMessage() принимает один параметр типа String . Поэтому при вызове функции в скобках необходимо передать значение для этого параметра: showMessage(«Hello Kotlin») . Причем это значение должно представлять тип String, то есть строку. Значения, которые передаются параметрам функции, еще назвают аргументами.
Консольный вывод программы:
Hello Kotlin Привет Kotlin Salut Kotlin
Другой пример — функция, которая выводит данные о пользователе на консоль:
fun main() < displayUser("Tom", 23) displayUser("Alice", 19) displayUser("Kate", 25) >fun displayUser(name: String, age: Int)
Функция displayUser() принимает два параметра — name и age. При вызове функции в скобках ей передаются значения для этих параметров. При этом значения передаются параметрам по позиции и должны соответствовать параметрам по типу. Так как вначале идет параметр типа String , а потом параметр типа Int , то при вызове функции в скобках вначале передается строка, а потом число.
Аргументы по умолчанию
В примере выше при вызове функций showMessage и displayUser мы обязательно должны предоставить для каждого их параметра какое-то определенное значение, которое соответствует типу параметра. Мы не можем, к примеру, вызвать функцию displayUser, не передав ей аргументы для параметров, это будет ошибка:
displayUser()
Однако мы можем определить какие-то параметры функции как необязательные и установить для них значения по умолчанию:
fun displayUser(name: String, age: Int = 18, position: String=»unemployed») < println("Name: $name Age: $age Position: $position") >fun main()
В данном случае функция displayUser имеет три параметра для передачи имени, возраста и должности. Для первого параметр name значение по умолчанию не установлено, поэтому для него значение по-прежнему обязательно передавать значение. Два последующих — age и position являются необязательными, и для них установлено значение по умолчанию. Если для этих параметров не передаются значения, тогда параметры используют значения по умолчанию. Поэтому для этих параметров в принципе нам необязательно передавать аргументы. Но если для какого-то параметра определено значение по умолчанию, то для всех последующих параметров тоже должно быть установлено значение по умолчанию.
Консольный вывод программы
Name: Tom Age: 23 Position: Manager Name: Alice Age: 21 Position: unemployed Name: Kate Age: 18 Position: unemployed
Именованные аргументы
По умолчанию значения передаются параметрам по позиции: первое значение — первому параметру, второе значение — второму параметру и так далее. Однако, используя именованные аргументы, мы можем переопределить порядок их передачи параметрам:
fun main()
При вызове функции в скобках мы можем указать название параметра и с помощью знака равно передать ему нужное значение.
При этом, как видно из последнего случае, необязательно все аргументы передавать по имени. Часть аргументов могут передаваться параметрам по позиции. Но если какой-то аргумент передан по имени, то остальные аргументы после него также должны передаваться по имени соответствующих параметров.
Также если до обязательного параметра функции идут необязательные параметры, то для обязательного параметра значение передается по имени:
fun displayUser(age: Int = 18, name: String) < println("Name: $name Age: $age") >fun main()
Изменение параметров
По умолчанию все параметры функции равносильны val-переменным, поэтому их значение нельзя изменить. Например, в случае следующей функции при компиляции мы получим ошибку:
fun double(n: Int) < n = n * 2 // !Ошибка - значение параметра нельзя изменить println("Значение в функции double: $n") >
Однако если параметр предствляет какой-то сложный объект, то можно изменять отдельные значения в этом объекте. Например, возьмем функцию, которая в качестве параметра принимает массив:
fun double(numbers: IntArray)< numbers[0] = numbers[0] * 2 println("Значение в функции double: $") > fun main() < var nums = intArrayOf(4, 5, 6) double(nums) println("Значение в функции main: $") >
Здесь функция double принимает числовой массив и увеличивает значение его первого элемента в два раза. Причем изменение элемента массива внутри функции приведет к тому, что также будет изменено значение элемента в том массиве, который передается в качестве аргумента в функцию, так как этот один и тот же массив. Консольный вывод:
Значение в функции double: 8 Значение в функции main: 8
Темы и стили
Чтобы наше приложение было стильным, можно воспользоваться специальной темой. Тема — это коллекция стилей, которые обеспечивают профессиональный вид приложению, чтобы оно было похоже на родные приложения Android. Сама система Android уже имеет несколько предустановленных тем, которыми можно воспользоваться в своих целях. Вам достаточно только указать имя темы в манифесте.
В предыдущем примере мы учились переключаться на другой экран, чтобы увидеть новое окно О программе. Сделаем это окно диалоговым, чтобы оно не занимало весь экран.
Откройте снова файл манифеста AndroidManifest.xml из прошлого урока и измените строчку для активности AboutActivity, указав тему.
Запустив программу, вы увидите, что внешний вид окна «О программе» стал уже другим. Сравните.
Обратите внимание, что теперь появляется не окно во весь экран, а диалоговое окно в центре экрана. При этом остальная часть экрана затемняется.
Похожие темы: android:theme=»@style/Theme.AppCompat.Light.Dialog», android:theme=»@style/Theme.AppCompat.Light.Dialog.MinWidth», android:theme=»@style/Theme.AppCompat.Dialog.MinWidth».
Тему можно применить не только к отдельной активности, но и ко всем активностям приложения, если прописать в теге application.
Кстати, вы можете разработать свою тему на основе существующих и сохранить ее в файле res/values/styles.xml.
Стили и темы — это такие же ресурсы, как и строки, изображения и т. д. Android обеспечивает некоторые заданные по умолчанию стили и темы, которые вы можете использовать в приложениях. При необходимости вы можете определить свой собственный стиль и тему для создаваемого приложения.
Стили
Стиль — это один или несколько сгруппированных атрибутов форматирования, которые отвечают за внешний вид и поведение элементов или окна. Стиль может задавать такие свойства, как ширину, отступы, цвет текста, размер шрифта, цвет фона и так далее. Сами стили хранятся в XML-файлах, отдельно от файлов разметки.
Подобное разделение напоминает использование каскадных стилей CSS для веб-документов, которые также отвечают за стили HTML-элементов и хранятся в отдельных файлах.
Предположим, у нас есть следующий код разметки для TextView:
Мы можем вынести все свойства в файл стилей следующим образом:
res/values/styles.xml
Тогда в файле разметки теперь будет так:
Как видите, мы удалили все свойства для текста из файла разметки и разместили их в файле стилей в ресурсе под именем MyTextStyle, который содержит теперь все необходимые свойства.
Создать файл со стилями несложно. Создаем новый XML-файл в папке res/values/ вашего проекта. Имя файла не имеет значения, главное, чтобы расширение было XML, а сам файл находился в указанной папке. В проекте, создаваемом студией, уже есть готовый файл res/values/styles.xml, в который вы можете добавить новые стили. А также вы можете создать свой отдельный файл стилей.
Корневым узлом файла должен быть элемент . Для каждого элемента, которому требуется стиль, нужно добавить элемент с уникальным именем. Далее создаются элементы для каждого свойства и присваиваются им имена, которые отвечают за выбранное свойство. Значением элемента должно выступать ключевое слово, цвет в шестнадцатеричном значении, ссылка на другой тип ресурсов или другое значение в зависимости от свойства стиля. Ниже представлен образец такого стиля:
Во время компиляции все свойства из файла стилей будут извлечены и применены к элементам.
Атрибут parent для элемента style является необязательным и позволяет задавать идентификатор ресурса другого стиля, из которого нужно наследовать свойства. При этом вы можете переопределить свойства в случае необходимости.
Быстрое создание стилей через Android Studio
Если вы решили вынести некоторые свойства компонентов в стили, то необязательно это делать вручную. В студии есть удобный инструмент для быстрого создания ресурсов стилей.
В текстовом режиме щёлкните правой кнопкой мыши на нужном компоненте и в контекстном меню выбирайте пункт Refactor | Extract | Style. Далее укажите имя стиля и выберите требуемые параметры для экспорта. Студия самостоятельно создаст стиль в файле styles.xml и автоматически применит созданный ресурс в layout-файле.
Наследование стилей
Наследование — мощный и полезный механизм, позволяющий не изобретать велосипед, а использовать готовые проверенные наработки. С помощью атрибута parent в элементе style вы можете наследовать нужные свойства из существующих стилей, а также переопределить некоторые свойства или добавить свои дополнительные свойства. Предположим, мы решили наследоваться от существующего системного стиля Android для текстовых сообщений и слегка модифицировать его.
Если вы собираетесь наследоваться от собственных стилей, то использовать атрибут parent не нужно. Просто используйте префикс имени наследуемого стиля перед создаваемым новым стилем, разделяя имена стилей точкой. Например, для создания нового стиля, который наследуется от стиля MyTextStyle, созданного нами ранее, где мы хотим получить красный текст, используйте следующий способ:
Как видите, нам не пришлось использовать атрибут parent в теге style, потому что имя стиля начинается с имени MyTextStyle (созданный нами стиль). Теперь наш стиль наследует все свойства от стиля родителя, при этом мы изменили одно свойство android:textColor, чтобы текст выводился красным цветом. Вы можете ссылаться на новый стиль через конструкцию @style/MyTextStyle.Red.
Вы можете продолжать наследоваться дальше много раз, добавляя новые имена через точку. Допустим, мы хотим расширить свойство MyTextStyle.Red, чтобы текст выводился более крупным шрифтом:
Итак, мы наследовались от стилей MyTextStyle и MyTextStyle.Red, а затем добавили новое свойство android:textSize.
Не забывайте, что данная техника наследования применима только к собственным стилям. Для наследования системных стилей типа TextAppearance необходимо использовать атрибут parent.
Свойства стиля
Разобравшись с созданием стилей, рассмотрим различные свойства, определяемые в элементе item. Мы уже встречались с такими свойствами, как layout_width и textColor. На самом деле свойств гораздо больше.
Для поиска свойств, которые применимы к заданному View, можно обратиться к документации и просмотреть все поддерживаемые свойства. Так все атрибуты, перечисленные в таблице атрибутов класса TextView могут быть использованы для элементов TextView или EditText. Например, у данных элементов есть свойство android:inputType:
Но вместо этого мы можем также создать стиль для элемента EditText, который будет включать в себя данное свойство:
В файле разметки теперь можно написать так:
На первый взгляд вам возможно покажется, что подобный подход отнимает слишком много времени. Но в больших проектах повторное использование стиля поможет вам сэкономить и время и силы.
Для просмотра всех существующих стилей вы можете посмотреть исходники Android. Найдите папку, в которую вы устанавливали Android SDK, там можно найти нужные исходники. Например, у меня путь к исходникам стилей Android API 17 выглядит следующим образом: D:\Android\android-sdk-windows\platforms\android-17\data\res\values\styles.xml. Помните, что все объекты View не поддерживает сразу все существующие атрибуты, поэтому используйте только специфичные стили для выбранного элемента. Но если вы по ошибке зададите ошибочный стиль для View, то это не вызовет краха приложения. Элемент View будет использовать только подходящие свойства и игнорировать чужие для него свойства.
Существуют также свойства, которые не поддерживаются ни одним элементом View и применимы только как тема. Подобные стили действуют сразу на всё окно, а не на отдельный элемент. Например, есть тема, скрывающая заголовок приложения, строку состояния или изменяющая фон окна. Подобные стили легко определить по слову window, с которого начинается название стиля: windowNoTitle, windowBackground (о них ниже).
Не забывайте использовать префикс android перед именем в каждом элементе item: .
Отдельно стоит отметить создание стиля для кнопки. У кнопки есть несколько состояний — обычное, в фокусе, нажатое, нажатое с фокусом. Поэтому для кнопки нужно создать четыре отдельных стиля, чтобы кнопка выглядела профессионально.
Извлечение свойств в стили
Если вы решили в своём проекте использовать стили и вам нужно быстро переместить нужные атрибуты, то Android Studio предлагает быстрый механизм для этой операции. В текстовом режиме ставите курсор на названии компонента, например, ImageView, затем щёлкаете правой кнопкой мыши и выбираете Refactor | Extract | Style. . В диалоговом окне выбираете нужные атрибуты для переноса в стили и выбираете имя стиля.
Динамическое изменение стилей
Ни разу не приходилось пользоваться, но вдруг пригодится.
Context themedContext = new ContextThemeWrapper(baseContext, R.style.MyTheme); // in LayoutInflater View view = LayoutInflater.from(themedContext) .inflate(R.layout.mylayout, null); // in constructor View view = new View(themedContext);
Тема
Тема — это более ёмкое понятие. По существу, тема — стиль, который относится ко всему экрану активности или приложению, а не к отдельному компоненту приложения. Таким образом, тема имеет свои атрибуты и свою область применения.
Темы похожи на определения стилей. Точно так же, как стили, темы объявляются в XML-файле элементами , и ссылаются на них тем же самым способом. Различие состоит в том, что тема добавляется ко всему приложению или к отдельной активности через элементы и в файле манифеста приложения, т. к. темы не могут быть применены к отдельным компонентам.
Чтобы установить тему, откройте файл AndroidManifest.xml и отредактируйте тег , чтобы он включал в себя атрибут android:theme с указанием имени стиля:
Если вы хотите, чтобы тема относилась не ко всему приложению, а к отдельной активности, то атрибут android:theme нужно добавить в тег .
Во многих случаях нет необходимости придумывать свои стили и темы, так как Android содержит множество собственных встроенных тем. Например, вы можете использовать тему Dialog, чтобы окно приложения выглядело как диалоговое окно (Смотри выше).
Если вам нравится тема, но несколько свойств всё-таки хотите подправить под себя, то просто добавьте тему как родительскую тему к своей теме. Например, мы хотим модифицировать стандартную тему Theme.Light, чтобы использовать свои цвета.
#b0b0ff
Теперь мы можем использовать свой стиль вместо Theme.Light в манифесте:
Небольшой список свойств, которые используются для настройки собственных тем.
- android:windowNoTitle: используйте значение true, чтобы скрыть заголовок
- android:windowFullscreen: используйте значение true, чтобы скрыть строку состояния и освободить место для приложения
- android:windowBackground: ресурс цвета или drawable для фона
- android:windowContentOverlay: Drawable, который рисуется поверх содержимого окна. По умолчанию, это тень от строки состояния. Можно использовать null (@null в XML-файле) для удаления ресурса.
В Android 5.0 появились новые темы, которые получили название Material Design.
- @android:style/Theme.Material (тёмная версия)
- @android:style/Theme.Material.Light (светлая версия)
- @android:style/Theme.Material.Light.DarkActionBar (светлая версия с тёмным заголовком)
В Android 9.0 темы Material Design продолжили развитие, они будут активно внедряться в ближайшее время.
- Theme.MaterialComponents
- Theme.MaterialComponents.NoActionBar
- Theme.MaterialComponents.Light
- Theme.MaterialComponents.Light.NoActionBar
- Theme.MaterialComponents.Light.DarkActionBar
Для Material Design были разработаны новые атрибуты тем.
- android:colorPrimary: основной цвет для интерфейса программы — панель, кнопки и т.д.
- android:colorPrimaryDark: цвет для системных элементов — строка состояния
- android:colorAccent: Цвет по умолчанию для компонентов, которые находятся в фокусе или активны
- android:colorControlNormal: Цвет для неактивных компонентов
- android:colorControlActivated: Цвет для активных компонентов
- android:colorControlHighlight: Цвет для нажатых элементов интерфейса
- colorSwitchThumbNormal: и т.д. изучаем документацию
Позже были добавлены другие атрибуты: colorPrimaryVariant, colorOnPrimary, colorSecondary, colorSecondaryVariant, colorOnSecondary, colorError, colorOnError, colorSurface, colorOnSurface, colorBackground, colorOnBackground.
Настройка цветов происходит по определённым правилам. На сайте http://www.google.com/design/spec/style/color.html# есть таблица цветов. Обратите внимание на числа слева. Основным цветом (colorPrimary) считается цвет под номером 500, он идёт первым в таблицах. Этот цвет должен использоваться в качестве заголовка (Toolbar).
Допустим, мы делаем специальное приложение для рыжего кота. Создадим новый файл res/values/colors.xml. На указанном сайте находим таблицу цветов оранжевого цвета Orange и будем использовать предлагаемое значение.
Зададим основной цвет.
#FF9800
Для строки состояние, которая находится выше заголовка приложения, нужно использовать цвет со значением 700 (colorPrimaryDark). Это более тёмный цвет и позволяет различать заголовок приложения и строку состояния. Возвращаемся к оранжевой таблице цветов, запоминаем значение цвета и прописываем его в ресурсах.
#F57C00
Пропишем в теме приложения новые элементы.
На старых устройствах цвет строки состояния не изменяется. Цвет заголовка поменять можно.
В файле res/values-v21/styles.xml для новых устройств нужно повторить указанные действия с небольшой поправкой. В API 21 уже есть предопределённые константы для эти цветов, поэтому используем в именах android:colorPrimary и android:colorPrimaryDark.
В одной из недавних версий библиотеки совместимости была реализована поддержка нового дизайна для всех устройст и такое разделение сейчас не является обязательным.
С главным цветом есть одна тонкость. Старые устройства используют ActionBar и его цвет подчиняется правилам Material Design из коробки. На новых устройствах для активности используется тема без панели действий Theme.AppCompat.NoActionBar и вручную добавляется компонент Toolbar. Чтобы он использовал основной цвет, используйте правильный стиль для фонового цвета.
Третий важный цвет для использования в приложениях — акцентированный. Данный цвет может использоваться для кнопки Floating Action Button и для различных компонентов. Он должен быть достаточно контрастным по сравнению с основным цветом. Для примера выберем зелёный цвет по цвету глаз рыжих котов. Находим в таблице зелёный цвет и выбираем нужное значение из A400
#00E676
Прописываем цвет в обоих темах:
- @color/accentColor
- @color/accentColor
Сейчас акцентированный цвет мы нигде не увидим. Вернёмся к нему позже.
Акцентированные цвета поддерживаются многими компонентами из коробки. Для некоторых следует использовать аналоги из библиотеки AppCompat:
- Флажки и переключатели
- SwitchCompat вместо Switch
- Курсор у EditText
- Текст у TextInputLayout
- Текущий индикатор у TabLayout
- Выбранный элемент у NavigationView
- Фон у FloatingActionButton
Пользуйтесь сервисом Material Design Color Palette Generator для создания палитры в стиле Material: выбираем основной цвет, цвет «плавающей» кнопки и сайт генерирует необходимую палитру.
В Android 5.0 появился новый атрибут темы colorEdgeEffect. Вам необходимо переопределить тему, а затем применить к компоненту.
res/values/themes.xml res/layout/fragment_list.xml
Темы для диалоговых окон
По умолчанию, диалоговые окна на Lollipop-устройствах будут выглядеть в стиле Material Design. Но если вы хотите немного изменить внешний вид, то можно применить стили и темы к ним. Создайте отдельный стиль:
Добавьте созданный стиль к теме.
Также можно создать отдельный стиль для негативной кнопки, чтобы она была красной и отличалась от позитивной кнопки.
Затем в коде используете созданный стиль.
AlertDialog alertDialog = new AlertDialog.Builder( getActivity(), R.style.AlertDialogCustom_Destructive) .setPositiveButton(R.string.button_delete, new DialogInterface.OnClickListener() < @Override public void onClick(DialogInterface dialogInterface, int i) < // Delete Action >>) .setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() < @Override public void onClick(DialogInterface dialogInterface, int i) < // Cancel Action >>) .setTitle(R.string.title_delete_item) .create();
Сам пока не проверял.
Темы для диалоговых окон для старых устройств
В библиотеке совместимости версии 22.1.0 появилась поддержка Material Design для диалоговых окон.
Для начала вызовем диалоговое окно стандартным способом:
import android.app.AlertDialog; public void onClick(View view)
Добавим стили в файл styles.xml:
Для вывода диалогового окна будем использовать класс из библиотеки совместимости, у которого есть дополнительный параметр для указания темы.
import android.support.v7.app.AlertDialog; public void onClick(View view) < AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AppCompatAlertDialogStyle); builder.setTitle("Dialog"); builder.setMessage("Покормить кота?"); builder.setPositiveButton("OK", null); builder.setNegativeButton("Cancel", null); builder.show(); >
Если хотите также поменять цвет для заголовка диалогового окно, то добавьте в предыдущую тему AppCompatAlertDialogStyle новый элемент windowTitleStyle и создайте стиль для него.
- @style/TitleDialogTextStyle
Стили для компонентов
У компонентов также появились новые стили, связанные с Material Design. Например, TextAppearance.Material.Title:
Темы для компонентов
Обычно темы применялись к активности или приложению. Сейчас самый распространённый вариант Theme.AppCompat.
В Lollipop и AppCompat с версии 22.1 стало возможным присваивать тему отдельному компоненту. В этой связи появился отдельный тип темы ThemeOverlay, который позволяет менять только необходимые настройки. Например, ThemeOverlay.AppCompat.Light меняет фоновый цвет, цвет текста и выделенный текст, как если это была бы светлая тема. Соответственно, ThemeOverlay.AppCompat.Dark работает как тёмная тема.
Это может пригодится, чтобы цвет текста не совпадал с цветом фона и был читаемым. Например, это часто используется у Toolbar:
Также используется при создании собственных тем
Выбор темы в зависимости от версии платформы
Каждая новая версия Android обзаводится несколькими новыми темами. И, возможно, вам захочется использовать новые темы в своих приложениях, чтобы пользователи новых аппаратов увидели современный интерфейс, при этом владельцы старых аппаратов будут видеть приложение в другом виде.
Предположим у вас есть собственная тема, использующая стандартную светлую тему, в файле res/values/styles.xml:
Чтобы задействовать также новую голографическую тему, доступную в Android 3.0 (API Level 11) и выше, создайте альтернативный файл стилей в папке res/values-v11, где будет указана новая тема:
Для последней версии Android 5.0 вам понадобится папка res/values-21 для темы, использующую Material Design.
Теперь программа автоматически будет переключаться между стилями, самостоятельно определяя версию Android.
Список стандартных атрибутов, используемых в темах, можно найти на странице R.styleable.Theme .
Использование стилей и тем платформы
Вы можете создавать собственные стили, а можете подключать готовые системные стили или отдельные атрибуты стилей, используемых в Android. Для указания стиля, предопределённого в Android, используется следующий синтаксис:
style bg-light">
Знак ? применяется для поиска значения стиля в текущей теме, а подстрока ?android означает поиск значения стиля в системной теме Android.
В студии можно выбрать системную тему сразу из среды разработки. Откройте файл разметки в режиме Design. Чуть выше формы имеется выпадающая кнопка AppTheme. Нажмите на неё и поиграйтесь со списком, чтобы просмотреть другие варианты. Вы сможете увидеть, как будет выглядеть ваше приложение в разных темах. Учтите, что эта настройка не вносит изменения в ваш файл, а предназначена только для просмотра темы, чтобы вы представляли, как будет выглядеть программа у разных пользователей.
В последних версиях проектов студии, которые используют тему AppCompat для создания полноэкранной активности используйте стиль:
И примените его к нужной активности.
Новые темы в Android 4.4
В Android 4.4 появилась возможность сделать панель навигации и строку состояния полупрозрачными. Откройте файл styles.xml и добавьте строчки:
Последний пункт у меня закомментирован. Он позволяет настроить тему для ActionBar. Можете поиграться с ним. Для сравнения ниже представлены скриншоты стандартного окна активности с чёрными полосками снизу и сверху и стилизованной активности. Для наглядности я выбрал оранжевый цвет для фона активности.
Если говорить об эволюции тем и стилей, то в Android 2.x темы были в зачаточном состоянии. В Android 3/4 дизайнеры проделали огромную работу, чтобы система стала красивой и предложили тему Holo. В новой версии Android 5.0 работа над стилями была продолжена и была представлена новая концепция стиля под названием Material Design с подробной документацией по её использованию.
В статье Android App Launching Made Gorgeous рассматривается интересный случай, когда неправильное использование тем приводит к некрасивому эффекту - сначала загружается пустой экран, а затем уже экран вашей активности.
Темы для View
В статье говорилось, что отдельные компоненты должны использовать стили, а активности - темы. В Android 5.0 Lollipop, а также старые устройства с API 11 через библиотеку совместимости AppCompat могут также использовать темы:
Небольшой список на память.
- ThemeOverlay.AppCompat
- ThemeOverlay.AppCompat.Light
- ThemeOverlay.AppCompat.Dark
- ThemeOverlay.AppCompat.ActionBar
- ThemeOverlay.AppCompat.Dark.ActionBar
Например, ваши компоненты в разметке используют светлую тему, а родительский элемент должен использовать тёмную тему. Тогда применим к нему готовую тему.
Если вам нужно изменить конкретный атрибут, например, colorAccent, то добавьте новую тему:
Примените тему к вашему компоненту через android:theme="CustomAccentOverlay". Так вы можете переопределить и другие атрибуты.
Общие советы
Избегайте конкретных значений для цвета.
Лучше используйте атрибуты, что позволит вам корректно переключаться к тёмной теме.
В некоторых ситуациях использование готовых значений цвета оправдано.
При работе с элементами темы программным способом не используйте Context от Application, только от Activity.
Kotlin Coding Conventions
Общеизвестные и простые для понимания конвенции по кодингу жизненно важны для любого языка программирования. Здесь Вы ознакомитесь с рекомендациями по стилю кода для проектов, использующих Kotlin.
Настройка стиля в IDE
Две самые популярные IDE для Kotlin — IntelliJ IDEA и Android Studio — обеспечивают мощную поддержку Code Style. Их можно настроить для автоматического форматирования кода в соответствии с заданным стилем.
Применение стиля
- Перейдите в Settings/Preferences | Editor | Code Style | Kotlin.
- Щелкните Set from. .
- Выберите Kotlin style guide.
Убедитесь, что ваш код соответствует выбранному руководству.
- Перейдите в Settings/Preferences | Editor | Inspections | General.
- Включите проверку Incorrect formatting. Дополнительные проверки, проверяющие другие проблемы, описанные в руководстве по стилю (например, соглашения об именах), включены по умолчанию.
Организация кода
Структура каталогов
В проектах на чистом Kotlin рекомендуемая структура каталогов соответствует структуре пакета с опущенным общим корневым пакетом. Например, если весь код находится в пакете org.example.kotlin и вложенных пакетах, то файлы с пакетом org.example.kotlin должны размещаться непосредственно в корневом каталоге, а файлы org.example.kotlin.network.socket должны находиться в подкаталоге корневого каталога network/socket .
В JVM: в проектах, в которых Kotlin используется совместно с Java, файлы Kotlin должны находиться в том же корневом каталоге, что и файлы Java, и иметь ту же структуру каталогов: каждый файл должен находиться в пакете, соответствующем для каждого оператора пакета.
Имена файлов
Если файл Kotlin содержит один класс или интерфейс (допускаются функции / переменные верхнего уровня), его имя должно совпадать с именем класса с расширением .kt . Это относится ко всем типам классов и интерфейсов. Если файл содержит несколько классов или только верхнеуровневые функции / переменные, выберите для файла соответствующее имя. Используйте Camel Case (пример: ProcessDeclarations.kt ).
Имя файла должно описывать работу кода. Избегайте использования бессмысленных слов (таких, как Util ).
Внутренняя организация файла
Мы приветствуем размещение нескольких программных сущностей (классы, верхнеуровневые функции / переменные) в одном файле, если эти сущности тесно связаны друг с другом семантически, а размер файла остаётся разумным и не превышает нескольких сотен строк.
В частности, при создании функций-расширений для класса, актуальных для всех пользователей этого класса, помещайте их в один файл с самим классом. Функции-расширения, используемые только определённым классом, разместите в файле этого класса. Избегайте создания файлов только для хранения всех функций-расширений класса.
Архитектура класса
Содержимое класса должно располагаться в следующем порядке:
- Объявления полей и блоки инициализации.
- Вторичные конструкторы.
- Функции.
- Объект-компаньон.
Не сортируйте функции по алфавиту или по видимости. Не отделяйте простые функции от функций-расширений. Вместо этого, сгруппируйте связанные функции так, чтобы читающий код сверху вниз мог понять логику происходящего. Выберите порядок (сначала функции верхнего уровня или наоборот) и придерживайтесь его. Поместите вложенные классы рядом с кодом, который их использует. Если классы предназначены для внешнего использования и на них нет ссылок внутри класса, поместите их после объекта-компаньона.
Схема реализации интерфейсов
При реализации интерфейса держите реализуемые функции в том же порядке, в котором они описаны в интерфейсе (при необходимости, чередуя их с дополнительными приватными функциями, используемыми в реализации).
Схема перегрузки функций
Всегда размещайте перегружаемые функции рядом друг с другом.
Правила именования
Правила именования пакетов и классов в Kotlin довольно просты:
Имена пакетов всегда пишутся строчными буквами без символов подчёркивания ( org.example.project ). Использовать имена, состоящие из нескольких слов, как правило, не рекомендуется, но если это необходимо, Вы можете соединить их вместе или использовать Camel Case ( org.example.myProject ).
Имена классов используют Upper Camel Case:
open class DeclarationProcessor < /*. */ >object EmptyDeclarationProcessor : DeclarationProcessor() < /*. */ >
Названия функций
Названия функций, полей и локальных переменных начинаются со строчной буквы, пишутся в Camel Case и без подчёркивания:
fun processDeclarations() < /*. */ >var declarationCount = 1
Исключение: фабричные функции, используемые для создания объектов, могут иметь то же имя, что и возвращаемый тип:
interface Foo < /*. */ >class FooImpl : Foo < /*. */ >fun Foo(): Foo
Имена тестовых функций
Для тестов (и только для тестов) Вы можете использовать имена с пробелами, заключённые в обратные скобки. Имейте в виду, такие имена функций не поддерживаются средой выполнения Android. Также, в тестовом коде в именах функций разрешены подчёркивания.
class MyTestCase < @Test fun `ensure everything works`() < /*. */ >@Test fun ensureEverythingWorks_onAndroid() < /*. */ >>
Имена полей
Имена констант (поля, помеченные как const / верхнеуровневые поля / поля классов без геттера, содержащие неизменяемые данные), пишутся в Screaming Snake Case:
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
Верхнеуровневые поля или поля классов, содержащие поведение / изменяемые данные, пишутся в Camel Case:
val mutableCollection: MutableSet = HashSet()
Имена полей, содержащих ссылки на синглтоны, могут использовать тот же стиль наименования, что и объявления объектов:
val PersonComparator: Comparator = /*. */
Для перечислений ( enum ), допускается использовать или Screaming Snake Case ( enum class Color < RED, GREEN >), или Upper Camel Case.
Имена полей-прокладок
Если в классе есть два концептуально одинаковых поля, одно из которых является частью общепринятого API, а второе - деталью реализации, используйте в имени приватного поля нижнее подчёркивание в качестве префикса:
class C < private val _elementList = mutableListOf() val elementList: List get() = _elementList >
Выбирайте хорошие имена
Имя класса обычно представляет из себя существительное или словосочетание, объясняющее, что это за класс: List , PersonReader .
Имя функции обычно представляет из себя глагол или отглагольную фразу, описывающую то, что делает функция: close , readPersons . Имя функции также должно подсказывать, изменяет ли она объект или возвращает новый. Например, sort возвращает существующую отсортированную коллекцию, а sorted возвращает отсортированную новую.
Имена должны точно давать понять цель сущности, поэтому лучше не использовать в именах бессмысленные слова ( Manager , Wrapper ).
При использовании аббревиатур, используйте все заглавные буквы, если аббревиатура состоит из двух букв ( IOStream ); используйте только первую заглавную букву, если аббревиатура длинее ( XmlFormatter , HttpInputStream ).
Форматирование
Отступы
Для отступа используйте 4 пробела. Не используйте табуляцию.
Применяя фигурные скобки, открывающую скобку поместите в конец строки открывающей конструкции, а закрывающую скобку поместите с новой строки; выровняйте её по горизонтали с открывающей конструкцией:
if (elements != null) < for (element in elements) < // . >>
В Kotlin точки с запятой необязательны, поэтому переносы строк имеют значение. Дизайн языка предполагает фигурные скобки в стиле Java, и вы можете столкнуться с неожиданным поведением, если попытаетесь использовать другой стиль форматирования.
Горизонтальные пробелы
- Ставьте пробелы вокруг бинарных операторов ( a + b ). Исключение: не ставьте пробелы вокруг оператора «диапазон до» ( 0..i ).
- Не ставьте пробелы вокруг унарных операторов ( a++ ).
- Используйте пробелы между ключевыми словами ветвлений и циклов ( if , when , for и while ) и соответствующей открывающей скобкой.
- Не ставьте пробел перед открывающей скобкой в объявлении основного конструктора, объявлении метода или вызове метода.
class A(val x: Int) fun foo(x: Int) < . >fun bar()
- Никогда не ставьте пробел после ( , [ или перед ] , )
- Никогда не ставьте пробел вокруг . или ?. : foo.bar().filter < it >2 >.joinToString() , foo?.bar()
- Ставьте пробел после // : // Это комментарий
- Не ставьте пробелы вокруг угловых скобок, используемых для указания параметров типа: class Map
- Не ставьте пробелы вокруг :: : Foo::class , String::length
- Не ставить пробел перед ? используется для обозначения типа, допускающего значение null: String?
Избегайте любого горизонтального выравнивания. Переименование идентификатора в имя другой длины не должно влиять ни на форматирование, ни на что-либо другое.
Двоеточие
Ставьте пробел перед : в следующих случаях:
- при использовании для разделения типа и супертипа
- при делегировании конструктору суперкласса или другому конструктору того же класса
- после ключевого слова объекта
Не ставьте пробел перед : , когда оно разделяет объявление и его тип.
Всегда ставьте пробел после : .
abstract class Foo : IFoo < abstract fun foo(a: Int): T >class FooImpl : Foo() < constructor(x: String) : this(x) < /*. */ >val x = object : IFoo < /*. */ >>
Заголовки классов
Классы с несколькими основными параметрами конструктора можно записать в одну строку:
class Person(id: Int, name: String)
Классы с более длинными заголовками должны быть отформатированы так, чтобы каждый параметр основного конструктора находился в отдельной строке с отступом. Кроме того, закрывающая скобка должна быть на новой строке. Если вы используете наследование, вызов конструктора суперкласса или список реализованных интерфейсов должен располагаться на той же строке, что и скобка:
class Person( id: Int, name: String, surname: String ) : Human(id, name) < /*. */ >
При реализации нескольких интерфейсов, вызов конструктора суперкласса должен быть расположен первым, далее каждый интерфейс должен быть расположен на отдельной строке:
class Person( id: Int, name: String, surname: String ) : Human(id, name), KotlinMaker < /*. */ >
Для классов с длинным списком супертипов поставьте разрыв строки после двоеточия и выровняйте все имена супертипов по горизонтали:
class MyFavouriteVeryLongClassHolder : MyLongHolder(), SomeOtherInterface, AndAnotherOne < fun foo() < /*. */ >>
Чтобы четко разделить заголовок и тело класса, когда заголовок класса длинный, либо поместите пустую строку после заголовка класса (как в приведенном выше примере), либо поместите открывающую фигурную скобку на отдельную строку:
class MyFavouriteVeryLongClassHolder : MyLongHolder(), SomeOtherInterface, AndAnotherOne < fun foo() < /*. */ >>
Используйте обычный отступ (четыре пробела) для параметров конструктора. Это гарантирует, что свойства, объявленные в основном конструкторе, будут иметь тот же отступ, что и свойства, объявленные в теле класса.
Порядок модификаторов
Если объявление имеет несколько модификаторов, всегда располагайте их в следующем порядке:
public / protected / private / internal expect / actual final / open / abstract / sealed / const external override lateinit tailrec vararg suspend inner enum / annotation / fun // as a modifier in `fun interface` companion inline / value infix operator data
Аннотации размещайте перед модификаторами:
@Named("Foo") private val foo: Foo
Если вы не работаете с библиотекой, не используйте лишние модификаторы (например, public ).
Аннотации
Размещайте аннотации на отдельных строках перед объявлением, к которому они относятся, с тем же отступом:
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
Аннотации без аргументов могут быть размещены на одной строке:
@JsonExclude @JvmField var x: String
Одна аннотация без аргументов может быть размещена на той же строке, что и соответствующее объявление:
@Test fun foo() < /*. */ >
Аннотации @file
Аннотации @file помещаются после комментария к файлу (если есть), перед оператором пакета и отделяются от пакета пустой строкой (чтобы подчеркнуть тот факт, что они нацелены на файл, а не на пакет).
/** License, copyright and whatever */ @file:JvmName("FooBar") package foo.bar
Функции
Если сигнатура функции не помещается на одной строке, используйте следующий синтаксис:
fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType, ): ReturnType < // body >
Используйте обычный отступ (четыре пробела) для параметров функции. Это помогает обеспечить согласованность с параметрами конструктора.
Для функций лучше использовать тело как выражение, если тело состоит из одного выражения.
fun foo(): Int < // bad return 1 >fun foo() = 1 // good
Тела как выражения
Если для функции используется тело как выражение, первая строка которого не помещается на той же строке, что и объявление, поставьте знак = на первой строке и сделайте отступ тела-выражения на четыре пробела.
fun f(x: String, y: String, z: String) = veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)
Свойства
Для очень простых свойств только для чтения рассмотрите однострочное форматирование:
val isEmpty: Boolean get() = size == 0
Для более сложных свойств всегда помещайте ключевые слова get и set в отдельные строки:
val foo: String get() < /*. */ >
Для свойств с инициализатором, если инициализатор длинный, добавьте разрыв строки после знака = и отступ инициализатора на четыре пробела:
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
Операторы ветвлений
Если условие оператора if или when является многострочным, всегда используйте фигурные скобки вокруг тела. Отступ каждой последующей строки условия на четыре пробела относительно начала оператора. Поместите закрывающие скобки условия вместе с открывающей фигурной скобкой на отдельной строке:
if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) )
Это помогает согласовать тело условия и инструкции.
Поместите ключевые слова else , catch , finally , а также ключевое слово while цикла do-while на той же строке, что и предыдущая фигурная скобка:
if (condition) < // body >else < // else part >try < // body >finally < // cleanup >
Для оператора when , если ветвь состоит из более чем одной строки, рассмотрите возможность отделения её от соседних блоков case пустой строкой:
private fun parsePropertyValue(propName: String, token: Token) < when (token) < is Token.ValueToken ->callback.visitValue(propName, token.value) Token.LBRACE -> < // . >> >
Поместите короткие ветвления на той же строке, что и условие, без фигурных скобок.
when (foo) < true ->bar() // good false -> < baz() >// bad >
Вызовы методов
В длинных списках аргументов ставьте разрыв строки после открывающей скобки. Аргументы с отступом в четыре пробела. Сгруппируйте несколько тесно связанных аргументов в одной строке.
drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true )
Поместите пробелы вокруг знака = , разделяющего имя аргумента и значение.
Перенос цепочек вызовов
При переносе цепочек вызовов поместите расширение . символ или оператор ?. на следующей строке с одним отступом:
val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile
Перед первым вызовом в цепочке обычно должен быть разрыв строки, но его можно опустить, если код имеет больше смысла.
Лямбды
В лямбда-выражениях следует использовать пробелы вокруг фигурных скобок, а также вокруг стрелки, отделяющей параметры от тела. Если вызов принимает одну лямбду, передайте её вне круглых скобок, когда это возможно.
list.filter < it >10 >
При назначении метки для лямбды не ставьте пробел между меткой и открывающей фигурной скобкой:
fun foo() < ints.forEach lit@< // . >>
При объявлении параметров в многострочном лямбда-выражении поместите параметры в первую строку, затем стрелку, далее с новой строки:
appendCommaSeparated(properties) < prop ->val propertyValue = prop.get(obj) // . >
Если список параметров слишком длинный и не помещается на одной строке, поместите стрелку на отдельной строке:
foo < context: Context, environment: Env ->context.configureEnv(environment) >
Замыкающие запятые
Замыкающая запятая — это символ запятой после последнего элемента последовательности элементов:
class Person( val firstName: String, val lastName: String, val age: Int, // завершающая запятая )
Использование замыкающих запятых имеет несколько преимуществ:
- Это делает контроль версий чище, так как все внимание сосредоточено на измененном значении.
- Это упрощает добавление и изменение порядка элементов — нет необходимости добавлять или удалять запятую, если вы манипулируете элементами.
- Это упрощает генерацию кода, например, для инициализаторов объектов. Последний элемент также может иметь запятую.
Замыкающие запятые необязательны — ваш код будет работать и без них. Руководство по стилю Kotlin поощряет использование замыкающих запятых в месте объявления и оставляет его на ваше усмотрение для места вызова.
Чтобы включить замыкающие запятые в инструменте форматирования IntelliJ IDEA, перейдите в раздел Settings/Preferences | Editor | Code Style | Kotlin, откройте вкладку Other и выберите параметр Use trailing comma.
enum class Direction < NORTH, SOUTH, WEST, EAST, // trailing comma >
fun shift(x: Int, y: Int) < /*. */ >shift( 25, 20, // trailing comma ) val colors = listOf( "red", "green", "blue", // trailing comma )
Свойства и параметры класса
class Customer( val name: String, val lastName: String, // trailing comma ) class Customer( val name: String, lastName: String, // trailing comma )
Параметры функций
fun powerOf( number: Int, exponent: Int, // trailing comma ) < /*. */ >constructor( x: Comparable, y: Iterable, // trailing comma ) <> fun print( vararg quantity: Int, description: String, // trailing comma ) <>
Параметры с необязательным типом (включая сеттеры)
val sum: (Int, Int, Int) -> Int = fun( x, y, z, // trailing comma ): Int < return x + y + x >println(sum(8, 8, 8))
Суффиксы индексов
class Surface < operator fun get(x: Int, y: Int) = 2 * x + 4 * y - 10 >fun getZValue(mySurface: Surface, xValue: Int, yValue: Int) = mySurface[ xValue, yValue, // trailing comma ]
Параметры в лямбдах
fun main() < val x = < x: Comparable, y: Iterable, // trailing comma -> println("1") > println(x) >
fun isReferenceApplicable(myReference: KClass) = when (myReference) < Comparable::class, Iterable::class, String::class, // trailing comma ->true else -> false >
Литералы в коллекциях
annotation class ApplicableFor(val services: Array) @ApplicableFor([ "serializer", "balancer", "database", "inMemoryCache", // trailing comma ]) fun run() <>
Аргументы типов
fun foo() <> fun main() < foo< Comparable, Iterable, // trailing comma >() >
Параметры типов
class MyMap < MyKey, MyValue, // trailing comma ><>
Деструктуризация объявлений
data class Car(val manufacturer: String, val model: String, val year: Int) val myCar = Car("Tesla", "Y", 2019) val ( manufacturer, model, year, // trailing comma ) = myCar val cars = listOf() fun printMeanValue() < var meanValue: Int = 0 for (( _, _, year, // trailing comma ) in cars) < meanValue += year >println(meanValue/cars.size) > printMeanValue()
Комментарии к документации
Для более длинных комментариев к документации поместите начало /** на отдельной строке и начинайте каждую последующую строку со звездочки:
/** * This is a documentation comment * on multiple lines. */
Короткие комментарии можно размещать на одной строке:
/** This is a short documentation comment. */
Как правило, избегайте использования тегов @param и @return . Вместо этого включите описание параметров и возвращаемых значений непосредственно в комментарий к документации и добавьте ссылки на параметры везде, где они упоминаются. Используйте @param и @return только тогда, когда требуется длинное описание, не вписывающееся в основной текст.
// Avoid doing this: /** * Returns the absolute value of the given number. * @param number The number to return the absolute value for. * @return The absolute value. */ fun abs(number: Int): Int < /*. */ >// Do this instead: /** * Returns the absolute value of the given [number]. */ fun abs(number: Int): Int < /*. */ >
Избегайте избыточных конструкций
В общем, если некая синтаксическая конструкция в Kotlin является необязательной и выделена IDE как избыточная, её следует избегать в своем коде. Не оставляйте ненужные синтаксические элементы в коде просто «для ясности».
Возвращаемый тип Unit
Если функция возвращает Unit , тип возвращаемого значения следует опустить:
fun foo() < // ": Unit" is omitted here >
Точки с запятой
Избегайте точек с запятой, когда это возможно.
Строковые шаблоны
Не используйте фигурные скобки при вставке простой переменной в строковый шаблон. Используйте фигурные скобки только для более длинных выражений.
println("$name has $ children")
Идиоматическое использование особенностей языка
Иммутабельность
Предпочитайте использовать иммутабельные данные. Всегда объявляйте локальные переменные и свойства как val , а не как var , если они не изменяются после инициализации.
// Bad: use of mutable collection type for value which will not be mutated fun validateValue(actualValue: String, allowedValues: HashSet) < . >// Good: immutable collection type used instead fun validateValue(actualValue: String, allowedValues: Set) < . >// Bad: arrayListOf() returns ArrayList, which is a mutable collection type val allowedValues = arrayListOf("a", "b", "c") // Good: listOf() returns List val allowedValues = listOf("a", "b", "c")
Значения параметров по умолчанию
Предпочтите объявление функций со значениями параметров по умолчанию объявлению перегруженных функций.
// Bad fun foo() = foo("a") fun foo(a: String) < /*. */ >// Good fun foo(a: String = "a") < /*. */ >
Псевдонимы типов (элиасы)
Если у вас есть функциональный тип или тип с параметрами типа, который используется несколько раз в кодовой базе, предпочтительнее определить для него элиас:
typealias MouseClickHandler = (Any, MouseEvent) -> Unit typealias PersonIndex = Map
Если вы используете частный или внутренний псевдоним типа, чтобы избежать конфликта имен, отдайте предпочтение import . as . , упомянутому в разделе "Пакеты и импорты".
Параметры лямбд
В коротких и не вложенных лямбда-выражениях рекомендуется использовать it вместо явного объявления параметра. Во вложенных лямбда-выражениях с параметрами всегда объявляйте параметры явно.
return в лямбдах
Избегайте использования нескольких помеченных return в лямбда-выражении. Рассмотрите возможность реструктуризации лямбды, чтобы она имела единую точку выхода. Если это невозможно или недостаточно ясно, рассмотрите возможность преобразования лямбды в анонимную функцию.
Не используйте помеченный return для последнего оператора в лямбда-выражении.
Именованные аргументы
Используйте синтаксис именованных аргументов, когда метод принимает несколько параметров одного и того же примитивного типа или для параметров логического типа, если только значение всех параметров не ясно из контекста.
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
Условные операторы
Предпочтительнее использовать форму выражения try , if и when .
return if (x) foo() else bar()
return when(x) < 0 ->"zero" else -> "nonzero" >
Вышеупомянутое предпочтительнее, чем:
if (x) return foo() else return bar()
when(x) < 0 ->return "zero" else -> return "nonzero" >
if или when?
Для бинарных условий, предпочтительнее использовать if вместо when . Например, используйте этот синтаксис с if :
if (x == null) . else .
вместо варианта ниже с when :
when (x) < null ->// . else -> // . >
when использовать предпочтительнее, если есть три или более вариантов.
Nullable булевые значения в условиях
Если вам нужно использовать Boolean значение, допускающее значение null , в условном операторе, используйте проверки if (value == true) или if (value == false) .
Циклы
Предпочтительнее использование функций более высокого порядка (фильтр, хэш-таблица и т. д.) вместо циклов. Исключение: forEach (вместо него лучше использовать обычный цикл for , если получатель forEach не допускает значение null или forEach не используется как часть более длинной цепочки вызовов).
Делая выбор между сложным выражением, использующим несколько функций высшего порядка, и циклом, оцените стоимость операций, выполняемых в каждом случае, и помните о производительности.
Циклы по диапазону
Используйте функцию until для цикла по диапазону:
for (i in 0..n - 1) < /*. */ >// bad for (i in 0 until n) < /*. */ >// good
Строки
Предпочитайте строковые шаблоны конкатенации строк.
Предпочитайте многострочные строки встраиванию управляющих последовательностей \n в обычные строковые литералы.
Чтобы сохранить отступ в многострочных строках, используйте trimIndent , если результирующая строка не требует внутреннего отступа, или trimMargin , когда требуется внутренний отступ:
fun main() < println(""" Not trimmed text """ ) println(""" Trimmed text """.trimIndent() ) println() val a = """Trimmed to margin text: |if(a >1) < | return a |>""".trimMargin() println(a) >
Изучите разницу между многострочными строками Java и Kotlin.
Функции или свойства?
В некоторых случаях функции без аргументов могут быть взаимозаменяемы со свойствами только для чтения. Хотя семантика аналогична, существуют некоторые стилистические соглашения о том, когда следует предпочесть одно другому.
Предпочитайте свойство функции, если базовый алгоритм:
- не выбрасывает исключение
- дёшево вычислить (или кэшировать при первом запуске)
- возвращает тот же результат при вызовах, если состояние объекта не изменилось
Функции-расширения
Свободно используйте функции расширения. Каждый раз, когда у вас есть функция, которая в основном работает с объектом, подумайте о том, чтобы сделать её функцией расширения, принимающей этот объект в качестве получателя. Чтобы свести к минимуму загрязнение API, ограничьте видимость функций-расширений настолько, насколько это имеет смысл. При необходимости используйте локальные функции-расширения, функции-расширения - члены или функции-расширения верхнего уровня с частной видимостью.
Инфиксные функции
Объявляйте функцию как инфиксную только тогда, когда она работает с двумя объектами, играющими одинаковую роль. Хорошие примеры: and , to , zip . Плохой пример: add .
Не объявляйте метод как инфиксный, если он изменяет объект-получатель.
Фабричные функции
Если вы объявляете фабричную функцию для класса, не давайте ей то же имя, что и сам класс. Лучше использовать отдельное имя, чтобы было понятно, почему поведение фабричной функции особенное. Только если особой семантики действительно нет, можно использовать то же имя, что и у класса.
class Point(val x: Double, val y: Double) < companion object < fun fromPolar(angle: Double, radius: Double) = Point(. ) >>
Если у вас есть объект с несколькими перегруженными конструкторами, которые не вызывают разные конструкторы суперкласса и не могут быть сведены к одному конструктору со значениями аргументов по умолчанию, предпочтительнее заменить перегруженные конструкторы фабричными функциями.
Платформенные типы
Общедоступная функция/метод, возвращающая выражение платформенного типа, должна явно объявлять свой тип Kotlin:
fun apiCall(): String = MyJavaApi.getProperty("name")
Любое свойство (на уровне пакета или на уровне класса), инициализированное выражением платформенного типа, должно явно объявлять свой тип Kotlin:
class Person
Локальное значение, инициализированное выражением платформенного типа, может иметь или не иметь объявление типа:
fun main()
Scope-функции apply/with/run/also/let
Kotlin предоставляет набор функций для выполнения блока кода в контексте заданного объекта: let , run , with , apply , и also . Руководство по выбору правильной scope-функции для вашего случая см. в разделе Scope-функции.
Конвенция для библиотек
При написании библиотек рекомендуется соблюдать дополнительный набор правил для обеспечения стабильности API:
- Всегда явно указывайте видимость члена (чтобы избежать случайного раскрытия объявлений как общедоступного API)
- Всегда явно указывайте возвращаемые типы функций и типы свойств (чтобы избежать случайного изменения возвращаемого типа при изменении реализации).
- Предоставляйте комментарии KDoc всем общедоступным участникам, за исключением переопределений, не требующих новой документации (для поддержки создания документации для библиотеки).
Как изменить имя файла в цикле с++ [закрыт]
Закрыт. Этот вопрос необходимо уточнить или дополнить подробностями. Ответы на него в данный момент не принимаются.
Хотите улучшить этот вопрос? Добавьте больше подробностей и уточните проблему, отредактировав это сообщение.
Закрыт 8 лет назад .
Прошу подсказать как можно создавать новое имя для файла в цикле?
Отслеживать
задан 11 авг 2015 в 15:03
Aleksandr Fursenko Aleksandr Fursenko
93 5 5 бронзовых знаков
Fursenko Думаю, что точно также, как и без хоровода вокруг елочки.:)
11 авг 2015 в 15:11
Да как угодно можно его создавать.
11 авг 2015 в 15:12
Есть файл например fs.txt следующий должен иметь другое имя, например fs1.txt?
11 авг 2015 в 15:21
@AleksandrFursenko ну вот вы сами и ответили на свой вопрос как можно создавать имя файла. Берите и реализовывайте озвученный вами алгоритм на нужном языке.
11 авг 2015 в 15:22
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
мой пример посмотрите к примеру. результат работы этой программы три новых файла в корне диска Ц (под виндовс писал).
#include #include #include using namespace std; int main() < ofstream file; // сам файл char filename[2]; // временный буфер string path; // сюда мы будем ложить новые имена for (int i = 0; i < 3; i++)< // цикл который будет создовать новые имена (и создаст новые файлы с новым именем itoa(i, filename, 10); // преоброзовать в текстовый символ path = "C:\\file"; // часть имени файла (оно будет постоянным) path += filename; // собираем путь и имя для нового файла file.open(path.c_str()); // создать новый файл с новым именем file.close(); // обязательно закрываем >return 0; >
Отслеживать
ответ дан 11 авг 2015 в 15:23
9,971 14 14 золотых знаков 53 53 серебряных знака 117 117 бронзовых знаков
А можно вместо 3-х строк (начиная с itoa()) использовать один вызов функции snprintf / При желании можно уйти от ofstream и несколько более эффективно делать файл одной строкой на низком уровне close(open(path, . ))
11 авг 2015 в 17:41
даже затрудняюсь с ответом я этой функцией не умею пользоваться и не знал что функция close() может принимать параметры
11 авг 2015 в 17:51
Так почитайте man-ы. Вы же программист, стыдно не знать основ POSIX.
11 авг 2015 в 18:01
да я как бы на оборот уйти от сишного стиля кодирования хочу. я даже в этом коде не хотел бы использовать настоящий массив и функцию itoa, потому что вместо этого в с++ есть функция to_string(), но она у меня не работает потому что у меня кривой компилятор. вот и пришлось так выкручиваться )
11 авг 2015 в 18:05
Дело Ваше. IMHO надо по возможности использовать лучшие (в широком смысле (здесь и сейчас обсуждать что стоит за этими словами мы, конечно же, не будем)) средства, независимо от их "происхождения".