Tento článek je prvním ze série o knihovně Anko (ANdroidKOtlin) pro vývoj Android aplikací. Další tři části budou na našem blogu vycházet v následujících týdnech. Naučíte se například, jak můžete definovat uživatelské rozhraní přímo v kódu pomocí Anko DSL namísto jeho zápisu do XML. Knihovna Anko se netýká pouze definice uživatelského rozhraní, ale obsahuje mnoho rozšíření, které vám během vývoje Androidu usnadní život.
Kotlin – Úvod do knihovny Anko pro Android (1/4)
Anko je knihovna napsaná v jazyce Kotlin obsahující řadu extension funkcí pro zrychlení a ulehčení vývoje Android aplikací. Je pořád ve fázi intenzivního vývoje a definice uživatelského rozhraní v kódu ještě podle mě není úplně připravena do produkce. Proto my v eManu používáme Anko především díky bohaté knihovně extension funkcí, o kterých si povíme v dalších článcích této série. Myslím si ale, že je velmi zajímavé se naučit, jak můžeme definovat UI přímo v kódu. Tomu se budeme v této sérii článků také věnovat.
verticalLayout { padding = dip(30) val usernameEditText = editText { id = R.id.userNameEditText hintResource = R.string.create_user_hint_username textSize = 24f } }
Přehled dílů série:
1. Vytvoření projektu v Android Studio 3 a první UI
2. Tlačítka, toasty, dialogy, intenty, uživatelská rozhraní a práce na pozadí (background threads)
3. Vylepšujeme naše UI
4. Fragmenty & RecyclerView
1. Vytvoření projektu v Android Studio 3 a první UI
V tomto prvním díle se naučíte, jak vytvořit nový Kotlin Android projekt s konfigurací Gradle souborů krok za krokem. Na závěr si vyzkoušíte, jak napsat UI ve formě přihlašovací obrazovky do aplikace v knihovně Anko DSL.
Poznámka: V druhém díle této série pak do vašeho UI přidáte více logiky.
Import projektu z GitHubu
Android aplikaci, kterou si touto sérií článků vytvoříte, si můžete stáhnout přímo tady z repozitáře na eMan GitHubu, nebo si ji pojďte vytvořit společně se mnou krok za krokem. Ničeho se nebojte, poskytnu vám k tomu kompletní tutoriál.
Popis repozitáře:
- master -> obsahuje finální aplikaci
- part 1 až 4 -> jednotlivý kód aplikace k danému článku
Vytvoření Kotlin Android projektu s použitím Anko
Pro celou sérii článků používám Android Studio 3.0 (verze beta2 v době psaní), Kotlin plugin je tady už jeho součástí, takže není potřeba nic instalovat. Pokud používáte starší verzi AS, instalaci Kotlin pluginu se nevyhnete: Preferences -> Plugins -> Browse repositories.
Nyní je naše AS připraveno pro Kotlin. Jak bychom poznamenali anglicky: Let’s have some fun! 🙂 Při čtení této série o Anko doporučuji používat Android Studio 3, bude to jednodušší.
Začněte vytvořením nového projektu. Vytvořte nový Android projekt z dialogového okna nebo z nabídky: File -> New -> New Project.
Nyní zadejte název aplikace, package, umístění projektu a vaší doménu. Dejte si pozor, abyste nechali zaškrtnutou volbu Include Kotlin support (kterou máme k dispozici právě od AS 3).
Pokud chcete upravit package name, můžete kliknout na tlačítko Edit na pravé straně a změnit si ho ručně, jako jsem to udělal já.
Teď budete vyzváni, abyste zvolili minimální podporovanou verzi Androidu. Zvolíme API 15 pro telefony a tablety.
Nyní zvolte prázdnou aktivitu, kterou si nějak pojmenujte. Já ponechám název MainActivity
.
Po dokončení těchto kroků byste měli mít vytvořený nový Android Kotlin projekt obsahující vaši MainActivity.kt
v Kotlinu.
Konfigurace Gradle
V této části si ukážeme integraci knihovny Anko do projektu. Taky prozradíme, jak si můžete lépe nadefinovat závislosti v Gradle souborech.
Nejprve začněte s hlavním build.gradle
souborem:
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = '1.1.4' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.0-beta2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
Vidíte, že Kotlin plugin přidal definici verze Kotlinu 1.1.4 do ext
. bloku a na classpath byl přidán kotlin-gradle-plugin
.
Nyní se podívejte do souboru build.gradle v app modulu:
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 26 buildToolsVersion "26.0.1" defaultConfig { applicationId "cz.eman.android.sample.anko" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" implementation 'com.android.support:appcompat-v7:26.0.1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.0' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.0' }
Všimněte si řádku č. 3 a č. 5. Zde jsou aplikovány pluginy kotlin-android
a kotlin-android-extension
. Na řádku č. 29 je přidána závislost na standardní Kotlin knihovně, která obsahuje i definici Java SDK a to buď jre7, nebo jre8. Definice JDK byla v Kotlinu zavedena od verze 1.1.
V následujících krocích se naučíte, jak nadefinovat závislosti v Gradle takovým způsobem, abyste měli jejich definici na jednom místě pro celý projekt.
Jak jsem už zmiňoval, bylo by hezké mít všechny definice závislostí v extension DSL bloku v Gradle, např. přesunout definici verzí do ext
. bloku v kořenovém build.gradle
souboru tak, jako to udělal Kotlin plugin pro definici verze Kotlinu. Já osobně dávám přednost definici závislostí v samostatném souboru. Je tedy na vás, co si vyberete. Pokud zvolíte stejnou cestu jako já, pak budete definovat všechny závislosti a konstanty v souboru dependencies.gradle
:
- Přidejte verze závislostí do samostatného pole (versions) a samotnou definici artefaktů pak rozdělte do dalších jednotlivých polí. Verze závislostí můžete přidat na oddělená pole například takto:
- gradlePlugins – gradle pluginy, které v kořenovém
build.gradle
souboru přidáte na classpath - supportDependencies – support prvky knihovny, které budete v projektu používat
- kotlinDependencies – všechny Kotlin knihovny budou tady
- testDependencies – knihovny potřebné pro testovací část
- gradlePlugins – gradle pluginy, které v kořenovém
- Dále přidejte Anko 0.9.1 (v době psaní tohoto článku již Anko 10.1, ale my v produkci používáme verzi 0.9.1, proto jsem se rozhodl ji ještě použít i pro tento článek)
- Java 8 – od AS 3.0 používáme Java 8:
sourceCompatibility: JavaVersion.VERSION_1_8
- t
argetCompatibility: JavaVersion.VERSION_1_8
- Kotlin Standardní knihovna s jre8:
org.jetbrains.kotlin: kotlin-stdlib-jre8
Váš finální soubor dependencies.gradle
by měl tedy vypadat takto:
ext { //Versions versions = [ appVersion : '1.0.0', code : 1, buildTools : "27.0.2", compileSdk : 26, minSdk : 15, targetSdkVersion : 26, sourceCompatibility: JavaVersion.VERSION_1_8, targetCompatibility: JavaVersion.VERSION_1_8, encoding : "UTF-8", gradleBuildTools : '3.0.1', gradle : '4.1', kotlin : '1.2.30', supportLib : '27.0.2', constraintLayout : '1.0.2', anko : '0.9.1', espresso : '3.0.0', junit : '4.12', testRunner : '1.0.0' ] // constants testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" gradlePlugins = [ android: "com.android.tools.build:gradle:${versions.gradleBuildTools}", kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" ] //Support Libraries dependencies supportDependencies = [ appCompat : "com.android.support:appcompat-v7:${versions.supportLib}", constrainLayout: "com.android.support.constraint:constraint-layout:${versions.constraintLayout}" ] kotlinDependencies = [ kotlinStbLib: "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}", ankoSdk15 : "org.jetbrains.anko:anko-sdk15:${versions.anko}" ] testDependencies = [ junit : "junit:junit:${versions.junit}", espressoCore: "com.android.support.test.espresso:espresso-core:${versions.espresso}", testRunner : "com.android.support.test:runner:${versions.testRunner}" ] }
Nyní se zaměřte na build.gradle
soubory. Konkrétně na kořenový:
// Top-level build file where you can add configuration options common to all sub-projects/modules. apply from: 'dependencies.gradle' buildscript { // Load dependencies - Gradle cannot handle with external properties inside of buildscript // So we need to apply external file here again apply from: 'dependencies.gradle' repositories { jcenter() maven { url 'https://maven.google.com' } } dependencies { classpath gradlePlugins.android // Kotlin Grade plugin classpath gradlePlugins.kotlin // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() maven { url 'https://maven.google.com' } } } task wrapper(type: Wrapper) { gradleVersion = versions.gradle } task clean(type: Delete) { delete rootProject.buildDir }
A na ten, co máte v app modulu:
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools defaultConfig { applicationId "cz.eman.android.sample.anko" minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk versionCode versions.code versionName versions.appVersion testInstrumentationRunner rootProject.ext.testInstrumentationRunner } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility versions.sourceCompatibility targetCompatibility versions.targetCompatibility } sourceSets { main.java.srcDirs += 'src/main/kotlin' test.java.srcDirs += 'src/test/kotlin' } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Kotlin implementation kotlinDependencies.kotlinStbLib implementation kotlinDependencies.ankoSdk15 // Support Libraries implementation supportDependencies.appCompat implementation supportDependencies.constrainLayout // Tests testCompile testDependencies.junit androidTestImplementation testDependencies.testRunner androidTestImplementation(testDependencies.espressoCore) { exclude module: 'support-annotations' } }
V kořenovém Gradle souboru je na řádku č. 3 vložená reference na soubor dependencies.gradle
, který musíte znovu vložit do buildscript{}
bloku. Gradle totiž neumí pracovat s externíma propertama uvnitř tohoto bloku. Dále jste přidali referenci na Google Maven repozitář a v dependencies{}
bloku jste přidali Gradle pluginy na classpath.
V app/build.gradle
si můžete všimnout, že přibylo několik závislostí na Anko knihovnu implementation kotlinDependencies.ankoSdk15
. SDK 15 vám říká, že chcete používat Anko s podporou minimální verze Androidu API 15, což odpovídá vaší definici minSdkVersion = 15.
Kromě Anko knihovny s SDK můžete použít i další malé Anko knihovny. Tato knihovna je totiž rozdělena na takové malé knihovničky. To je skvělá zpráva, protože nemusíte mít celou knihovnu, ale stačí vám vložit jen tu část, kterou budete potřebovat. Například pokud chcete používat knihovnu Anko pouze pro práci s RecyclerView
, můžete zahrnout pouze: org.jetbrains.anko: anko-recyclerview-v7
.
Informace o dostupných knihovnách, které můžete používat, najdete na oficiálních stránkách této knihovny.
Nyní máte připravené Gradle soubory a můžete se pomalu pustit do práce s Anko knihovnou. Než začnete, čeká vás ale ještě jeden nepovinný krok. Ti z vás, kteří nechtějí definovat kotlin složku jako zdrojovou v projektu, ho mohou přeskočit.
Kotlin jako zdrojová složka
Jak jsem řekl, tento krok můžete a nemusíte udělat. Pokud chcete zachovat java složku jako zdrojovou, tento krok vynechte.
Poznámka: Pokud budete postupovat podle níže uvedených kroků, uvidíte stále složku java jako zdrojovou v Android Project View, neboť Android Studio v tomto view vloží všechny java/kotlin soubory do složky java.
Přejmenujte složku java (src/main/java)
na kotlin (src/main/kotlin)
.
Nyní musíte do app/build.gradle přidat blok sourceSets
dsl:
Android { ... SourceSets { Main.java.srcDirs + = 'src / main / kotlin' Test.java.srcDirs + = 'src / test / kotlin' }} }}
Podívejte se na strukturu projektu.
V zobrazení projektu Android máte jako zdrojovou složku stále java
:
Přepněte AS projekt do zobrazení projektu (Project) a uvidíte, že zdrojová složka je teď kotlin
:
Problém je popsán zde.
Pokuste se spustit aplikaci, abyste se ujistili, že vše funguje tak, jak má:
Vytvoření prvního view v Anko knihovně
Podívejte se na activity_main.xml
. Vidíte, že vygenerovaný layout má jako kořenový layout android.support.constraint.ConstraintLayout
.
Anko je velmi užitečná a obsahově silná knihovna, ale je ve stádiu vývoje, takže potřebuje ještě trošku času na přidání všech funkcí a podpory pro vytváření UI. Proto samozřejmě i zde narazíte na různé chyby nebo vám budou chybět vámi požadované části.
Jednou z chybějících funkcí je podpora právě zmíněné ConstraintLayout.
Tento problém můžete vyřešit kombinací vytváření layoutu jak v XML, tak i přímo v kódu, čímž docílíte použití ConstraintLayout
. To nicméně není žádoucí, cílem těchto článků je ukázat, jak vytvořit layouty přímo v kódu. Řešení tohoto tématu se tak dozvíte na konci série. Z důvodu tohoto omezení tedy použijte LinearLayout
. Odeberte i závislost na ConstraintLayout
ze samotného projektu (dependencies.gradle, app/build.gradle).
Co vás čeká dál? Vytvoříte aplikaci, která bude mít dva UI layouty:
- Sign In – přihlašovací obrazovka
- Say Hello
Funkce Sign In
V tomto procesu vytvoříte přihlašovací obrazovku obsahující:
- Uživatelské jméno (frosty)
- Heslo (snowman)
- Tlačítko pro přihlášení
Uživatelské jméno a heslo budou předdefinovány (nebudete tady implementovat žádný registrační proces).
Sing In Aktivita
Nyní vytvořte v projektu nový package pod názvem sign_in
, ve kterém vytvořte novou aktivitu (nezapomeňte, že ji vytváříte v Kotlinu 🙂 ).
Váš projekt by měl tedy mít nyní tuto strukturu:
Anko komponenta
Jak už víte, můžete použít Anko DSL přímo v kódu vaší aktivity či fragmentu pro vytvoření layoutu. Teď si ukážeme, jak můžete DSL layout přesunout do samostatné třídy (komponenty) a docílit tak znovupoužitelnosti uživatelského rozhraní v jiné aktivitě či fragmentu.
Vytvořte tak vaši první Anko kompentu AnkoComponent
s definicí UI layoutu, kterou nazvete SignInView
. Poté nastavte SignInActivit
y, aby ji použila jako content view.
class SingInView : AnkoComponent<SignInActivity> { override fun createView(ui: AnkoContext<SignInActivity>): View { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }
Přidejte nový blok editText{}
. Uvnitř tohoto bloku máte samozřejmě přístup ke všem atributům, které jste zvyklí používat v XML, a také ke všem metodám, které můžete volat z kódu. Nejdříve ale musíte definovat unikátní id vašeho editText
widgetu.
Vytvořte soubor ids.xml
, kde budete postupně definovat identifikátory pro vaše widgety:
Teď konečně můžete nastavit id = R.id.username
v editText
bloku.
Nyní přidáte nové atributy hintResource
a textSize = 24f
. Nejdříve ale přidejte nové klíče do strings.xml
.
Teď můžete přidat zmíněné atributy:
Poznámka: Pokud z nějakého důvodu potřebujete použít přímo text namísto reference do strings.xml
, můžete použít atribut hint
.
Kód, který jste napsali, vypadá přehledně a čistě, nemyslíte? Ale ještě něco chybí… Jak jsem řekl, použijete LinearLayout
, takže dalším cílem je obalit do ní váš editText{}
.
LinearLayout
může mít vertikální a horizontální polohu. Tu můžete nastavit pomocí atributu orientace. V Anko máme přímo specifický blok DSL pro vertikální zobrazení – verticalLayout{}
.
Pokud chcete použít LinearLayout
s horizontální orientací, můžete to zapsat takto:
V dalším kroku definujte parametry pro vaše verticalLayout
a usernameEditText
. V Anko se to provede pomocí funkce lparams
:
class SingInView : AnkoComponent<SignInActivity> { override fun createView(ui: AnkoContext<SignInActivity>) = with(ui) { verticalLayout { lparams(width = matchParent, height = matchParent) editText { id = R.id.usernameEditText hintResource = R.string.sign_in_username textSize = 24f }.lparams(width = matchParent, height = wrapContent) } } }
Poznámka: LayoutParams
v Anko definujte hned za ukončením DSL bloku daného view. Toto je doporučení z oficiální dokumentace. Je možné layout parametry definovat uvnitř bloku daného view, ale s určitým omezením. Navíc to ne vždy zafunguje tak, jak očekáváte. Vývojáři Anko knihovny v diskuzích upozorňují, že layout parametry se mají opravdu raději umisťovat až na konec bloku. Více např. v tomto issue na GitHubu.
Nyní je pomalu ten správný čas vyzkoušet vaši aplikaci a zjistit, zda všechno funguje, jak má. Než budete moct aplikaci spustit, musíte dokončit ještě poslední dva kroky:
- přiřadit aktivitě vytvořenou Anko komponentu
- přesměrovat flow aplikace z
MainActivity
doSignInActivity
Otevřete si v AS třídu SignInActivity
. Chcete-li definovat, že content view má být vytvořeno z naší komponenty SignInView (AnkoComponent)
, tak stačí jen přidat do metody onCreate následující řádek kódu (poté, co se zavolá super.onCreate (...)
):
class SignInActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) SingInView().setContentView(this) } }
Přidejte přechod z MainActivity
do SingInActivity
. Nehledejte tady žádnou logiku. Ve 3. díle této série článků o knihovně Anko se naučíte pracovat s extension funkcemi pro zjednodušení práce s intenty, tedy jak můžete spustit novou aktivitu s jejich využitím.
Konečně můžete spustit aplikaci:
Poznámka: Nelekejte se, jak vypadá design vaší aplikace! Úkolem 3. článku bude vaše UI trochu zkrášlit a vylepšit.
Pokud vidíte to, co já na předchozím obrázku, pak vám aplikace naběhla. Také se vám tím pádem povedlo zobrazit UI definované v komponentě (SingInView
). Nyní přidejte další editText
blok pro zadávání hesla.
Nyní už umíte pracovat s editText
blokem, ale jak se definuje tlačítko? Přidání tlačítka je velmi podobné definici editText
bloku. Nejjednodušší způsob, jak přidat tlačítko, je prostě jen napsat:
button("Sign In")
Docela lehké, co říkáte?
Spusťte znovu aplikaci:
Tamtadadá! Vaše tlačítko Sign In
je tam! 🙂
Samozřejmě, že toto byla jen ukázka, takže tlačítko nadefinujte tak, jak byste to udělali v XML. To znamená, že mu nastavíte jeho id atribut a velikost. V dalším článku pak zaregistrujete akci, tedy co se má stát po jeho stisknutí.
Finální kód SignInView.kt
:
class SingInView : AnkoComponent<SignInActivity> { override fun createView(ui: AnkoContext<SignInActivity>) = with(ui) { verticalLayout { lparams(width = matchParent, height = matchParent) editText { id = R.id.usernameEditText hintResource = R.string.sign_in_username textSize = 24f }.lparams(width = matchParent, height = wrapContent) editText { id = R.id.passwordEditText hintResource = R.string.signIn_password textSize = 24f }.lparams(width = matchParent, height = wrapContent) button { id = R.id.signIn_button textResource = R.string.signIn_button }.lparams(width = matchParent, height = wrapContent) } } }
Shrnutí 1. části
V tomto prvním článku jste se naučili, jak vytvořit krok za krokem nový Kotlin Android projekt. Provedli jste konfiguraci Gradle souborů a nakonec jste vytvořili svůj první layout za použití Anko knihovny.
V dalším článku (část 2) přidáte logiku do vaší funkce Sign In. Naučíte se pracovat s vlákny, intenty, toasty a dialogy pomocí knihovny Anko.
Díky za přečtení a snad se potkáme i u dalšího dílu série!