Quando é necessário mostrar imagens estáticas no app, você pode usar a classe Drawable
e as subclasses dela para desenhar formas e
imagens. Um Drawable
é uma abstração geral para
algo que pode ser desenhado. As várias subclasses ajudam em cenários de imagem
específicos, e você pode estendê-las para definir seus próprios objetos drawable
que se comportam de maneiras únicas.
Há duas maneiras de definir e instanciar um Drawable
, além de usar os construtores da classe:
- Inflar um recurso de imagem (arquivo de bitmap) salvo no projeto.
- Inflar um recurso XML que define as propriedades do drawable.
Observação:talvez você prefira usar um drawable vetorial, que define uma imagem com um conjunto de pontos, linhas e curvas, além das informações de cor associadas. Isso permite que drawables vetoriais sejam dimensionados para diferentes tamanhos sem perda de qualidade. Para ver mais informações, consulte Visão geral de drawables vetoriais.
Criar drawables a partir de imagens de recursos
É possível adicionar gráficos ao app referenciando um arquivo de imagem dos recursos do projeto. Os tipos de arquivo compatíveis são PNG (preferencial), JPG (aceitável) e GIF (não recomendado). Ícones de apps, logotipos e outros elementos gráficos, como os usados em jogos, são adequados para essa técnica.
Para usar um recurso de imagem, adicione seu arquivo ao diretório res/drawable/
do projeto. No projeto, você pode referenciar o recurso
de imagem do seu código ou layout XML. De qualquer forma, é usado um
ID de recurso, que é o nome do arquivo sem a extensão do tipo. Por
exemplo, faça referência a my_image.png
como my_image
.
Observação:os recursos de imagem colocados no
diretório res/drawable/
podem ser otimizados automaticamente com
compactação de imagem sem perdas pela ferramenta aapt
durante o processo de
build. Por exemplo, um PNG de cores verdadeiras que não exige mais de 256 cores
pode ser convertido em um PNG de 8 bits com uma paleta de cores. Isso resulta em uma imagem
com a mesma qualidade, mas que requer menos memória. Como resultado, os binários de imagem
colocados nesse diretório podem mudar durante a compilação. Se você planeja ler uma
imagem como um bitstream para convertê-la em um bitmap, coloque as imagens na
pasta res/raw/
, onde a ferramenta aapt
não
as modificará.
O snippet de código abaixo demonstra como criar um ImageView
que usa
uma imagem criada de um recurso drawable e a adiciona ao 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); }
Em outros casos, convém processar seu recurso de imagem como um objeto Drawable
, conforme mostrado no exemplo
a seguir:
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);
Aviso:cada recurso exclusivo do projeto
pode manter apenas um estado, independentemente de quantos objetos diferentes
você instanciar para ele. Por exemplo, se você instanciar dois objetos Drawable
do mesmo recurso de imagem e
mudar uma propriedade (como a Alfa) de um objeto, isso também vai afetar
o outro. Ao lidar com várias instâncias de um recurso de imagem, em vez
de transformar diretamente o objeto Drawable
, execute uma
animação
de interpolação.
O snippet de XML abaixo mostra como adicionar um recurso drawable a um ImageView
no layout XML:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" android:contentDescription="@string/my_image_desc" />
Para mais informações sobre o uso de recursos do projeto, consulte Recursos.
Observação:ao usar recursos de imagem como origem dos drawables, confira se as imagens têm o tamanho adequado para várias densidades de pixel. Se as imagens não estiverem corretas, elas serão dimensionadas para caber, o que pode causar artefatos nos seus drawables. Para saber mais, leia Suporte a densidades de pixel diferentes.
Criar drawables a partir de recursos XML
Se você quiser criar um objeto Drawable
que não depende inicialmente de variáveis definidas pelo seu código ou interação do usuário, definir Drawable
em XML é uma boa opção. Mesmo
que você espere que Drawable
mude as propriedades durante a interação do usuário
com seu app, considere definir o objeto em XML, já que é possível modificar as propriedades depois
de ser instanciado.
Depois de definir o Drawable
em XML, salve o arquivo no diretório
res/drawable/
do projeto. O exemplo a seguir mostra o XML que
define um recurso
TransitionDrawable
, herdado de 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>
Em seguida, recupere e instancie o objeto chamando
Resources#getDrawable()
e transmitindo o ID do recurso do seu arquivo XML. Qualquer subclasse Drawable
compatível com o método inflate()
pode ser definida em XML e instanciada pelo seu app.
Cada classe de drawable compatível com a inflação de XML usa atributos XML específicos que ajudam a definir as propriedades do objeto. O código a seguir instancia o TransitionDrawable
e o define como o conteúdo de um objeto 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.
Para mais informações sobre os atributos XML compatíveis, consulte as classes listadas acima.
Drawables de formato
Um objeto ShapeDrawable
pode ser uma boa opção
quando você quer desenhar dinamicamente um gráfico bidimensional. É possível
desenhar formas primitivas de forma programática em um objeto ShapeDrawable
e aplicar os estilos necessários ao app.
ShapeDrawable
é uma subclasse de Drawable
. Por esse motivo, você pode usar um
ShapeDrawable
sempre que um Drawable
for esperado. Por
exemplo, você pode usar um objeto ShapeDrawable
para definir o plano de fundo
de uma visualização transmitindo-o para o método setBackgroundDrawable()
da visualização. Você também pode desenhar sua forma como a
própria visualização personalizada e adicioná-la a um layout no app.
Como ShapeDrawable
tem o próprio método draw()
, é possível criar uma
subclasse de View
que desenha o objeto ShapeDrawable
durante o evento onDraw()
, conforme mostrado no
exemplo de código a seguir.
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); } }
Você pode usar a classe CustomDrawableView
no exemplo de código
acima da mesma forma que usaria qualquer outra visualização personalizada. Por exemplo, você pode
adicioná-la de maneira programática a uma atividade no app, conforme mostrado no exemplo
abaixo:
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 você quiser usar a visualização personalizada no layout XML, a
classe CustomDrawableView
precisa substituir o construtor View(Context, AttributeSet)
, que é chamado quando a classe é
inflada do XML. O exemplo abaixo mostra como declarar o
CustomDrawableView
no layout XML:
<com.example.shapedrawable.CustomDrawableView android:layout_width="fill_parent" android:layout_height="wrap_content" />
A classe ShapeDrawable
, como muitos outros
tipos de drawables no pacote android.graphics.drawable
, permite que você
defina várias propriedades do objeto usando métodos públicos. Alguns exemplos
de propriedades que você pode ajustar incluem transparência Alfa, filtro de cor,
pontilhamento, opacidade e cor.
Também é possível definir formas básicas de drawable por meio de recursos XML. Para saber mais, consulte Drawable de formato em Tipos de recursos de drawables.
Drawables NinePatch
Um gráfico NinePatchDrawable
é uma
imagem bitmap esticável que pode ser usada como plano de fundo de uma visualização. O Android
redimensiona automaticamente o gráfico para acomodar o conteúdo da visualização. Um
exemplo de uso de uma imagem NinePatch é o plano de fundo usado pelos botões padrão
do Android. Os botões precisam ser esticados para acomodar strings de vários comprimentos. Um
gráfico NinePatch é uma imagem PNG padrão que inclui uma borda extra de 1 pixel.
Ele precisa ser salvo com a extensão 9.png
no
diretório res/drawable/
do projeto.
Use a borda para definir as áreas esticáveis e estáticas da imagem. Indique uma seção esticável desenhando uma ou mais linhas pretas de 1 pixel de largura nas partes esquerda e superior da borda. Os outros pixels de borda precisam ser totalmente transparentes ou brancos. É possível ter quantas seções esticáveis quiser. O tamanho relativo das seções esticáveis permanece o mesmo, então a seção maior permanece sempre a maior.
Também é possível definir uma seção drawable opcional da imagem (efetivamente,
as linhas de padding) desenhando uma linha à direita e outra na parte de baixo. Se um
objeto View
definir o gráfico NinePatch como o plano de fundo
e, em seguida, especificar o texto da visualização, ele se estenderá para que todo o texto
ocupe apenas a área designada pelas linhas direita e inferior (se incluída).
Se as linhas de padding não forem incluídas, o Android vai usar as linhas esquerda e superior para
definir essa área do drawable.
Para esclarecer a diferença entre as linhas, as linhas esquerda e superior definem quais pixels da imagem podem ser replicados para esticar a imagem. As linhas inferior e direita definem a área relativa dentro da imagem que o conteúdo da visualização pode ocupar.
A Figura 1 mostra um exemplo de gráfico NinePatch usado para definir um botão:
![Imagem da área esticável
e da caixa de padding](https://developer.android.com/static/images/ninepatch_raw.png?authuser=6&hl=pt)
Figura 1. Exemplo de um gráfico NinePatch que define um botão.
Esse gráfico NinePatch define uma área esticável com as linhas esquerda e superior e a área do drawable com as linhas inferior e direita. Na imagem superior, as linhas cinza pontilhadas identificam as regiões da imagem que são replicadas para esticar a imagem. O retângulo rosa na imagem inferior identifica a região em que o conteúdo da visualização é permitido. Se o conteúdo não se encaixar nessa região, a imagem será esticada para ajustá-los.
A ferramenta Draw 9-patch oferece uma maneira extremamente útil de criar imagens NinePatch usando um editor gráfico WYSIWYG. Ele gera até mesmo avisos se a região definida para a área elástica estiver em risco de produzir artefatos de desenho como resultado da replicação de pixels.
O exemplo de XML de layout abaixo demonstra como adicionar um gráfico NinePatch
a alguns botões. A imagem NinePatch é salva em
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"/>
Observe que os atributos layout_width
e layout_height
são definidos como wrap_content
para que o botão se encaixe perfeitamente
ao redor do texto.
A Figura 2 mostra os dois botões renderizados do XML e da imagem NinePatch mostrados acima. Observe como a largura e a altura do botão variam com o texto, e a imagem de plano de fundo se estica para acomodá-lo.
![Imagem de botões pequenos
e de tamanho normal](https://developer.android.com/static/images/ninepatch_examples.png?authuser=6&hl=pt)
Figura 2. Botões renderizados usando um recurso XML e um gráfico NinePatch.
Drawables personalizados
Quando você quiser criar alguns desenhos personalizados, estenda a classe Drawable
(ou qualquer uma das subclasses relacionadas).
O método mais importante a ser implementado é o draw(Canvas)
,
porque ele fornece o objeto Canvas
que você precisa usar para fornecer
as instruções de exibição.
O código abaixo mostra uma subclasse simples de Drawable
que desenha um círculo:
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; } }
Em seguida, você poderá adicionar o drawable onde quiser, por exemplo, a um
ImageView
, como mostrado aqui:
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));
No Android 7.0 (nível 24 da API) e versões mais recentes, também é possível definir instâncias do drawable personalizado com XML das seguintes maneiras:
- Use o nome de classe totalmente qualificado como o nome do elemento XML. Para essa abordagem, a classe
de drawable personalizado precisa ser pública de nível superior:
<com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ffff0000" />
- Usar
drawable
como o nome da tag XML e especificar o nome da classe totalmente qualificado no atributo da classe. Essa abordagem pode ser usada para classes públicas de nível superior e classes internas estáticas públicas:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
Adicionar tonalidade a drawables
Com o Android 5.0 (nível 21 da API) e versões mais recentes, você pode colorir bitmaps e 9-patches definidos como
máscaras alfa. Você pode colori-los com recursos de cor ou atributos de tema que determinam os recursos
de cor (por exemplo, ?android:attr/colorPrimary
). Normalmente, você cria esses recursos
apenas uma vez e colore-os automaticamente para combinar com o tema.
Você pode aplicar uma tonalidade a objetos BitmapDrawable
, NinePatchDrawable
ou VectorDrawable
com o método setTint()
. Também é possível
definir a cor e o modo da tonalidade nos seus layouts com os atributos android:tint
e
android:tintMode
.
Extrair cores em destaque de uma imagem
A Biblioteca de Suporte do Android inclui a classe Palette
, que permite extrair as cores em destaque de uma imagem.
Carregue seus drawables como um Bitmap
e transmita-o para Palette
para acessar as cores dele.
Para mais informações, leia Como selecionar cores com
a API Palette.