Drawables & Vector (Java)

A Drawable is Android's general-purpose abstraction for "something that can be drawn." That covers a lot of ground: a bitmap photo, a solid-color rectangle, a rounded chip background, an icon that changes when you tap it, or several drawables stacked on top of each other. Any View that accepts android:background or android:src is really just asking for a Drawable — it doesn't care whether that Drawable came from a PNG file or was described entirely in XML.

In RecipeBox we lean on XML-defined drawables constantly: the little heart icon that marks a favorite recipe, the rounded "category tag" chip behind each recipe's cuisine label, and the buttons throughout the app that need to look pressed when tapped. None of these need a single PNG file.

1. Vector Drawables

A Vector Drawable (res/drawable/*.xml, using the <vector> tag) describes an image as a set of mathematical paths instead of a fixed grid of pixels. Because paths scale cleanly to any size, one vector file looks crisp at every screen density — there is no such thing as a "blurry" vector.

Here is a simple favorite-heart icon defined as a vector, RecipeBox's actual favorite marker:

<!-- res/drawable/ic_favorite_heart.xml -->
<vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">

    <path
        android:fillColor="#E53935"
        android:pathData="M12,21s-7.5,-4.6 -10,-9.3C0.4,8.3 2,5 5.3,5 c1.8,0 3.4,1 4.7,2.6C11.3,6 12.9,5 14.7,5 C18,5 19.6,8.3 22,11.7 C19.5,16.4 12,21 12,21z" />
</vector>
Explanation:
  • viewportWidth/viewportHeight define an internal coordinate space; width/height define the drawable's default rendered size.
  • The single <path> element's pathData traces the heart outline using standard SVG-style path commands.
  • Used as android:src="@drawable/ic_favorite_heart" on an ImageButton, this icon renders sharply whether the device is a small phone or a large tablet.

Why vectors win for icons: before vector support, shipping one icon meant exporting a separate PNG for every density bucket — drawable-mdpi, drawable-hdpi, drawable-xhdpi, and so on (density buckets are covered in depth in the next lesson, Dimensions & Density). A vector file replaces all of those with one XML file that Android rasterizes on demand at whatever size is needed. That means a smaller APK, one file to maintain instead of five, and guaranteed sharpness at any density.

2. Shape Drawables

A Shape Drawable (<shape>) draws a simple geometric background — a rectangle, oval, line, or ring — described entirely in XML, with no image file involved at all. It is the standard way to give a View rounded corners, a solid fill, or a border without touching an image editor.

Here is RecipeBox's category-tag chip background — a small rounded-rectangle pill behind labels like "Dessert" or "Breakfast":

<!-- res/drawable/bg_category_chip.xml -->
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#FFE0B2" />

    <corners android:radius="16dp" />

    <padding
        android:left="12dp"
        android:top="4dp"
        android:right="12dp"
        android:bottom="4dp" />
</shape>
<!-- Applied to the category TextView -->
<TextView
    android:id="@+id/tvCategory"
    android:background="@drawable/bg_category_chip"
    android:text="Dessert"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
Explanation:
  • <solid> sets the fill color; <corners> rounds every corner by the given radius.
  • The <padding> block built into the shape means any TextView using this background automatically gets breathing room — no need to repeat padding on every chip.
  • No PNG, no density folders, no image asset to manage — the whole chip look lives in one small XML file.

3. Selector / State List Drawables

A Selector (also called a State List Drawable) lets a single drawable reference switch its appearance based on the View's current state — pressed, focused, enabled, or the default. It is defined with a <selector> root containing multiple <item> entries, each guarded by a state attribute, and Android picks the first matching item.

Here is a button background that visibly darkens while pressed, used on RecipeBox's "Save Recipe" button:

<!-- res/drawable/button_background.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Pressed state: darker shade -->
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#1B5E20" />
            <corners android:radius="8dp" />
        </shape>
    </item>

    <!-- Disabled state: greyed out -->
    <item android:state_enabled="false">
        <shape android:shape="rectangle">
            <solid android:color="#BDBDBD" />
            <corners android:radius="8dp" />
        </shape>
    </item>

    <!-- Default state -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#2E7D32" />
            <corners android:radius="8dp" />
        </shape>
    </item>
</selector>
<!-- Save Recipe button, in the layout -->
<Button
    android:id="@+id/btnSaveRecipe"
    android:background="@drawable/button_background"
    android:text="Save Recipe"
    android:textColor="#FFFFFF"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
Explanation:
  • Android evaluates <item> entries top to bottom and uses the first one whose state condition matches — so the most specific states (pressed, disabled) must come before the plain default item.
  • The default item (no state attribute) always matches, so it must be listed last to act as a fallback.
  • No Java code is needed to swap the look — the system handles the state change automatically as the user's finger goes down and up.

4. Layer List Drawables (Conceptual Awareness)

A Layer List (<layer-list>) stacks several drawables on top of each other in a single drawable resource, painted bottom to front in the order they're listed. It is how you might build something like a bordered badge (a solid circle shape layer plus a smaller icon layer centered on top) without writing any custom drawing code. RecipeBox doesn't need one today, but it's worth recognizing the tag when you see it:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/bg_category_chip" />
    <item
        android:drawable="@drawable/ic_favorite_heart"
        android:width="14dp"
        android:height="14dp"
        android:gravity="center" />
</layer-list>
Explanation: each <item> in a layer list is its own drawable reference, optionally sized and positioned with width/height/gravity — later items paint over earlier ones.

Drawable Types at a Glance

Drawable typeDefined withBest for
Vector Drawable<vector>Icons that must stay sharp at every density
Shape Drawable<shape>Solid backgrounds, rounded corners, borders — no image file
Selector (State List)<selector>Pressed/disabled/focused visual feedback
Layer List<layer-list>Stacking multiple drawables into one composite image
Bitmap DrawablePNG/JPG/WebP filePhotos and complex imagery that can't be described as shapes

Troubleshooting

Vector icon looks tiny or huge? → Check that viewportWidth/viewportHeight match the coordinate space your pathData was authored in.

Selector not switching states? → Confirm the default (no-state) <item> is listed last, and that the View actually receives touch (it isn't covered by another View).

Shape corners look wrong on some Views? → Some widgets clip content to their own bounds; wrap the shape as a background on a plain View/FrameLayout if a child is overflowing the rounded corners.

Drawable (abstraction: "anything that can be drawn") ├─ Vector Drawable → scalable XML icon (one file, every density) ├─ Shape Drawable → solid/rounded background, no image file ├─ Selector → swaps drawable per View state (pressed/disabled) ├─ Layer List → stacks multiple drawables into one └─ Bitmap Drawable → PNG/JPG/WebP for photos
Drawables give a View its look; the next lesson, Dimensions & Density, covers how to size everything — including the bitmap alternative to vectors — consistently across every device.