Visão geral dos drawables

Teste o Compose
O Jetpack Compose é o kit de ferramentas de interface recomendado para Android. Aprenda a mostrar gráficos no Compose.

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

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

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.