Как отформатировать код в андроид студио
Перейти к содержимому

Как отформатировать код в андроид студио

  • автор:

Автоматический Code Improvement при коммите в Android Studio

Работать с гитом в Android Studio, как известно, можно двумя способами.

Первый способ классический — использовать консоль. Плюсом такого подхода являются в первую очередь надежность. GUI может залагать и например, зависнуть на этапе rebase так, что abort/continue/переключение на другую ветку не поможет. Консольные команды вам в помощь, они всегда безотказны.

Второй способ — использовать GUI, предоставляемый Android Studio. Плюсы очевидны — меньший порог входа, более понятно, что вообще можно делать. А также есть различные плюшки от самой студии для удобства работы с гитом. Об одном из них и пойдет речь. Кстати, используя GUI, тоже можно оставлять в покое мышку и использовать hotkey

Используемые Hotkey в статье

Shift+Shift (двойное нажатие shift) — Окно поиска. Позволяет искать как код/ресурсы, так и различные действия и настройки.

Ctrl+Alt+L (⌘+⌥+L) → Форматирование кода

Shift+Ctrl+Alt+L (⇧+⌘+⌥+L) → Форматирование кода → Форматирование кода с параметрами.

вызывает данное окно

image

О чем вообще речь и где найти?

Неактуальные импорты, пустые строки, пробелы вместо табов — эти вещи бесят, если они попадают в готовый код, предназначенный для pull request. Исправить это легко, используя hotkey Ctrl+Alt+L, однако часто мы забываем это делать.
Idea/AndroidStudio позволяет проводить эти действия автоматически, в последний момент перед коммитом

При использовании commit’а через Android Studio можно увидеть такого вида окно:

Нас интересуют галочки, которые находятся справа от списка файлов, в блоке Before Commit. При активации этих помощников, они будут автоматически применяться при каждом коммите

Чем именно мне помогут эти галочки?

✓ Reformat code

Данное улучшение приводит код в соответствии с вашим code style. Действие полностью аналогично ctrl+shift+alt+L с включенным clean up.

Настроить code style можно здесь Settings → Editor → Code Style

Tab and Indents

Применяет отступы строк в соответствии с code style (Подробно в Code Style, в одноименном разделе)

В примере пробелы, используемые для формирования отступа (отмечены точками), будут замены табами (отмечены стрелкой)

class CleanTab(context: Context)
class CleanTab(context: Context) < ->val date = Date() -> val button = Button(context) -> val textView = TextView(context) -> val ratingBar = RatingBar(context) -> val imageView = ImageView(context) >

Spaces

Форматирует все пробелы в коде в соответствии с code-style.

Будут удалены лишние пробелы в конструкторе Date, добавлены пробелы между объявлением переменной button и =, а также между списком аргументов args в main и

class CleanSpaces(context:Context) < val date = Date( ) val button= Button(context) fun main(arg: Args)< >>
class CleanSpaces(context: Context) < val date = Date() val button = Button(context) fun main(arg: Args) < >>

Wrapping and Braces

Этот блок управляет переносом строк. Переносятся как сами строки кода, так и закрывающие/открывающие скобки.

Строчка else, а также строчка catch должны располагаться на той же линии, что и закрывающая скобка (так настроено в code style)
Общее количество символов на строке с функцией manyArguments вышло за 80 знаков(настроено в code style), поэтому аргументы будут перенесены на новую строку

class CleanWrappingAndBraces < fun condition() < if (liveData != null) < > else < > > fun catching() < try < > catch (e: Exception) < > > fun manyArguments(userId: Int, issuerCountryId: String, sendingCountryId: String, receivingCountryId: String) < >>
class CleanWrappingAndBraces < fun condition() < if (liveData != null) < > else < > > fun catching() < try < > catch (e: Exception) < > > fun manyArguments(userId: Int, issuerCountryId: String, sendingCountryId: String, receivingCountryId: String)

Blank Lines

Регулирует максимальное и минимальное количество пустых строчек в коде. (Подробно в Code Style, в одноименном разделе)

В данном примере удалит лишние строки от последней функции до >, а также 2 лишних пустых строчки между функциями

class CleanBlank < fun foo() < >fun bar() < >>
class CleanBlank < fun foo() < >fun bar() < >>

Rearrange code

Располагает атрибуты и элементы в том порядке, который соответствует код-стайлу. Работает в XML и HTML файлах. Настроить расположение можно в Setting → Editor → CodeStyle → XML/HTML → Arrangement

Внимание! неправильная настройка может привести к печальным последствиям. Rearrange может влиять на очередность элементов, что может привести к неправильной работе в файлах, чувствительных к расположению. Пример: в LinearLayout, при сортировке по алфавитному порядку, элемент Button может переместится выше TextView, хотя по дизайну такого быть не должно. Выход — настраивать расположение ТОЛЬКО атрибутов

Сортировка

В данном примере xmlns расположились в трех первых строчках и строго по порядку, определенному в code-style(xmlns:android на первой строчке, остальные xmlns: <. >в алфавитном порядке.
Обратите внимание, xmlns:tools и xmlns:app не были удалены даже несмотря на то, что нет случаев их использования. Это говорит о том, что rearrange влияет только на расположение элементов

Сортировка

В данном примере блок с атрибутами android:padding изменит порядок в соответствии с code-style. Также атрибут style расположится сразу за android:id

Optimize import

Следит за чистотой блока импортов и приводит в правильный вид, а также проверяет на соответствие code style. Само действие полностью аналогично ctrl+shift+alt+L с включенным optimize import.

Удаление неиспользуемых импортов

В данном примере при optimize imports import java.util.* будет удален, так как нет полей или методов, нуждающихся в нем

import android.content.Context import android.widget.Button import java.util.* class RemoveUnused(context: Context)
import android.content.Context import android.widget.Button class RemoveUnused(context: Context)

Объединение импортов

Если n или более классов импортируются из одного пакета, то такой импорт будет заменен импортом всего пакета.
Если m или более enum или java static импортируется из одного класса, то такой импорт будет заменен на импорт всего класса

Оба параметра задаются в Settings → Editor → Code Style → Kotlin → Imports

В данном примере множественные импорты из пакета android.widget будут заменены на android.widget.*

import android.content.Context import android.widget.Button import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView import android.widget.RatingBar import java.util.* class MergeImport(context: Context) < >
import android.content.Context import android.widget.* import java.util.* class MergeImport(context: Context) < >

В том случае, если стоит настройка не объединять импорты(m = 0, n=0), объединенные импорты(импорт пакета) будут заменены импортом каждого класса/функции/поля отдельно.
В случае ниже import android.widget. и import java.util. будут упразднены

import android.content.Context import android.widget.* import java.util.* class MergeImport(context: Context)
import android.content.Context import android.widget.Button import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView import android.widget.RatingBar import java.util.* class MergeImport(context: Context) < >

Расположение по алфавиту

После применения, все импорты отсортируются, и будут удобно располагаться в алфавитном порядке

import android.content.Context import android.widget.Button import android.widget.ImageView import android.widget.TextView import java.util.* import android.widget.RatingBar class SortImport(context: Context) < >
import android.content.Context import android.widget.Button import android.widget.ImageView import android.widget.RatingBar import android.widget.TextView import java.util.* class SortImport(context: Context) < >

Perform code analysis

Помогает находить проблемные места. Носит информационный характер и никак не меняет код. Самые частые случаи — неиспользуемые параметры, переменные, использование кода, помеченного как Deprecated

Пример кода с множествеными нарушениями

fun TextView.format(message: String)

Перед коммитом студия подскажет, что есть проблемы с кодом и предложит их посмотреть внимательнее:

При нажатии на Review покажется список найденных предупреждений:

Check TODO

Очень полезная галочка, помогает не забыть о новых TODO, которые были поставлены именно в этом коммите. Более того, студия позволяет отслеживать не только //TODO, но и //FIXME, а также любые собственные todo, которые были добавлены через Setting → Editor → TODO.

Также можно фильтровать список todo, которые студия будет проверять.

Так выглядит подсказка студии, что был добавлен todo. При нажатии на review он покажет список всех новых todo и предложит их пересмотреть

Clean up

Очень помогает держать код в чистоте. Удаляет избыточный код (см. примеры), заменяет deprecated функции. Действие полностью аналогично Actions → Code cleanup. Достаточно интересная функция, но вместе с этим может быть и опасной.

Из замеченных особенностей можно выделить две основные:

  • замена deprecated методов (подробнее ниже)
  • непонятную замену вызова методов view в презентере в одном из личных случаев

! Обязательно пересматривайте получившийся коммит, если используете данную функцию!

Redutant code

После применения удалятся элвис-операторы, т.к. были применены к non-nullable переменным

data class User(val firstName: String, val secondName: String) val user = User("Василий", "Пупкин") fun getUserFullName(): String
data class User(val firstName: String, val secondName: String) val user = User("Василий", "Пупкин") fun getUserFullName(): String

После применения удалится оператор public, т.к. конкретно в этом случае лишний

data class User(val firstName: String, val secondName: String) val user = User("Василий", "Пупкин") public fun getUserFullName(): String
data class User(val firstName: String, val secondName: String) val user = User("Василий", "Пупкин") fun getUserFullName(): String

После применения удалится вызов конструктора

class ExecutionClass(val exec: () -> Unit) val exec = ExecutionClass()
class ExecutionClass(val exec: () -> Unit) val exec = ExecutionClass

Deprecated код

Если в аннотации deprecated указан ReplaceWith(), то при cleanup старые методы и классы будут заменены в соответствии с этой пометкой. Надо быть очень внимательным с применением этой возможности, так как работает все очень банально — название старого метода меняется ровно на то, что было указано в ReplaceWith(). Анализатор не проверят даже существует ли такой код. Поэтому неправильно указание нового класса/метода может привести к невозможности компиляции кода. И даже если такая функция/класс существует, даже если у нее одинаковая сигнатура(что анализатор также НЕ проверяет), то это может привести к другой проблеме, необходимо было не просто заменить одну функцию на другую, но и изменить сценарий использования. Код в таком случае скомпилируется, но не будет правильно работать

Заменит старый метод и класс на новые

@Deprecated("Будет удален", ReplaceWith("newMethod()")) fun oldMethod() = Unit fun newMethod() = Unit @Deprecated("Будет удален", ReplaceWith("NewClass")) class OldClass class NewClass class GetDeprecatedCodeUseCase(val clazz: OldClass) < init < val initData = oldMethod() >>
@Deprecated("Будет удален", ReplaceWith("newMethod()")) fun oldMethod() = Unit fun newMethod() = Unit @Deprecated("Будет удален", ReplaceWith("NewClass")) class OldClass class NewClass class GetDeprecatedCodeUseCase(val clazz: NewClass) < init < val initData = newMethod() >>

Выводы и рекомендация

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

Из приведенных выше примеров можно понять несколько моментов:

Optimize import и Reformat code можно использовать безбоязненно. Они влияют только на форматирование и явные огрехи.

Rearrange и Clean up необходимо использовать аккуратно. Да, они в целом могут хорошо помочь и выловить уже что-то посерьезнее, но при неправильной настройке (Rearrange) или неоднозначном коде (Clean up) помощники могут сильно ошибиться

Check TODO и Perform code analysis также можно использовать безбоязненно. Они никаким образом не влияют на код, только дают назойливые подсказки. Да, если у вас в проекте все полностью построено на TODO и Deprecated коде, то отбоя от них не будет, и они больше будут мозолить глаза. Но если у вас в проекте достаточно чистый код и такие моменты стараетесь минимизировать, то помощники будут давать отличный шанс пересмотреть код, где вы могли допустить упущение.

Горячие клавиши в Android Studio

Android Tools

Теперь разберёмся, как сделать кучу полезных действий при сохранении.

В Eclipse есть настройка Save Actions, т.е. при сохранении вы ещё какие-то действия производите с кодом.

Находится она по пути Preferenses-Java-Editor-Save Actions

Безымянный

Тут я ставлю форматировать код (Ctrl+Shift+F) и подправить все сторонние пакеты в заголовке (Ctrl+Shift+O).

В общем экономим целых два нажатия горячих клавиш!

Как это удобно — не опишешь словами.Лучше один раз попробовать.

А вот теперь поищем такую настройку в Android Studio и не найдем.

Но голь на выдумку хитра и товарищи предложили переопределить сочетание Ctrl+S на запуск макроса, который всю эту работу быстренько и проделает!

Макрос можно записать не отходя от кассы: Edit — Macros- StartMacroRecording

— делаем форматирование (Ctrl+Shift+F)

— организуем импорты (Ctrl+Shift+O)

— по вкусу добавить свои действия

Как сделали, что хотели, сохраняем макрос под свои именем (например formatted save).

Теперь идем File-Settings-Keymap-Macros выделяем наш макрос и жмем правой кнопкой мыши по нему в меню Add Keyboard ShortCut.

Безымянный

IDE скажет, что у нас есть конфликт с другой горячей клавишей, но мы его переопределим.

БезымянныйБезымянныйВот и всё, так можно и в магазине стенку убрать, если постараться)

Зачем нам это?

При написании кода всегда неплохо соблюдать какой-то общепринятый codestyle. Однако делать это вручную, даже в современных IDE практически нереально — рано или поздно вы всё равно где-то забудете об этом.

Особенно это важно, когда вы работаете в команде. Если я привык почти после каждого изменения нажимать ⌘+⌥+L (автоформатирование), то далеко не все члены команды делают то же самое.

Плюсом к этому идут такие «косяки», как wildcard-импорты.

Конечно же, код даже с большими проблемами в кодстайле успешно скомпилируется, и работать будет не хуже, чем написанный «правильно». Но ведь всегда приятнее, когда код отформатирован с учётом общепринятых стандартов, диффы будут читаться легче, да и коллеги будут меньше ругаться 🙂

Как решить проблему?

С этим нам поможет Spotless! Он позволяет форматировать (и проверять форматирование) код на множестве языков, но нас в данном случае интересует Kotlin.

Для работы с Kotlin он использует ktlint.

Стандартные правила, которые проверяет ktlint перечислены в его README, но давайте я продублирую некоторые из них тут:

  • 4 пробела в качестве отступов;
  • Отсутствие точек с запятыми («;») там, где их не должно быть — важно для тех, кто переключается с Java;
  • Отсутствие неиспользуемых импортов;
  • Отсутствие лишних пустых строк;
  • Отсутствие пустых строк перед закрывающей фигурной скобкой («>»);
  • Отсутствие лишних пробелов в конце строк;
  • Отсутствие указания ненужного возвращаемого типа Unit у функций (например, fun fn: Unit <> );
  • Отсутствие пустых фигурных скобок, если у класса нет тела;
  • И т.д.

Как видите, штука довольно полезная. Давайте настроим — это очень просто!

Интеграция с проектом

Итак, мы предполагаем, что у нас проект для Android, в котором используется Gradle и Kotlin.

Spotless выпускается в том числе в виде Gradle-плагина. В первую очередь нам нужно добавить его в проект.

Добавьте в корневой build.gradle (т.е. не в тот, что в модуле, например, app.gradle , а в тот, что в проекте, в его корневой директории), в блок dependencies строчку: classpath ‘com.diffplug.spotless:spotless-plugin-gradle:3.27.0’ , например:

buildscript  
repositories google()
jcenter()

>
dependencies classpath "com.android.tools.build:gradle:4.0.0-alpha07"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.diffplug.spotless:spotless-plugin-gradle:3.27.0'
>
>

allprojects repositories google()
jcenter()
>
>

Далее создайте файл spotless.gradle, и добавьте в него следующий код:

apply plugin: "com.diffplug.gradle.spotless"

spotless java target '**/*.java'
googleJavaFormat().aosp()
removeUnusedImports()
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
>
kotlin target '**/*.kt'
ktlint()
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
>
format 'misc', target '**/*.gradle', '**/*.md', '**/.gitignore'
indentWithSpaces()
trimTrailingWhitespace()
endWithNewline()
>

format 'xml', target '**/*.xml'
indentWithSpaces()
trimTrailingWhitespace()
endWithNewline()
>
>

Главный блок конфигурации — spotless . В нём содержатся правила для различных языков и форматов файлов.

Рассмотрим правила для Java:

target задаёт маску файлов, для которых применяются правила. Например, target ‘**/*.java’ говорит о том, что правила будут применяться для файлов с расширением .java внутри любой директории в проекте.

Далее мы перечисляем список правил, которые будут применяться для этого:

googleJavaFormat().aosp()
removeUnusedImports()
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()

В принципе, названия правил отлично описывают их, так что, думаю, разъяснять каждое не требуется.

Далее мы задаём правила для Kotlin, XML и некоторых других файлов — Markdown , .gradle , .gitignore .

Теперь нужно включить этот конфиг в сборку — например, в app/build.gradle :

apply from: "$project.rootDir/spotless.gradle"

Теперь app/build.gradle будет выглядеть примерно так:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

apply from: "$project.rootDir/spotless.gradle"
// .

Теперь синхронизируйте Gradle.

Troubleshooting

Возможно, Gradle при синхронизации выдаст такую ошибку:

Cannot add task ‘clean’ as a task with that name already exists

В таком случае из корневого файла build.gradle нужно удалить блок:

task clean(type: Delete)  delete rootProject.buildDir
>

Настройка Android Studio

Чтобы Android Studio сразу форматировала код правильно, необходимо немного её настроить.

В первую очередь, установите ktlint. В macOS это удобно сделать через Homebrew:

$ brew install ktlint

Теперь в терминале перейдите в директорию проекта, и выполните команду:

$ ktlint --android applyToIDEAProject

И последнее — осталось выключить Wildcard-импорты в самой Android Studio. Откройте настройки, Editor -> Code Style -> Java , поставьте чекбокс Use single class import , и увеличьте значения Class count to use import with ‘*’ и Names count to use static import with ‘*’ на какое-то большое значение, например, 99:

Для Kotlin достаточно лишь поставить галочки Use single name import :

Использование Spotless

Пользоваться Spotless ещё проще, чем настраивать. Нам нужны две основные команды:

./gradlew spotlessCheck
./gradlew spotlessApply

Первая команда запускает проверки и выдаёт ошибку, если какая-то проверка не прошла.

Вторая команда запускает автоформатирование кода. С помощью неё можно исправить ошибки автоматически во всём проекте. Но не все — она исправляет только форматирование, но не, например, wildcard импорты — их нужно исправить вручную.

Теперь просто перед каждым пушем в Git выполняйте эти две команды, и всё будет хорошо 🙂

Бонус: интеграция с Travis CI

В идеале правила Spotless должны проверяться в том числе и при сборке в CI.

Для Travis CI достаточно в блок script добавить строчку ./gradlew spotlessCheck , и при каждом запуске сборки Travis будет проверять код, и, если проверка не прошла, сборка прервётся с ошибкой.

script: 
- "./gradlew spotlessCheck"
- "./gradlew :library:clean :library:build :library:connectedCheck -PdisablePreDex --stacktrace"
- "./gradlew :app:clean :app:build :app:connectedCheck -PdisablePreDex --stacktrace"

Полный пример конфига можно посмотреть здесь.

Полную интеграцию, включая Travis CI, можно увидеть в этом репозитории.

Как отформатировать код в андроид студио

МЕРОПРИЯТИЯ

Всероссийский хакатон по биометрии

Комментарии

Популярные По порядку
Не удалось загрузить комментарии.

ВАКАНСИИ

Преподаватель на курс БД SQL в Proglib.Academy
по итогам собеседования

Методист-педагогический дизайнер в Proglib.Academy
по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

Мобильная разработка на Python: обзор двух фреймворков

Мобильная разработка на Python – одно из перспективных направлений. В статье автор рассматривает два фреймворка с их недостатками и преимуществами.

10 мобильных приложений, которые научат вас программировать

Ищете курсы, которые научат вас программировать? Мы собрали лучшие мобильные приложения, с которыми учиться можно даже в дороге.

15 классных идей для создания своего мобильного приложения

Если думали, что неплохо бы проверить свои знания разработки приложений на практике или наоборот начать учиться в процессе, предлагаем вам подборку идей для приложений.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *