Quando devi visualizzare 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 disegnato. Le varie sottoclassi aiutano in scenari specifici
e puoi estenderle per definire i tuoi oggetti disegnabili
che si comportano in modo unico.
Oltre a utilizzare i costruttori di classi, esistono due modi per definire e creare un'istanza di Drawable:
- Gonfia una risorsa immagine (un file bitmap) salvata nel progetto.
- Eseguire l'inflate di una risorsa XML che definisce le proprietà di una risorsa drawable.
Nota: Potresti preferire utilizzare un elemento disegnabile vettoriale, che definisce un'immagine con un insieme di punti, linee e curve, insieme alle informazioni sul colore associate. In questo modo, le risorse grafiche vettoriali possono essere scalate per dimensioni diverse senza perdita di qualità. Per saperne di più, consulta la panoramica delle risorse grafiche vettoriali.
Crea risorse grafiche dalle immagini delle risorse
Puoi aggiungere elementi grafici alla tua app facendo riferimento a un file immagine dalle risorse del progetto. I tipi di file supportati sono PNG (preferito), JPG (accettabile) e GIF (sconsigliato). Le icone, i loghi e le altre immagini delle app, come quelle utilizzate nei giochi, sono adatti a 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 entrambi i casi, viene fatto riferimento utilizzando un ID risorsa, ovvero il nome 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/ potrebbero essere ottimizzate automaticamente con
la compressione delle immagini senza perdita di dati dallo strumento aapt durante la procedura di build. 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 di colori. Il risultato è un'immagine
di pari qualità, ma che richiede meno memoria. Di conseguenza, i binari delle immagini
inseriti in questa directory possono cambiare in tempo di compilazione. Se prevedi di leggere un'immagine come flusso di bit per convertirla in una bitmap, inserisci le immagini nella cartella res/raw/, dove lo strumento aapt non le modifica.
Il seguente snippet di codice mostra come creare un 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 univoca nel tuo progetto
può mantenere un solo stato, indipendentemente dal numero di oggetti diversi che
istanzi per essa. Ad esempio, se crei un'istanza di due oggetti Drawable dalla stessa risorsa immagine e
modifica una proprietà (ad esempio l'alpha) per un oggetto, la modifica influisce anche
sull'altro. Quando gestisci più istanze di una risorsa immagine, anziché
trasformare direttamente l'oggetto Drawable, devi eseguire un'
animazione
tween.
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 saperne di più sull'utilizzo delle risorse del progetto, consulta Risorse e asset.
Nota:quando utilizzi le risorse immagine come origine dei drawables, assicurati che le immagini abbiano le dimensioni appropriate per le varie densità di pixel. Se le immagini non sono corrette, verranno scalate per adattarsi, il che può causare artefatti nelle risorse disegnabili. Per ulteriori informazioni, leggi l'articolo Supportare diverse densità di pixel.
Crea risorse disegnabili da risorse XML
Se vuoi creare un oggetto Drawable che inizialmente non dipende da variabili definite dal tuo codice o dall'interazione dell'utente, definire Drawable in XML è una buona opzione. Anche
se prevedi che il tuo Drawable modifichi le sue proprietà durante l'interazione dell'utente
con la tua app, ti consigliamo di definire l'oggetto in XML, in quanto puoi modificare le proprietà dopo
l'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>
Poi, recupera e crea un'istanza dell'oggetto chiamando
Resources#getDrawable()
e passando l'ID risorsa del file XML. Qualsiasi
sottoclasse di Drawable
che supporta il metodo inflate() può essere definita in XML e istanziata
dalla tua app.
Ogni classe disegnabile che supporta l'inflazione XML utilizza attributi XML specifici
che aiutano a definire le proprietà dell'oggetto. Il seguente codice crea un'istanza di
TransitionDrawable
e la 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 sopra.
Risorse disegnabili di forme
Un oggetto ShapeDrawable può essere una buona opzione
quando vuoi 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 sia previsto un Drawable. Ad esempio, puoi utilizzare un oggetto ShapeDrawable per impostare lo sfondo di una visualizzazione passandolo al metodo setBackgroundDrawable() della visualizzazione. Puoi anche disegnare la 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
sopra come faresti con qualsiasi altra visualizzazione personalizzata. Ad esempio, puoi
aggiungerlo in modo programmatico a un'attività nella tua app, come mostrato nel seguente
esempio:
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 eseguire l'override del costruttore View(Context, AttributeSet), che viene chiamato quando la classe viene creata 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 risorse disegnabili nel pacchetto android.graphics.drawable, ti consente di definire varie proprietà dell'oggetto utilizzando metodi pubblici. Alcune proprietà di esempio che potresti voler regolare includono trasparenza alfa, filtro colore, dithering, opacità e colore.
Puoi anche definire forme primitive disegnabili utilizzando le risorse XML. Per ulteriori informazioni, vedi Shape drawable in Tipi di risorse drawable.
Risorse grafiche NinePatch
Un elemento grafico NinePatchDrawable è un'immagine bitmap estensibile che puoi utilizzare come sfondo di una visualizzazione. 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. Un'immagine
NinePatch è 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. Indica una sezione estensibile disegnando 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. Le dimensioni relative delle sezioni estensibili rimangono invariate, quindi la sezione più grande rimane sempre la più grande.
Puoi anche definire una sezione di risorsa drawable facoltativa dell'immagine (in pratica,
le linee di spaziatura interna) disegnando una linea a destra e una in basso. Se un oggetto View imposta la grafica NinePatch come sfondo e poi specifica il testo della visualizzazione, si estende in modo che tutto il testo occupi solo l'area designata dalle linee destra e inferiore (se incluse).
Se le linee di spaziatura interna non sono incluse, Android utilizza le linee a sinistra e in alto per definire questa risorsa drawable.
Per chiarire la differenza tra le linee, quelle a sinistra e in alto definiscono quali pixel dell'immagine possono essere replicati per allungarla. Le linee 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:
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 per allungarla. Il rettangolo rosa nell'immagine in basso identifica la regione in cui sono consentiti i contenuti della visualizzazione. Se i contenuti non rientrano in questa regione, l'immagine viene allungata per adattarli.
Lo strumento Disegna 9-patch offre un modo estremamente pratico per creare le immagini NinePatch, utilizzando un editor grafico WYSIWYG. Vengono visualizzati avvisi anche se la regione che hai definito per l'area estensibile rischia di produrre artefatti di disegno a causa della replica dei pixel.
Il seguente XML di layout di esempio mostra come aggiungere una grafica NinePatch
a un paio di pulsanti. L'immagine 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 mostrata sopra. Nota come la larghezza e l'altezza del pulsante variano in base al testo e come l'immagine di sfondo si allunga per adattarsi.
Figura 2: pulsanti visualizzati utilizzando una risorsa XML e una grafica NinePatch
Risorse disegnabili personalizzate
Quando vuoi creare alcuni 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 istruzioni di disegno.
Il seguente codice mostra una semplice sottoclasse 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; } }
Poi puoi aggiungere la risorsa disegnabile 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 istanze della risorsa disegnabile personalizzata con XML nei seguenti modi:
- Utilizzando 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" />
- Utilizzando
drawablecome nome del tag XML e specificando il nome completo della classe dall'attributo class. Questo approccio può essere utilizzato sia per le classi di primo livello pubbliche 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" />
Aggiungere la tinta alle risorse disegnabili
Con Android 5.0 (livello API 21) e versioni successive, puoi colorare bitmap e nine-patch definiti come
maschere alfa. Puoi colorarli con risorse di colore o attributi del tema che vengono risolti in risorse di colore (ad esempio ?android:attr/colorPrimary). Di solito, crei questi asset una sola volta e li colori automaticamente in modo che corrispondano al tuo tema.
Puoi applicare una tinta agli oggetti BitmapDrawable, NinePatchDrawable o VectorDrawable
con il metodo setTint(). Puoi anche impostare il colore e la modalità della tinta nei layout con gli attributi android:tint e android:tintMode.
Estrarre i colori principali da un'immagine
L'Android Support Library include la classe Palette, che consente di estrarre i colori principali da un'immagine.
Puoi caricare le risorse grafiche come Bitmap e passarlo a Palette per accedere ai relativi colori.
Per maggiori informazioni, leggi la pagina Selezione dei colori con l'API Palette.