Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Re:Android Studio 설정 살펴보기 및 생산성 올리기

Re:Android Studio 설정 살펴보기 및 생산성 올리기

"DroidKnights 2023"에서 발표한 "Re:Android Studio 설정 살펴보기 및 생산성 올리기" 발표 자료입니다.

pluulove (노현석)

September 12, 2023
Tweet

More Decks by pluulove (노현석)

Other Decks in Programming

Transcript

  1. Search Everywhere Double ⇧ Shift Find Action ⌘ Сmd +

    ⇧ Shift + A Show Context Actions ⌥ Opt + ↩ Enter Navigate between code issues F2 / ⇧ Shift + F2 View recent files ⌘ Сmd + E Complete Current Statement ⌘ Сmd + ⇧ Shift + ↩ Enter Reformat Code ⌘ Сmd + ⌥ Opt + L Invoke refactoring ⌃ Ctrl + T Extend or shrink selection ⌥ Opt + ↑ / ⌥ Opt + ↓ Add/remove line or block comment ⌘ Сmd + / / ⌘ Сmd + ⌥ Opt + / Go To Declaration ⌘ Сmd + B Find Usages ⌥ Opt + F7 Focus the Project tool window ⌘ Сmd + 1 Focus the editor ⎋ Esc IntelliJ IDEA੄ ਬਊೠ ױ୷ః IntelliJ IDEA keyboard shortcuts : https://www.jetbrains.com/help/idea/mastering-keyboard-shortcuts.html Reformat Code ⌘ Сmd + ⌥ Opt + L View recent files ⌘ Сmd + E Show Context Actions ⌥ Opt + ↩ Enter Find Action ⌘ Сmd + ⇧ Shift + A Extend or shrink selection ⌥ Opt + ↑ / ⌥ Opt + ↓ Go To Declaration ⌘ Сmd + B Find Usages ⌥ Opt + F7 Navigate between code issues F2 / ⇧ Shift + F2 Focus the Project tool window ⌘ Сmd + 1 Invoke refactoring ⌃ Ctrl + T
  2. • Inlay Hints • Surround With… • Complete Current Statement

    • Chose Content to Paste • Highlighted Error • Move Between method • Refactor • Change Signature • Extract/Introduce • Recent Files • Quick Definition • Quick Lists • My Productivity • Logcat > Fold Lines Like This • Compare • Put parameters on separate lines • Interactively Rebase • Version Control > Shelf • Layout Editor • Live Templates • File and Code Templates • Logcat Color • Postfix Completion • Method Separators Demo…
  3. Live Templates ❏ ߈ࠂ,ઑѤ,ࢶ঱ ١ ੌ߈੸ੋ ҳޙਸ ௏٘ী ࢗੑ Live

    templates : https://www.jetbrains.com/help/idea/using-live-templates.html#live_templates_types
  4. Postfix Completion ❏ ੑ۱ೠ ಴അध ઱ਤী 
 మ೒݁ ௏٘ܳ ୶о

    ❏ ੼(.) ٍী ৘ডয ੑ۱ೞҊ ࢶఖೞݶ మ೒݁ ௏٘о ୶о
  5. Chose Content to Paste ⌘ Сmd + ⇧ Shift +

    V / Edit - Paste - Paste from History
  6. Plugin੄ ೙ਃࢿ ❏ Android Studio੄ ӝࠄ ӝמ੄ ইए਑ ❏ ӝࠄ

    ઁҕ ࣻળҗ ೐۽ં౟ ҳઑ৬੄ ࠛӐഋ ❏ ࠁੌ۞ ೒ۨ੉౟ ௏٘ ഑਷ ز੘ٜ ❏ ழझథ ӝמ੄ ೙ਃࢿ
  7. Module ࢤࢿ ױ҅ ੿੄ class FeatureModuleDescriptionProvider : ModuleDescriptionProvider { override

    fun getDescriptions(project: Project) = listOf<ModuleGalleryEntry>( FeatureModuleTemplateGalleryEntry() ) } Module ࢤࢿ਷ 1ױ҅݅ ੿੄ ೙ਃী ٮۄ ୶о ࢶ঱ https://cs.android.com/android-studio/platform/tools/adt/idea/+/mirror-goog-studio-main:android-npw/src/com/android/tools/idea/ npw/module/ModuleDescriptionProvider.kt
  8. class FeatureModuleTemplateGalleryEntry : ModuleGalleryEntry { override val icon: Icon =

    PluuIcons.Konata override val name: String = PluuBundle.message(“...”) override val description: String = PluuBundle.message(“...”) override fun toString(): String = name override fun createStep( project: Project, moduleParent: String, projectSyncInvoker: ProjectSyncInvoker ): SkippableWizardStep<*> { val basePackage = getSuggestedProjectPackage() val model = NewFeatureModuleModel.fromExistingProject( ... п Step߹ ز੘ ੿੄ ݽٕ ݾ۾ী ֢୹ೡ ੿ࠁ
  9. override val name: String = PluuBundle.message(“...”) override val description: String

    = PluuBundle.message(“...”) override fun toString(): String = name override fun createStep( project: Project, moduleParent: String, projectSyncInvoker: ProjectSyncInvoker ): SkippableWizardStep<*> { val basePackage = getSuggestedProjectPackage() val model = NewFeatureModuleModel.fromExistingProject( ... ) return ConfigureFeatureModuleStep(model, LOWEST_ACTIVE_API, basePackage, name) } } п Step߹ ز੘ ੿੄ 1.Wizard ࢤࢿী ೙ਃೠ ੿ࠁ 2.ੑ۱ ಬ ੿ࠁ ੿੄
  10. class ConfigureFeatureModuleStep( model: NewFeatureModuleModel, minSdkLevel: Int, basePackage: String?, title: String

    ) : ConfigureModuleStep<NewFeatureModuleModel>(...) { private val appName = JBTextField(model.applicationName.get()) private val bytecodeCombo = BytecodeLevelComboProvider().createComponent() private val conventionPluginCheckbox = JBCheckBox("Use Gradle convention plugin") override fun createMainPanel(): DialogPanel = panel { row(contextLabel("Module name", AndroidBundle.message("android.wizard.module.help.name"))) { ੑ۱ ಬ ੑ۱ਵ۽ ߉ਸ UI ੿੄ import javax.swing.JTextField; import javax.swing.JComboBox; import javax.swing JCheckBox;
  11. private val bytecodeCombo = BytecodeLevelComboProvider().createComponent() private val conventionPluginCheckbox = JBCheckBox("Use

    Gradle convention plugin") override fun createMainPanel(): DialogPanel = panel { row(contextLabel("Module name", AndroidBundle.message("android.wizard.module.help.name"))) { cell(moduleName).align(AlignX.FILL) } ... row("Language") { cell(languageCombo).align(AlignX.FILL) } ... row { cell(conventionPluginCheckbox).align(AlignX.FILL) } }.withBorder(empty(6)) ੑ۱ ಬ JBCheckBox JTextField JTextField
  12. class NewFeatureModuleModel( projectModelData: ProjectModelData, template: NamedModuleTemplate, moduleParent: String, override val

    formFactor: ObjectProperty<FormFactor>, override val category: ObjectProperty<Category>, commandName: String = "New Module", override val isLibrary: Boolean = false, wizardContext: WizardUiContext ) : ModuleModel( ... ) { val bytecodeLevel = OptionalValueProperty(getInitialBytecodeLevel()) val conventionPlugin = BoolValueProperty(true) ModuleModel ੿੄ ModuleStepীࢲ ࢎਊೡ ؘ੉ఠ
  13. wizardContext: WizardUiContext ) : ModuleModel( ... ) { val bytecodeLevel

    = OptionalValueProperty(getInitialBytecodeLevel()) val conventionPlugin = BoolValueProperty(true) override val renderer = object : ModuleTemplateRenderer() { override val recipe: Recipe get() = { td: TemplateData -> generateFeatureModule( data = td as ModuleTemplateData, bytecodeLevel = bytecodeLevel.value, useVersionCatalog = true, useConventionPlugins = conventionPlugin.get() ) } } ModuleModel ੿੄ ModuleModel੄ ೨ब ౵ੌ ࢤࢿ ۽૒੉ ઓ੤
  14. fun RecipeExecutor.generateFeatureModule( data: ModuleTemplateData, useKts: Boolean = false, addLintOptions: Boolean

    = false, bytecodeLevel: BytecodeLevel = BytecodeLevel.default, useVersionCatalog: Boolean = false, useConventionPlugins: Boolean = false ) { ... createDefaultDirectories(moduleOut, srcOut) addIncludeToSettings(data.name) val gradleFile: String = if (useConventionPlugins) { buildFeatureGradle( ౵ੌ ࢤࢿ 1.ಫ؊ ࢤࢿ 2.settings.gradle ୶о
  15. ... createDefaultDirectories(moduleOut, srcOut) addIncludeToSettings(data.name) val gradleFile: String = if (useConventionPlugins)

    { buildFeatureGradle( isKts = useKts, applicationId = data.namespace, useVersionCatalog = useVersionCatalog ) } else { buildGradle(...) } save(gradleFile, moduleOut.resolve(FN_BUILD_GRADLE)) if (isLibraryProject) { if (useConventionPlugins) { ౵ੌ ࢤࢿ Custom build.gradle Default build.gradle build.gradle ੷੢
  16. } save(gradleFile, moduleOut.resolve(FN_BUILD_GRADLE)) if (isLibraryProject) { if (useConventionPlugins) { //

    build-logic applyPlugin(PluuPlugin.Convension.LIBRARY, null) applyPlugin(PluuPlugin.Convension.HILT, null) } else { applyPlugin(PluuPlugin.Android.LIBRARY, null) } } if (!useConventionPlugins) { addKotlinIfNeeded(...) requireJavaVersion(...) } ౵ੌ ࢤࢿ plugins { id 'pluu.android.library' id 'pluu.android.hilt' } plugins { id 'com.android.library' }
  17. addKotlinIfNeeded(...) requireJavaVersion(...) } if (data.useGenericLocalTests) { addLocalTests(...) addTestDependencies() } if

    (data.useGenericInstrumentedTests) { addInstrumentedTests(...) addTestDependencies() } save( generateManifest( hasApplicationBlock = !isLibraryProject, theme = "@style/${data.themesData.main.name}", ), ౵ੌ ࢤࢿ Unit Test Instrument Test
  18. addInstrumentedTests(...) addTestDependencies() } save( generateManifest( hasApplicationBlock = !isLibraryProject, theme =

    "@style/${data.themesData.main.name}", ), manifestOut.resolve(FN_ANDROID_MANIFEST_XML) ) save(gitignore(), moduleOut.resolve(".gitignore")) proguardRecipe(moduleOut, isLibraryProject) } ౵ੌ ࢤࢿ AndroidManifest.xml Proguard gitignore
  19. val sampleActivitySetupTemplate get() = template { name = ... minApi

    = MIN_API description = ... category = Category.Activity formFactor = FormFactor.Mobile screens = listOf( WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule WizardTemplateProvider Activity, Fragment, Application, Folder, Service, UiComponent, Automotive, XML, Wear, AIDL, Widget, Google, Compose, Other; Mobile, Wear, Tv, Automotive, Generic;
  20. val sampleActivitySetupTemplate get() = template { name = ... minApi

    = MIN_API description = ... category = Category.Activity formFactor = FormFactor.Mobile screens = listOf( WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule WizardTemplateProvider Activity, Fragment, Application, Folder, Service, UiComponent, Automotive, XML, Wear, AIDL, Widget, Google, Compose, Other; Mobile, Wear, Tv, Automotive, Generic; Category
  21. category = Category.Activity formFactor = FormFactor.Mobile screens = listOf( WizardUiContext.ActivityGallery,

    WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule ) lateinit var activityClass: StringParameter val layoutName = stringParameter { name = "Layout Name" default = "activity_main" suggest = { activityToLayout(activityClass.value) } help = "The name of the layout to create for the activity" constraints = listOf(LAYOUT, UNIQUE, NONEMPTY) WizardTemplateProvider Wizardо ࠁৈ૕ ਤ஖
  22. WizardUiContext.NewProject, WizardUiContext.NewModule ) lateinit var activityClass: StringParameter val layoutName =

    stringParameter { name = "Layout Name" default = "activity_main" suggest = { activityToLayout(activityClass.value) } help = "The name of the layout to create for the activity" constraints = listOf(LAYOUT, UNIQUE, NONEMPTY) } activityClass = stringParameter { ... suggest = { layoutToActivity(layoutName.value) } } WizardTemplateProvider ୶ୌ ౵ੌݺ ੑ۱ಬ ղ੄ ׮ܲ ೦ݾب ࢎਊ оמ
  23. suggest = { layoutToActivity(layoutName.value) } } val useBinding = enumParameter<ViewBindingType>

    { name = "Use binding" default = ViewBindingType.ViewBinding help = "Help" } val isViewModel = booleanParameter { name = "Use ViewModel" default = true help = "(Default true), Use ViewModel" } val viewModelClass = stringParameter { ... } val packageNameParam = defaultPackageNameParameter WizardTemplateProvider Enum Boolean
  24. val viewModelClass = stringParameter { ... } val packageNameParam =

    defaultPackageNameParameter widgets( TextFieldWidget(activityClass), TextFieldWidget(layoutName), CheckBoxWidget(isViewModel), TextFieldWidget(viewModelClass), EnumWidget(useBinding), Separator, PackageNameWidget(packageNameParam) ) thumb { File("empty-activity").resolve("template_empty_activity.png") } recipe = { data -> sampleActivitySetup( WizardTemplateProvider ۨ੉ইਓ ߓ஖
  25. thumb { File("empty-activity").resolve("template_empty_activity.png") } recipe = { data -> sampleActivitySetup(

    moduleData = data as ModuleTemplateData, packageName = packageNameParam.value, activityClass = activityClass.value, layoutName = layoutName.value, isUsedViewModel = isViewModel.value, viewModelClass = viewModelClass.value, viewBindingType = useBinding.value ) } } • WizardTemplateProvider ౵ੌ ࢤࢿ ਃ୒
  26. fun RecipeExecutor.sampleActivitySetup(...) { // Add, Dependencies if (!useConventionPlugin) { addAllKotlinDependencies(moduleData)

    } addMaterialDependency(useAndroidX) addDependency("com.android.support:appcompat-v7:$appCompatVersion.+") addDependency("com.android.support.constraint:constraint-layout:+") if (isUsedViewModel) { addDependency("androidx.activity:activity-ktx:+") addLifecycleDependencies(true) } // Add, Manifest ౵ੌ ࢤࢿ Gradle ઙࣘࢿ ੿੄ Support ۄ੉࠳ߡܻח ࢤࢿद AndroidX۽ ؀୓ؽ
  27. addDependency("androidx.activity:activity-ktx:+") addLifecycleDependencies(true) } // Add, Manifest generateManifest( moduleData, activityClass, packageName,

    isLauncher = false, hasNoActionBar = false, generateActivityTitle = false ) // Create, Activity val simpleActivityPath = srcOut.resolve("$activityClass.$ktOrJavaExt") val simpleActivity = basicActivityKt( isNewProject = moduleData.isNewModule, applicationPackage = projectData.applicationPackage, packageName = packageName, ౵ੌ ࢤࢿ AndroidManifest.xmlী ୶о
  28. generateActivityTitle = false ) // Create, Activity val simpleActivityPath =

    srcOut.resolve("$activityClass.$ktOrJavaExt") val simpleActivity = basicActivityKt( isNewProject = moduleData.isNewModule, applicationPackage = projectData.applicationPackage, packageName = packageName, useAndroidX = useAndroidX, activityClass = activityClass, layoutName = layoutName, viewBindingType = viewBindingType, isUsedViewModel = isUsedViewModel, viewModelClass = viewModelClass, ) save(simpleActivity, simpleActivityPath) open(simpleActivityPath) ౵ੌ ࢤࢿ Custom Activity Template
  29. ) save(simpleActivity, simpleActivityPath) open(simpleActivityPath) // Create, Layout if (viewBindingType.isDataBinding) {

    save( generateDataBindingSimpleXml(activityClass, packageName), resOut.resolve("layout/${layoutName}.xml") ) } else { generateSimpleLayout(moduleData, activityClass, layoutName) } open(resOut.resolve("layout/${layoutName}.xml")) // Create, ViewModel if (isUsedViewModel) { val viewModel = viewModelKt(packageName, useAndroidX, viewModelClass) ౵ੌ ࢤࢿ DataBindingਊ Layout ࢤࢿ ӝઓ SimpleLayout ഝਊ
  30. generateSimpleLayout(moduleData, activityClass, layoutName) } open(resOut.resolve("layout/${layoutName}.xml")) // Create, ViewModel if (isUsedViewModel)

    { val viewModel = viewModelKt(packageName, useAndroidX, viewModelClass) val viewModelPath = srcOut.resolve("${viewModelClass}.${ktOrJavaExt}") save(viewModel, viewModelPath) open(viewModelPath) } // Enable, BuildFeature if (viewBindingType.isViewBinding) { setBuildFeature("viewBinding", true) } else if (viewBindingType.isDataBinding) { setBuildFeature("dataBinding", true) } ౵ੌ ࢤࢿ ViewModel ౵ੌ ࢤࢿ
  31. save(viewModel, viewModelPath) open(viewModelPath) } // Enable, BuildFeature if (viewBindingType.isViewBinding) {

    setBuildFeature("viewBinding", true) } else if (viewBindingType.isDataBinding) { setBuildFeature("dataBinding", true) } if (!useConventionPlugin && generateKotlin) { requireJavaVersion("1.8", true) } } ౵ੌ ࢤࢿ BuildFeature ഝࢿച
  32. Summary Plugin File Template ࢤࢿ դ੉ب ׮ࣻ ౵ੌ ױੌ ౵ੌ

    Live Template ௏٘ ࠶۟ ઁড ࢎ೦ য۰਑ ए਑ ~ ઺р ए਑ AS ߸҃ द ࣻ੿ ೙ਃ Plugin Plugin
  33. Plugin Summary ❏ Android Studio Wizardਊ ੗ܐח డহ੉ ࠗ઒ ❏

    Android Studio ࣗझ ௏٘ীࢲ ଺ח ߑߨ੉ Ѩ࢝੄ 80% ❏ ೐۽ં౟੄ ழझథ ࣻળী ٮۄࢲ Template ഑਷ Wizard ࢶఖ