Layout nelle visualizzazioni

Prova Compose
Jetpack Compose è il toolkit UI consigliato per Android. Scopri come lavorare con i layout in Compose.

Un layout definisce la struttura di un'interfaccia utente nella tua app, ad esempio in un' attività. Tutti gli elementi del layout vengono creati utilizzando una gerarchia di View e ViewGroup oggetti. Un oggetto View in genere disegna qualcosa che l'utente può vedere e con cui può interagire. Un ViewGroup è un container invisibile che definisce la struttura del layout per gli oggetti View e altri oggetti ViewGroup, come mostrato nella Figura 1.

Figura 1. Illustrazione di una gerarchia di oggetti View che definisce un layout UI.

Gli oggetti View sono spesso chiamati widget e possono essere una delle tante sottoclassi, come Button o TextView. Gli oggetti ViewGroup sono in genere chiamati layout e possono essere di molti tipi che forniscono una struttura di layout diversa, come LinearLayout o ConstraintLayout.

Puoi dichiarare un layout in due modi:

  • Dichiarare gli elementi UI in XML. Android fornisce un vocabolario XML semplice che corrisponde alle classi e alle sottoclassi View, come quelle per widget e layout. Puoi anche utilizzare l'editor di layout di Android Studio per creare il layout XML utilizzando un'interfaccia di tipo drag-and-drop.

  • Creare un'istanza degli elementi di layout in fase di runtime. La tua app può creare View e ViewGroup oggetti e manipolarne le proprietà a livello di programmazione.

La dichiarazione dell'UI in XML consente di separare la presentazione dell'app dal codice che ne controlla il comportamento. L'utilizzo di file XML semplifica anche la fornitura di layout diversi per diverse dimensioni e orientamenti dello schermo. Questo argomento è trattato più nel dettaglio in Supportare diverse dimensioni dello schermo.

Il framework Android ti offre la flessibilità di utilizzare uno o entrambi questi metodi per creare l'UI della tua app. Ad esempio, puoi dichiarare i layout predefiniti della tua app in XML e poi modificarli in fase di runtime.

Scrivere il codice XML

Utilizzando il vocabolario XML di Android, puoi progettare rapidamente i layout UI e gli elementi dello schermo che contengono, nello stesso modo in cui crei pagine web in HTML con una serie di elementi nidificati.

Ogni file di layout deve contenere esattamente un elemento principale, che deve essere un View o ViewGroup oggetto. Dopo aver definito l'elemento principale, puoi aggiungere altri oggetti o widget di layout come elementi secondari per creare gradualmente una gerarchia View che definisce il layout. Ad esempio, ecco un layout XML che utilizza un oggetto LinearLayout verticale per contenere un oggetto TextView e un oggetto Button:

<?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" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

Dopo aver dichiarato il layout in XML, salva il file con l'estensione .xml nella directory res/layout/ del progetto Android in modo che venga compilato correttamente.

Per ulteriori informazioni sulla sintassi di un file XML di layout, consulta la sezione Risorsa di layout.

Caricare la risorsa XML

Quando compili la tua app, ogni file di layout XML viene compilato in una risorsa View. Carica la risorsa di layout nell'implementazione del callback Activity.onCreate() della tua app. Per farlo, chiama setContentView(), passagli il riferimento alla risorsa di layout nel formato: R.layout.layout_file_name. Ad esempio, se il layout XML viene salvato come main_layout.xml, caricalo per la tua Activity come segue:

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

Il framework Android chiama il metodo di callback onCreate() nella tua Activity quando viene avviata.Activity Per ulteriori informazioni sui cicli di vita delle attività, consulta la sezione Introduzione alle attività.

Attributi

Ogni View e ViewGroup oggetto supporta la propria varietà di attributi XML. Alcuni attributi sono specifici di un oggetto View. Ad esempio, TextView supporta l'attributo textSize. Tuttavia, questi attributi vengono ereditati anche da tutti gli oggetti View che estendono questa classe. Alcuni sono comuni a tutti gli oggetti View, perché vengono ereditati dalla classe View principale, come l'attributo id. Altri attributi sono considerati parametri di layout, ovvero attributi che descrivono determinati orientamenti di layout dell'oggetto View, come definiti dall'oggetto ViewGroup principale dell'oggetto.

ID

A qualsiasi oggetto View può essere associato un ID intero per identificare in modo univoco l'oggetto View all'interno dell'albero. Quando l'app viene compilata, questo ID viene fatto riferimento come un intero, ma in genere viene assegnato nel file XML di layout come stringa nell'attributo id. Si tratta di un attributo XML comune a tutti gli oggetti View ed è definito dalla classe View. Lo utilizzi molto spesso. La sintassi di un ID all'interno di un tag XML è la seguente:

android:id="@+id/my_button"

Il simbolo at (@) all'inizio della stringa indica che il parser XML analizza ed espande il resto della stringa ID e la identifica come risorsa ID. Il simbolo più (+) indica che si tratta di un nuovo nome di risorsa che deve essere creato e aggiunto alle risorse nel file R.java.

Il framework Android offre molte altre risorse ID. Quando fai riferimento a un ID risorsa Android, non hai bisogno del simbolo più, ma devi aggiungere lo spazio dei nomi del pacchetto android come segue:

android:id="@android:id/empty"

Lo spazio dei nomi del pacchetto android indica che stai facendo riferimento a un ID dalla classe di risorse android.R, anziché alla classe di risorse locale.

Per creare visualizzazioni e farvi riferimento dalla tua app, puoi utilizzare un pattern comune come segue:

  1. Definisci una visualizzazione nel file di layout e assegnagli un ID univoco, come nell' esempio seguente:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
  2. Crea un'istanza dell'oggetto di visualizzazione e acquisiscila dal layout, in genere nel onCreate() metodo, come mostrato nell'esempio seguente:

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)

    Java

    Button myButton = (Button) findViewById(R.id.my_button);

La definizione degli ID per gli oggetti di visualizzazione è importante quando si crea un oggetto RelativeLayout. In un layout relativo, le visualizzazioni di pari livello possono definire il proprio layout rispetto a un'altra visualizzazione di pari livello, a cui viene fatto riferimento tramite l'ID univoco.

Un ID non deve essere univoco in tutto l'albero, ma deve essere univoco nella parte dell'albero in cui esegui la ricerca. Spesso potrebbe essere l'intero albero, quindi è meglio renderlo univoco, se possibile.

Parametri di layout

Gli attributi di layout XML denominati layout_something definiscono i parametri di layout per l'oggetto View appropriati per l'oggetto ViewGroup in cui si trova.

Ogni classe ViewGroup implementa una classe nidificata che estende ViewGroup.LayoutParams. Questa sottoclasse contiene tipi di proprietà che definiscono le dimensioni e la posizione di ogni visualizzazione secondaria, in base al gruppo di visualizzazioni. Come mostrato nella Figura 2, il gruppo di oggetti View principale definisce i parametri di layout per ogni vista bambino, inclusa la vista bambino.

Figura 2. Visualizzazione di una gerarchia di oggetti View con parametri di layout associati a ogni oggetto View.

Ogni sottoclasse LayoutParams ha una propria sintassi per l'impostazione dei valori. Ogni elemento secondario deve definire un oggetto LayoutParams appropriato per il relativo elemento principale, anche se potrebbe definire un oggetto LayoutParams diverso per i propri elementi secondari.

Tutti i gruppi di visualizzazioni includono una larghezza e un'altezza, utilizzando layout_width e layout_height, e ogni visualizzazione deve definirli. Molti oggetti LayoutParams includono margini e bordi facoltativi.

Puoi specificare la larghezza e l'altezza con misurazioni esatte, ma non è consigliabile farlo spesso. Più spesso, utilizzi una di queste costanti per impostare la larghezza o l'altezza:

  • wrap_content: indica alla visualizzazione di ridimensionarsi in base alle dimensioni richieste dai contenuti.
  • match_parent: indica alla visualizzazione di diventare grande quanto il gruppo di oggetti View principale lo consente.

In generale, non è consigliabile specificare la larghezza e l'altezza di un layout utilizzando unità assolute come i pixel. Un approccio migliore consiste nell'utilizzare misurazioni relative, come le unità di pixel indipendenti dalla densità (dp), wrap_content o match_parent, perché aiuta l'app a essere visualizzata correttamente su una varietà di dimensioni dello schermo del dispositivo. I tipi di misurazione accettati sono definiti in Risorsa di layout.

Posizione del layout

Una visualizzazione ha una geometria rettangolare. Ha una posizione, espressa come una coppia di coordinate sinistra e superiore, e due dimensioni, espresse come larghezza e altezza. L'unità per la posizione e le dimensioni è il pixel.

Puoi recuperare la posizione di una visualizzazione richiamando i metodi getLeft() e getTop(). Il primo restituisce la coordinata sinistra (x) del rettangolo che rappresenta la visualizzazione. Il secondo restituisce la coordinata superiore (y) del rettangolo che rappresenta la visualizzazione. Questi metodi restituiscono la posizione della visualizzazione rispetto al relativo elemento principale. Ad esempio, quando getLeft() restituisce 20, significa che la visualizzazione si trova 20 pixel a destra del bordo sinistro del relativo elemento principale diretto.

Inoltre, esistono metodi pratici per evitare calcoli non necessari: getRight() e getBottom(). Questi metodi restituiscono le coordinate dei bordi destro e inferiore del rettangolo che rappresenta la visualizzazione. Ad esempio, la chiamata a getRight() è simile al seguente calcolo: getLeft() + getWidth().

Dimensioni, spaziatura interna e margini

Le dimensioni di una visualizzazione sono espresse con una larghezza e un'altezza. Una visualizzazione ha due coppie di valori di larghezza e altezza.

La prima coppia è nota come larghezza misurata e altezza misurata. Queste dimensioni definiscono le dimensioni che una visualizzazione vuole avere all'interno del relativo elemento principale. Puoi ottenere le dimensioni misurate chiamando getMeasuredWidth() e getMeasuredHeight().

La seconda coppia è nota come larghezza e altezza, o a volte larghezza di disegno e altezza di disegno. Queste dimensioni definiscono le dimensioni effettive della visualizzazione sullo schermo, al momento del disegno e dopo il layout. Questi valori potrebbero, ma non devono, differire dalla larghezza e dall'altezza misurate. Puoi ottenere la larghezza e l'altezza chiamando getWidth() e getHeight().

Per misurare le dimensioni, una visualizzazione tiene conto della spaziatura interna. La spaziatura interna è espressa in pixel per le parti sinistra, superiore, destra e inferiore della visualizzazione. Puoi utilizzare la spaziatura interna per spostare il contenuto della visualizzazione di un numero specifico di pixel. Ad esempio, una spaziatura interna a sinistra di due sposta il contenuto della visualizzazione di due pixel a destra del bordo sinistro. Puoi impostare la spaziatura interna utilizzando il metodo setPadding(int, int, int, int) ed eseguirne una query chiamando getPaddingLeft(), getPaddingTop(), getPaddingRight() e getPaddingBottom().

Sebbene una visualizzazione possa definire una spaziatura interna, non supporta i margini. Tuttavia, i gruppi di visualizzazioni supportano i margini. Per ulteriori informazioni, consulta ViewGroup e ViewGroup.MarginLayoutParams.

Per ulteriori informazioni sulle dimensioni, consulta la sezione Dimensione.

Oltre a impostare i margini e la spaziatura interna a livello di programmazione, puoi anche impostarli nei layout XML, come mostrato nell'esempio seguente:

  <?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" >
      <TextView android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

L'esempio precedente mostra l'applicazione di margini e spaziatura interna. L'oggetto TextView ha margini e spaziatura interna uniformi applicati su tutti i lati, mentre l'oggetto Button mostra come applicarli in modo indipendente a bordi diversi.

Layout comuni

Ogni sottoclasse della classe ViewGroup fornisce un modo univoco per visualizzare le visualizzazioni nidificate al suo interno. Il tipo di layout più flessibile e quello che fornisce gli strumenti migliori per mantenere la gerarchia di layout superficiale è ConstraintLayout.

Di seguito sono riportati alcuni dei tipi di layout comuni integrati nella piattaforma Android.

Creare un layout lineare

Organizza gli elementi secondari in una singola riga orizzontale o verticale e crea una barra di scorrimento se la lunghezza della finestra supera la lunghezza dello schermo.

Creare app web in WebView

Visualizza le pagine web.

Creare elenchi dinamici

Quando i contenuti del layout sono dinamici o non predeterminati, puoi utilizzare RecyclerView o una sottoclasse di AdapterView. RecyclerView è in genere l'opzione migliore, perché utilizza la memoria in modo più efficiente rispetto a AdapterView.

I layout comuni possibili con RecyclerView e AdapterView includono i seguenti:

Elenco

Visualizza un elenco a scorrimento a colonna singola.

Griglia

Visualizza una griglia a scorrimento di colonne e righe.

RecyclerView offre più possibilità e l'opzione per creare un gestore di layout personalizzato.

Riempire una visualizzazione dell'adattatore con i dati

Puoi popolare un AdapterView come ListView o GridView mediante l'associazione dell'istanza AdapterView a un Adapter, che recupera i dati da un'origine esterna e crea un View che rappresenta ogni voce di dati.

Android fornisce diverse sottoclassi di Adapter utili per recuperare diversi tipi di dati e creare visualizzazioni per un AdapterView. I due adattatori più comuni sono:

ArrayAdapter
Utilizza questo adattatore quando l'origine dati è un array. Per impostazione predefinita, ArrayAdapter crea una visualizzazione per ogni elemento dell'array chiamando toString() su ogni elemento e inserendo i contenuti in un oggetto TextView.

Ad esempio, se hai un array di stringhe che vuoi visualizzare in un ListView, inizializza un nuovo ArrayAdapter utilizzando un costruttore per specificare il layout per ogni stringa e l'array di stringhe:

Kotlin

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    

Java

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, myStringArray);
    

Gli argomenti di questo costruttore sono i seguenti:

  • `Context` dell'app Context
  • Il layout che contiene un TextView per ogni stringa dell' array
  • L'array di stringhe

Quindi chiama setAdapter() sul tuo ListView:

Kotlin

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    

Java

    ListView listView = (ListView) findViewById(R.id.listview);
    listView.setAdapter(adapter);
    

Per personalizzare l'aspetto di ogni elemento, puoi sostituire il toString() metodo per gli oggetti dell'array. In alternativa, per creare una visualizzazione per ogni elemento diverso da un oggetto TextView, ad esempio se vuoi un oggetto ImageView per ogni elemento dell'array, estendi la classe ArrayAdapter e sostituisci getView() per restituire il tipo di visualizzazione che vuoi per ogni elemento.

SimpleCursorAdapter
Utilizza questo adattatore quando i dati provengono da un Cursor. Quando utilizzi SimpleCursorAdapter, specifica un layout da utilizzare per ogni riga dell'oggetto Cursor e le colonne dell'oggetto Cursor che vuoi inserire nelle visualizzazioni del layout che vuoi. Ad esempio, se vuoi creare un elenco di nomi e numeri di telefono delle persone, puoi eseguire una query che restituisce un Cursor contenente una riga per ogni persona e colonne per i nomi e i numeri. Poi crei un array di stringhe che specifica le colonne dell'oggetto Cursor che vuoi nel layout per ogni risultato e un array di numeri interi che specifica le visualizzazioni corrispondenti in cui inserire ogni colonna:

Kotlin

    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                              ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    

Java

    String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                            ContactsContract.CommonDataKinds.Phone.NUMBER};
    int[] toViews = {R.id.display_name, R.id.phone_number};
    

Quando crei un'istanza di SimpleCursorAdapter, passa il layout da utilizzare per ogni risultato, l'oggetto Cursor contenente i risultati e questi due array:

Kotlin

    val adapter = SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    

Java

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    ListView listView = getListView();
    listView.setAdapter(adapter);
    

L'SimpleCursorAdapter crea quindi una visualizzazione per ogni riga in l'Cursor utilizzando il layout fornito inserendo ogni elemento fromColumns nella visualizzazione toViews corrispondente.

Se durante il ciclo di vita dell'app modifichi i dati sottostanti letti dall'adattatore, chiama notifyDataSetChanged(). In questo modo, la visualizzazione collegata viene informata che i dati sono stati modificati e si aggiorna.

Gestire gli eventi di clic

Puoi rispondere agli eventi di clic su ogni elemento di un AdapterView implementando l' AdapterView.OnItemClickListener interfaccia. Ad esempio:

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click.
}

Java

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click.
    }
};

listView.setOnItemClickListener(messageClickedHandler);

Risorse aggiuntive

Scopri come vengono utilizzati i layout nell' app demo Sunflower su GitHub.