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 você precisa mostrar imagens estáticas no seu app, 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 diversas 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:você pode preferir 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

Você pode 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 do app, 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 o 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 de tipo de arquivo. Por exemplo, consulte 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 bitstream a fim de convertê-la em um bitmap, coloque suas imagens na pasta res/raw/, onde a ferramenta aapt não as modifique.

O snippet de código abaixo demonstra como criar um ImageView que usa uma imagem criada com 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, é recomendável processar o 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 no projeto pode manter apenas um estado, independentemente de quantos objetos diferentes você instancia para ele. Por exemplo, se você instanciar dois objetos Drawable do mesmo recurso de imagem e mudar uma propriedade (como a Alfa) para 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, verifique se as imagens têm o tamanho adequado para várias densidades de pixel. Se as imagens não estiverem corretas, elas vão ser dimensionadas para caber, o que pode gerar artefatos nos 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 o 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, defina o objeto em XML, já que você pode modificar as propriedades depois que o objeto for instanciado.

Após 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 que ofereça suporte ao método inflate() pode ser definida em XML e instanciada pelo 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. Você pode desenhar formas primitivas de forma programática em um objeto ShapeDrawable e aplicar os estilos de que o app precisa.

ShapeDrawable é uma subclasse de Drawable. Por isso, é possível usar uma 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() dela. 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 como usaria qualquer outra visualização personalizada. Por exemplo, você pode adicioná-lo 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 precisará 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, assim 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 mais informações, consulte Drawable de formato em Tipos de recursos drawables.

Drawables NinePatch

Um gráfico NinePatchDrawable é uma imagem de 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, de modo que a maior seção sempre permanece 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 plano de fundo e, em seguida, especificar o texto da visualização, ele será esticado para que todo o texto ocupe apenas a área designada pelas linhas direita e inferior (se incluídas). 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 de cima, as linhas cinza pontilhadas identificam as regiões 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 couber nessa região, a imagem será esticada para ajustá-lo.

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 eticável 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 da imagem XML e do NinePatch mostrados acima. Observe como a largura e a altura do botão variam de acordo com o texto, e a imagem de plano de fundo é esticada 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 é draw(Canvas), porque ele fornece o objeto Canvas que você precisa usar para fornecer suas 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ê pode adicionar o drawable onde quiser, por exemplo, em uma 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 de 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, é possível colorir os bitmaps e os Nine-Patches definidos como máscaras alfa. Você pode colori-los com recursos de cor ou atributos de tema que são resolvidos para recursos de cor (por exemplo, ?android:attr/colorPrimary). Normalmente, você cria esses recursos apenas uma vez e colore-os automaticamente para combinar com seu tema.

Você pode aplicar uma tonalidade a objetos BitmapDrawable, NinePatchDrawable ou VectorDrawable com o método setTint(). Você também pode 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.