XML v Androidu: layouty, zdroje a manifest

Android popisuje rozvržení obrazovek, texty, barvy, ikony i celou aplikaci v XML souborech. Kapitola vysvětluje layouty a jejich atributy, odkazy na zdroje, lokalizaci textů, manifest a vektorové obrázky a ukazuje, jak se layout propojí s kódem v Kotlinu.

Autor: Aktualizováno

Kde se v Android projektu používá XML

Klasické Android UI (systém View) odděluje vzhled od logiky: co je na obrazovce, popisuje XML, co se stane po kliknutí, řeší kód. XML ale najdeš i mimo obrazovky:

Soubor nebo složkaObsah
AndroidManifest.xmlpopis aplikace: aktivity, služby, oprávnění, ikona
res/layout/rozvržení obrazovek a položek seznamů
res/values/texty (strings.xml), barvy, rozměry, styly a motivy
res/drawable/vektorové obrázky, tvary a výběry stavů (selector)
res/menu/, res/navigation/menu a navigační grafy
res/xml/další konfigurace: nastavení, pravidla zálohování, síťová bezpečnost

Google dnes pro nové obrazovky doporučuje Jetpack Compose, kde se UI píše přímo v Kotlinu. XML layouty tím nezmizely — běží na nich většina existujících aplikací a manifest, texty, motivy i vektorové ikony zůstávají v XML i v projektech s Compose.

Layout: strom prvků

Soubor v res/layout/ je strom: kořen je kontejner (LinearLayout, FrameLayout, ConstraintLayout…), listy jsou ovládací prvky. Název elementu je přímo název třídy Viewu:

res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/nadpis"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/vitejte"
        android:textSize="20sp" />

    <Button
        android:id="@+id/pokracovat"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/mezera"
        android:text="@string/pokracovat" />
</LinearLayout>
  • Všechny atributy frameworku patří do jmenného prostoru http://schemas.android.com/apk/res/android, který se podle konvence váže na prefix android. Bez deklarace xmlns:android na kořeni se layout nezkompiluje.
  • Každý prvek musí mít layout_width a layout_height. Atributy s předponou layout_ nejsou vlastnosti prvku samotného, ale pokyny pro jeho rodiče.
  • Prvky z knihoven (AndroidX, Material) se píšou plným názvem třídy, například <com.google.android.material.button.MaterialButton>.
JednotkaPoužití
dprozměry a okraje; nezávislé na hustotě displeje (160 dp ≈ 1 palec)
spvelikost písma; navíc respektuje nastavení velikosti písma v systému
match_parentprvek zabere celý rozměr rodiče
wrap_contentprvek bude jen tak velký, jak potřebuje jeho obsah
0dp (v ConstraintLayout)velikost určí omezení (constraints) na obou stranách

Tři jmenné prostory: android, app a tools

ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:contentDescription="@string/avatar_popis"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/jmeno"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@id/avatar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@id/avatar"
        tools:text="Jana Nováková" />
</androidx.constraintlayout.widget.ConstraintLayout>
  • android: — atributy definované samotným Androidem.
  • app: s URI http://schemas.android.com/apk/res-auto — atributy z knihoven a z vlastních komponent. Adresa res-auto je zvláštní: nástroje sestavení ji při kompilaci nahradí balíčkem, ve kterém je atribut skutečně deklarovaný.
  • tools: s URI http://schemas.android.com/tools — pokyny jen pro Android Studio. tools:text zobrazí ukázkový text v náhledu, ale do aplikace se nedostane.

Je to názorná ukázka, jak fungují jmenné prostory: atribut text z prostoru android a text z prostoru tools jsou pro parser dva různé atributy.

Odkazy na zdroje

Hodnoty se do layoutu většinou nepíšou natvrdo, ale odkazují na zdroje. Při sestavení z nich vznikne třída R s číselnými identifikátory.

ZápisVýznam
@+id/nadpisvytvoří nový identifikátor prvku (v kódu R.id.nadpis)
@id/avatarodkaz na už existující identifikátor
@string/vitejtetext z res/values/strings.xml
@dimen/mezera, @color/primarnirozměr nebo barva z res/values
@drawable/ic_sipkaobrázek nebo vektor z res/drawable
@android:string/okzdroj ze systémového frameworku Androidu
?attr/colorPrimaryhodnota atributu z aktuálního motivu (tématu)

Texty a lokalizace

Texty jsou v res/values/strings.xml. Překlad je stejný soubor ve složce s kvalifikátorem jazyka — pro češtinu values-cs. Android vybere složku podle jazyka zařízení a chybějící texty doplní z výchozí složky:

res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- res/values/strings.xml – výchozí (anglické) texty -->
<resources>
    <string name="app_name">Shopping list</string>
    <string name="vitejte">Welcome!</string>
    <string name="pocet_polozek">%1$d items</string>
    <string name="uvozovky">Don\'t forget the \"milk\"</string>
</resources>
res/values-cs/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- res/values-cs/strings.xml – použije se v češtině -->
<resources>
    <string name="app_name">Nákupní seznam</string>
    <string name="vitejte">Vítejte!</string>
    <plurals name="polozky">
        <item quantity="one">%d položka</item>
        <item quantity="few">%d položky</item>
        <item quantity="many">%d položky</item>
        <item quantity="other">%d položek</item>
    </plurals>
</resources>
  • Kromě pravidel XML (&amp;, &lt;) platí v textech i vlastní escapování Androidu: apostrof a uvozovky je potřeba zapsat jako \' a \", jinak sestavení skončí chybou.
  • Čeština má víc tvarů množného čísla, proto používej <plurals> místo skládání „položka/položky/položek“ v kódu.
  • Parametry ve formátovacích řetězcích čísluj (%1$d, %2$s), aby je překlad mohl přeházet.

AndroidManifest.xml

Manifest popisuje aplikaci pro systém a obchod Google Play: které aktivity a služby obsahuje, která je vstupní (ikona v launcheru), jaká potřebuje oprávnění a jakou má ikonu a název.

app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.NakupniSeznam">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
  • Název balíčku se dnes neuvádí atributem package v manifestu, ale jako namespace a applicationId v souboru build.gradle.
  • Od Androidu 12 musí mít každá aktivita, služba nebo přijímač s <intent-filter> výslovně uvedený atribut android:exported.
  • Výsledný manifest vzniká slučováním manifestů aplikace a všech knihoven. Co se nakonec dostalo do aplikace, ukáže v Android Studiu záložka Merged Manifest.

Vektorové ikony (vector drawable)

Android má vlastní vektorový formát, který se podobá SVG. Atribut pathData používá stejnou syntaxi příkazů jako d v SVG, a Android Studio proto umí SVG převést (Vector Asset Studio). Nepodporuje ale vše — filtry, CSS nebo vložené rastry při převodu zmizí.

res/drawable/ic_sipka.xml
<!-- res/drawable/ic_sipka.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="?attr/colorOnSurface"
        android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" />
</vector>

Propojení s kódem

Layout se v aktivitě načte metodou setContentView. Místo hledání prvků přes findViewById se dnes používá View Binding: pro každý layout se vygeneruje třída s vlastnostmi pojmenovanými podle android:id (pokracovat → binding.pokracovat).

MainActivity.kt
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // res/layout/activity_main.xml -> třída ActivityMainBinding
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.pokracovat.setOnClickListener {
            binding.nadpis.text = resources.getQuantityString(R.plurals.polozky, 3, 3)
        }
    }
}

Hotová aplikace (APK, AAB) už XML neobsahuje jako text. Nástroj AAPT2 layouty i manifest při sestavení převede do binárního XML, takže po rozbalení APK uvidíš nečitelná data. Čitelnou podobu vrátí například apktool d aplikace.apk nebo APK Analyzer v Android Studiu.