Quando devi mostrare immagini statiche nella tua app, puoi utilizzare la classe Drawable
e le relative sottoclassi per disegnare forme e immagini. Un Drawable
è un'astrazione generale per
qualcosa che può essere tracciato. Le varie sottoclassi sono utili per scenari di immagini specifici e puoi estenderle per definire i tuoi oggetti disegnabili che si comportano in modi unici.
Esistono due modi per definire e creare un'istanza per un Drawable
oltre all'utilizzo dei costruttori della classe:
- Aumenta il livello di una risorsa immagine (un file bitmap) salvata nel tuo progetto.
- Aumenta il livello di una risorsa XML che definisce le proprietà drawable.
Nota: in alternativa, potresti preferire l'utilizzo di un drawable vettoriale, che definisce un'immagine con un insieme di punti, linee e curve, insieme alle informazioni sui colori associate. In questo modo è possibile scalare i drawable vettoriali per dimensioni diverse senza compromettere la qualità. Per ulteriori informazioni, consulta la Panoramica dei disegnatori vettoriali.
Creazione di drawable da immagini risorse
Puoi aggiungere elementi grafici all'app facendo riferimento a un file immagine dalle risorse del progetto. I tipi di file supportati sono PNG (opzione preferita), JPG (accettabile) e GIF (non consigliato). Icone delle app, loghi e altri elementi grafici, come quelli utilizzati nei giochi, sono adatti per questa tecnica.
Per utilizzare una risorsa immagine, aggiungi il file alla directory res/drawable/
del progetto. Una volta nel progetto, puoi fare riferimento alla
risorsa immagine dal codice o dal layout XML. In ogni caso, si fa riferimento all'utilizzo
di un ID risorsa, che è il nome del file senza l'estensione del tipo di file. Ad
esempio, fai riferimento a my_image.png
come my_image
.
Nota: le risorse immagine inserite nella directory res/drawable/
possono essere ottimizzate automaticamente con la compressione delle immagini senza perdita di dati dallo strumento aapt
durante il processo di creazione. Ad esempio, un PNG a colori reali che non richiede più di 256 colori può essere convertito in un PNG a 8 bit con una tavolozza dei colori. Ciò si traduce in un'immagine di uguale qualità,
ma che richiede meno memoria. Di conseguenza, i file binari delle immagini
posizionati in questa directory possono cambiare in fase di creazione. Se prevedi di leggere un'immagine come flusso di bit per convertirla in una bitmap, inserisci invece le immagini nella cartella res/raw/
, dove lo strumento aapt
non le modifica.
Il seguente snippet di codice mostra come creare un elemento ImageView
che utilizza
un'immagine creata da una risorsa disegnabile e la aggiunge al layout:
Kotlin
private lateinit var constraintLayout: ConstraintLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Instantiate an ImageView and define its properties val i = ImageView(this).apply { setImageResource(R.drawable.my_image) contentDescription = resources.getString(R.string.my_image_desc) // set the ImageView bounds to match the Drawable's dimensions adjustViewBounds = true layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) } // Create a ConstraintLayout in which to add the ImageView constraintLayout = ConstraintLayout(this).apply { // Add the ImageView to the layout. addView(i) } // Set the layout as the content view. setContentView(constraintLayout) }
Java
ConstraintLayout constraintLayout; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a ConstraintLayout in which to add the ImageView constraintLayout = new ConstraintLayout(this); // Instantiate an ImageView and define its properties ImageView i = new ImageView(this); i.setImageResource(R.drawable.my_image); i.setContentDescription(getResources().getString(R.string.my_image_desc)); // set the ImageView bounds to match the Drawable's dimensions i.setAdjustViewBounds(true); i.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // Add the ImageView to the layout and set the layout as the content view. constraintLayout.addView(i); setContentView(constraintLayout); }
In altri casi, potresti voler gestire la risorsa immagine come oggetto Drawable
, come mostrato nell'esempio seguente:
Kotlin
val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)
Java
Resources res = context.getResources(); Drawable myImage = ResourcesCompat.getDrawable(res, R.drawable.my_image, null);
Avviso: ogni risorsa unica nel progetto
può mantenere un solo stato, indipendentemente dal numero di oggetti diversi
che vengono identificati. Ad esempio, se crei un'istanza di due oggetti Drawable
dalla stessa risorsa immagine e modifichi una proprietà (ad esempio, alpha) per un oggetto, verrà applicato anche l'altro. Quando si gestiscono più istanze di una risorsa immagine, invece
di trasformare direttamente l'oggetto Drawable
, devi eseguire
un'animazione
di interconnessione.
Lo snippet XML di seguito mostra come aggiungere una risorsa disegnabile a un ImageView
nel layout XML:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" android:contentDescription="@string/my_image_desc" />
Per ulteriori informazioni sull'utilizzo delle risorse di progetto, consulta Risorse e asset.
Nota: quando utilizzi risorse immagine come origine dei drawable, assicurati che le immagini siano delle dimensioni appropriate per le diverse densità di pixel. Se le immagini non sono corrette, verranno ridimensionate per adattarsi, il che può causare la presenza di artefatti nei disegni. Per ulteriori informazioni, consulta Supporto di diverse densità di pixel.
Creazione di drawable da risorse XML
Se vuoi creare un oggetto Drawable
, che inizialmente non dipende dalle variabili definite dal codice o dall'interazione dell'utente, definire il valore Drawable
in XML è una buona opzione. Anche se prevedi che Drawable
modifichi le sue proprietà durante l'interazione dell'utente con l'app, ti consigliamo di definire l'oggetto in XML, dato che puoi modificare le proprietà dopo aver creato un'istanza dell'oggetto.
Dopo aver definito Drawable
in XML, salva il file nella directory res/drawable/
del progetto. L'esempio seguente mostra il codice XML che
definisce una risorsa
TransitionDrawable
, che eredita da Drawable
:
<!-- res/drawable/expand_collapse.xml --> <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/image_expand"/> <item android:drawable="@drawable/image_collapse"/> </transition>
Quindi, recupera e crea un'istanza dell'oggetto chiamando
Resources#getDrawable()
e passando l'ID risorsa del tuo file XML. Qualsiasi sottoclasse Drawable
che supporta il metodo inflate()
può essere definita in XML e creata un'istanza dalla tua app.
Ogni classe drawable che supporta l'inflazione XML utilizza attributi XML specifici che aiutano a definire le proprietà dell'oggetto. Il codice seguente crea un'istanza di TransitionDrawable
e lo imposta come contenuto di un oggetto ImageView
:
Kotlin
val transition= ResourcesCompat.getDrawable( context.resources, R.drawable.expand_collapse, null ) as TransitionDrawable val image: ImageView = findViewById(R.id.toggle_image) image.setImageDrawable(transition) // Description of the initial state that the drawable represents. image.contentDescription = resources.getString(R.string.collapsed) // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000) // After the transition is complete, change the image's content description // to reflect the new state.
Java
Resources res = context.getResources(); TransitionDrawable transition = (TransitionDrawable) ResourcesCompat.getDrawable(res, R.drawable.expand_collapse, null); ImageView image = (ImageView) findViewById(R.id.toggle_image); image.setImageDrawable(transition); // Description of the initial state that the drawable represents. image.setContentDescription(getResources().getString(R.string.collapsed)); // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000); // After the transition is complete, change the image's content description // to reflect the new state.
Per ulteriori informazioni sugli attributi XML supportati, consulta le classi elencate in precedenza.
Disegna forme
Un oggetto ShapeDrawable
può essere una buona
opzione per disegnare dinamicamente un grafico bidimensionale. Puoi
disegnare in modo programmatico forme primitive su un oggetto ShapeDrawable
e applicare gli stili necessari alla tua app.
ShapeDrawable
è una sottoclasse di Drawable
. Per questo motivo, puoi utilizzare un
ShapeDrawable
ovunque è previsto un Drawable
. Ad esempio, puoi utilizzare un oggetto ShapeDrawable
per impostare lo sfondo di una vista passandolo al metodo setBackgroundDrawable()
della vista. Puoi anche tracciare una forma come
visualizzazione personalizzata e aggiungerla a un layout nell'app.
Poiché ShapeDrawable
ha il proprio metodo draw()
, puoi creare una
sottoclasse di View
che disegna l'oggetto ShapeDrawable
durante l'evento onDraw()
, come mostrato nell'esempio di codice seguente:
Kotlin
class CustomDrawableView(context: Context) : View(context) { private val drawable: ShapeDrawable = run { val x = 10 val y = 10 val width = 300 val height = 50 contentDescription = context.resources.getString(R.string.my_view_desc) ShapeDrawable(OvalShape()).apply { // If the color isn't set, the shape uses black as the default. paint.color = 0xff74AC23.toInt() // If the bounds aren't set, the shape can't be drawn. setBounds(x, y, x + width, y + height) } } override fun onDraw(canvas: Canvas) { drawable.draw(canvas) } }
Java
public class CustomDrawableView extends View { private ShapeDrawable drawable; public CustomDrawableView(Context context) { super(context); int x = 10; int y = 10; int width = 300; int height = 50; setContentDescription(context.getResources().getString( R.string.my_view_desc)); drawable = new ShapeDrawable(new OvalShape()); // If the color isn't set, the shape uses black as the default. drawable.getPaint().setColor(0xff74AC23); // If the bounds aren't set, the shape can't be drawn. drawable.setBounds(x, y, x + width, y + height); } protected void onDraw(Canvas canvas) { drawable.draw(canvas); } }
Puoi utilizzare la classe CustomDrawableView
nell'esempio di codice riportato sopra
come qualsiasi altra visualizzazione personalizzata. Ad esempio, puoi
aggiungerlo in modo programmatico a un'attività nella tua app, come illustrato nell'esempio seguente:
Kotlin
private lateinit var customDrawableView: CustomDrawableView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customDrawableView = CustomDrawableView(this) setContentView(customDrawableView) }
Java
CustomDrawableView customDrawableView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); customDrawableView = new CustomDrawableView(this); setContentView(customDrawableView); }
Se invece vuoi utilizzare la visualizzazione personalizzata nel layout XML, la classe CustomDrawableView
deve sostituire il costruttore View(Context, AttributeSet)
, che viene chiamato quando la classe viene gonfiata da XML. L'esempio seguente mostra come dichiarare CustomDrawableView
nel layout XML:
<com.example.shapedrawable.CustomDrawableView android:layout_width="fill_parent" android:layout_height="wrap_content" />
La classe ShapeDrawable
, come molti altri tipi di elementi estraibili nel pacchetto android.graphics.drawable
, consente di definire varie proprietà dell'oggetto utilizzando metodi pubblici. Alcuni esempi di proprietà che potresti voler regolare includono trasparenza alfa, filtro colore, dither, opacità e colore.
Puoi anche definire forme disegnabili primitive utilizzando le risorse XML. Per maggiori informazioni, consulta Drawable della forma in Tipi di risorse drawable.
Drawable NinePatch
Un'immagine NinePatchDrawable
è un'immagine bitmap estensibile che puoi utilizzare come sfondo di una vista. Android
ridimensiona automaticamente l'immagine per adattarla ai contenuti della visualizzazione. Un
esempio di utilizzo di un'immagine NinePatch è lo sfondo utilizzato dai pulsanti Android
standard, che devono allungarsi per adattarsi a stringhe di varie lunghezze. Una
grafica a nove patch è un'immagine PNG standard che include un bordo aggiuntivo di 1 pixel.
Deve essere salvato con l'estensione 9.png
nella directory res/drawable/
del progetto.
Utilizza il bordo per definire le aree estensibili e statiche dell'immagine. Per indicare una sezione estensibile, disegna una (o più) linee nere larghe 1 pixel nella parte sinistra e superiore del bordo (gli altri pixel del bordo devono essere completamente trasparenti o bianchi). Puoi avere tutte le sezioni estensibili che vuoi. La dimensione relativa delle sezioni estensibili rimane la stessa, quindi la sezione più grande rimane sempre quella più grande.
Puoi anche definire una sezione disegnabile facoltativa dell'immagine (effettivamente, le linee di spaziatura interna) disegnando una linea a destra e una linea nella parte inferiore. Se un oggetto
View
imposta l'immagine NinePatch come sfondo
e poi specifica il testo della visualizzazione, questo si estende in modo che tutto il testo
occupi solo l'area designata dalla riga a destra e dalla riga in basso (se inclusa).
Se le linee di spaziatura interna non sono incluse, Android utilizza le linee di sinistra e in alto per definire quest'area disegnabile.
Per chiarire la differenza tra le righe, le righe di sinistra e in alto definiscono quali pixel dell'immagine possono essere replicati per allungare l'immagine. Le righe in basso e a destra definiscono l'area relativa all'interno dell'immagine che i contenuti della visualizzazione possono occupare.
La Figura 1 mostra un esempio di grafica NinePatch utilizzata per definire un pulsante:
![Immagine di un'area estensibile
e di una casella di spaziatura interna](https://developer.android.com/static/images/ninepatch_raw.png?authuser=5&hl=it)
Figura 1: esempio di grafica NinePatch che definisce un pulsante
Questa grafica NinePatch definisce un'area estensibile con le linee sinistra e superiore e l'area disegnabile con le linee inferiore e destra. Nell'immagine in alto, le linee grigie tratteggiate identificano le regioni dell'immagine che vengono replicate al fine di allungare l'immagine. Il rettangolo rosa nell'immagine in basso identifica l'area in cui sono consentiti i contenuti della visualizzazione. Se i contenuti non si adattano a quest'area, l'immagine viene allungata per adattarli.
Lo strumento Draw 9-patch offre un modo estremamente pratico per creare immagini NinePatch utilizzando un editor grafico WYSIWYG. Genera anche avvisi se la regione definita per l'area estensibile rischia di produrre artefatti di disegno come risultato della replica dei pixel.
Il seguente codice XML di layout di esempio mostra come aggiungere una grafica NinePatch a un paio di pulsanti. L'immagine di NinePatch viene salvata in
res/drawable/my_button_background.9.png
.
<Button android:id="@+id/tiny" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerInParent="true" android:text="Tiny" android:textSize="8sp" android:background="@drawable/my_button_background"/> <Button android:id="@+id/big" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:text="Biiiiiiig text!" android:textSize="30sp" android:background="@drawable/my_button_background"/>
Tieni presente che gli attributi layout_width
e layout_height
sono impostati su wrap_content
per adattare perfettamente il pulsante al testo.
La Figura 2 mostra i due pulsanti visualizzati dall'immagine XML e NinePatch mostrati in alto. Nota come la larghezza e l'altezza del pulsante variano con il testo e l'immagine di sfondo si estende per adattarsi al testo.
![Immagine di pulsanti di dimensioni
normali e minuscoli](https://developer.android.com/static/images/ninepatch_examples.png?authuser=5&hl=it)
Figura 2: pulsanti visualizzati utilizzando una risorsa XML e un'immagine NinePatch
Drawable personalizzati
Se vuoi creare dei disegni personalizzati, puoi farlo estendendo la classe Drawable
(o una delle sue sottoclassi).
Il metodo più importante da implementare è draw(Canvas)
perché fornisce l'oggetto Canvas
che devi utilizzare per fornire le tue istruzioni di disegno.
Il seguente codice mostra una sottoclasse semplice di Drawable
che disegna un cerchio:
Kotlin
class MyDrawable : Drawable() { private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) } override fun draw(canvas: Canvas) { // Get the drawable's bounds val width: Int = bounds.width() val height: Int = bounds.height() val radius: Float = Math.min(width, height).toFloat() / 2f // Draw a red circle in the center canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint) } override fun setAlpha(alpha: Int) { // This method is required } override fun setColorFilter(colorFilter: ColorFilter?) { // This method is required } override fun getOpacity(): Int = // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE PixelFormat.OPAQUE }
Java
public class MyDrawable extends Drawable { private final Paint redPaint; public MyDrawable() { // Set up color and text size redPaint = new Paint(); redPaint.setARGB(255, 255, 0, 0); } @Override public void draw(Canvas canvas) { // Get the drawable's bounds int width = getBounds().width(); int height = getBounds().height(); float radius = Math.min(width, height) / 2; // Draw a red circle in the center canvas.drawCircle(width/2, height/2, radius, redPaint); } @Override public void setAlpha(int alpha) { // This method is required } @Override public void setColorFilter(ColorFilter colorFilter) { // This method is required } @Override public int getOpacity() { // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE return PixelFormat.OPAQUE; } }
Puoi quindi aggiungere il disegno dove vuoi, ad esempio a un ImageView
, come mostrato qui:
Kotlin
val myDrawing = MyDrawable() val image: ImageView = findViewById(R.id.imageView) image.setImageDrawable(myDrawing) image.contentDescription = resources.getString(R.string.my_image_desc)
Java
MyDrawable mydrawing = new MyDrawable(); ImageView image = findViewById(R.id.imageView); image.setImageDrawable(mydrawing); image.setContentDescription(getResources().getString(R.string.my_image_desc));
Su Android 7.0 (livello API 24) e versioni successive, puoi anche definire le istanze del drawable personalizzato con XML nei seguenti modi:
- Utilizzare il nome completo della classe come nome dell'elemento XML. Per questo approccio, la classe drawable personalizzata deve essere una classe pubblica di primo livello:
<com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ffff0000" />
- Utilizza
drawable
come nome del tag XML e specifica il nome completo della classe dall'attributo class. Questo approccio può essere utilizzato sia per le classi pubbliche di primo livello sia per le classi interne statiche pubbliche:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
Aggiungi tinta ai disegni
Con Android 5.0 (livello API 21) e versioni successive, puoi colorare le bitmap e le nove patch definite come maschere alfa. Puoi tingersi con le risorse colore o gli attributi del tema che si risolvono nelle risorse colore (ad esempio ?android:attr/colorPrimary
). In genere, puoi creare questi asset una sola volta e colorarli automaticamente in base al tema.
Puoi applicare una tinta agli oggetti BitmapDrawable
, NinePatchDrawable
o VectorDrawable
con il metodo setTint()
. Puoi
anche impostare il colore e la modalità di tinta nei tuoi layout con gli attributi android:tint
e
android:tintMode
.
Estrarre colori in evidenza da un'immagine
Android Support Library include la classe Palette
, che consente di estrarre colori in evidenza da un'immagine.
Puoi caricare i drawables come Bitmap
e passarli a Palette
per accedere ai relativi colori.
Per ulteriori informazioni, consulta Selezionare i colori
con l'API Palette.