Drawables – Übersicht

Jetpack Compose
Jetpack Compose ist das empfohlene UI-Toolkit für Android. Informationen zum Anzeigen von Grafiken in Compose

Wenn Sie statische Bilder in Ihrer App anzeigen möchten, können Sie die Klasse Drawable und ihre abgeleiteten Klassen verwenden, um Formen und Bilder zu zeichnen. Drawable ist eine allgemeine Abstraktion für etwas, das gezeichnet werden kann. Die verschiedenen abgeleiteten Klassen helfen bei bestimmten Bildszenarien. Sie können sie erweitern, um eigene Drawable-Objekte zu definieren, die sich auf einzigartige Weise verhalten.

Es gibt zwei Möglichkeiten, ein Drawable-Objekt zu definieren und zu instanziieren, abgesehen von der Verwendung der Klassenkonstruktoren:

  • Eine in Ihrem Projekt gespeicherte Bildressource (eine Bitmap-Datei) aufblähen.
  • Eine XML-Ressource aufblähen, die die Drawable-Attribute definiert.

Hinweis:Sie können stattdessen auch ein Vektor-Drawable verwenden, das ein Bild mit einer Reihe von Punkten, Linien und Kurven sowie zugehörigen Farbinformationen definiert. So lassen sich Vektor-Drawables ohne Qualitätsverlust auf verschiedene Größen skalieren. Weitere Informationen finden Sie unter Vektor Drawables-Übersicht.

Drawables aus Ressourcenbildern erstellen

Sie können Ihrer App Grafiken hinzufügen, indem Sie auf eine Bilddatei aus Ihren Projektressourcen verweisen. Unterstützte Dateitypen sind PNG (bevorzugt), JPG (akzeptabel) und GIF (nicht empfohlen). App-Symbole, Logos und andere Grafiken, z. B. in Spielen, eignen sich gut für diese Technik.

Wenn Sie eine Bildressource verwenden möchten, fügen Sie die Datei dem Verzeichnis res/drawable/ Ihres Projekts hinzu. Sobald sie sich in Ihrem Projekt befindet, können Sie im Code oder im XML-Layout auf die Bildressource verweisen. In beiden Fällen wird sie mit einer Ressourcen-ID bezeichnet, die dem Dateinamen ohne die Dateityperweiterung entspricht. Verweisen Sie beispielsweise auf my_image.png als my_image.

Hinweis:Bildressourcen, die im Verzeichnis res/drawable/ platziert sind, werden während des Build-Prozesses möglicherweise automatisch mit verlustfreier Bildkomprimierung durch das Tool aapt optimiert. Beispielsweise kann ein True-Color-PNG, das nicht mehr als 256 Farben benötigt, in ein 8-Bit-PNG mit einer Farbpalette konvertiert werden. Das Ergebnis ist ein Bild mit gleicher Qualität, das aber weniger Speicherplatz benötigt. Daher können sich die in diesem Verzeichnis platzierten Bildbinärdateien zur Build-Zeit ändern. Wenn Sie ein Bild als Bitstream lesen möchten, um es in eine Bitmap zu konvertieren, platzieren Sie die Bilder stattdessen im Ordner res/raw/, wo sie vom Tool aapt nicht geändert werden.

Das folgende Code-Snippet zeigt, wie Sie ein ImageView erstellen, das ein aus einer zeichenfähigen Ressource erstelltes Bild verwendet, und es dem Layout hinzufügen:

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 anderen Fällen möchten Sie Ihre Bildressource möglicherweise als Drawable-Objekt behandeln, wie im folgenden Beispiel gezeigt:

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);

Warnung:Jede eindeutige Ressource in Ihrem Projekt kann nur einen Zustand beibehalten, unabhängig davon, wie viele verschiedene Objekte Sie dafür instanziieren. Wenn Sie beispielsweise zwei Drawable-Objekte aus derselben Bildressource instanziieren und eine Eigenschaft (z. B. den Alphawert) für ein Objekt ändern, wirkt sich das auch auf das andere aus. Wenn Sie mit mehreren Instanzen einer Bildressource arbeiten, sollten Sie anstelle einer direkten Transformation des Drawable Objekts eine Tween Animation ausführen.

Das folgende XML-Snippet zeigt, wie Sie einem ImageView im XML-Layout eine zeichenfähige Ressource hinzufügen:

<ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/my_image"
        android:contentDescription="@string/my_image_desc" />

Weitere Informationen zur Verwendung von Projektressourcen finden Sie unter Ressourcen und Assets.

Hinweis:Wenn Sie Bildressourcen als Quelle für Ihre Drawables verwenden, müssen die Bilder die richtige Größe für verschiedene Pixeldichten haben. Wenn die Bilder nicht korrekt sind, werden sie so skaliert, dass sie passen. Das kann zu Artefakten in Ihren Drawables führen. Weitere Informationen finden Sie unter Verschiedene Pixeldichten unterstützen.

Drawables aus XML-Ressourcen erstellen

Wenn Sie ein Drawable Objekt erstellen möchten, das nicht von Variablen abhängt, die durch Ihren Code oder die Nutzerinteraktion definiert werden, ist es eine gute Option, das Drawable in XML zu definieren. Auch wenn Sie erwarten, dass sich die Eigenschaften Ihres Drawable während der Nutzerinteraktion mit Ihrer App ändern, sollten Sie das Objekt in XML definieren, da Sie die Eigenschaften ändern können, nachdem das Objekt instanziiert wurde.

Nachdem Sie Ihr Drawable in XML definiert haben, speichern Sie die Datei im res/drawable/ Verzeichnis Ihres Projekts. Das folgende Beispiel zeigt das XML, das eine TransitionDrawable Ressource definiert, die von Drawable abgeleitet wird:

<!-- 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>

Rufen Sie dann Resources#getDrawable() auf und übergeben Sie die Ressourcen-ID Ihrer XML-Datei, um das Objekt abzurufen und zu instanziieren. Jede Drawable Unterklasse die die inflate() Methode unterstützt kann in XML definiert und instanziiert von Ihrer App werden.

Jede Drawable-Klasse, die XML-Inflation unterstützt, verwendet bestimmte XML-Attribute mit denen die Objekteigenschaften definiert werden. Der folgende Code instanziiert das TransitionDrawable und legt es als Inhalt eines ImageView Objekts fest:

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.

Weitere Informationen zu den unterstützten XML-Attributen finden Sie in den oben aufgeführten Klassen.

Form-Drawables

Ein ShapeDrawable-Objekt kann eine gute Option sein, wenn Sie dynamisch eine zweidimensionale Grafik zeichnen möchten. Sie können programmatisch primitive Formen auf ein ShapeDrawable-Objekt zeichnen und die Stile anwenden, die Ihre App benötigt.

ShapeDrawable ist eine Unterklasse von Drawable. Aus diesem Grund können Sie ein ShapeDrawable überall verwenden, wo ein Drawable erwartet wird. Sie können beispielsweise ein ShapeDrawable-Objekt verwenden, um den Hintergrund einer Ansicht festzulegen, indem Sie es an die Methode setBackgroundDrawable() der Ansicht übergeben. Sie können Ihre Form auch als eigene benutzerdefinierte Ansicht zeichnen und sie einem Layout in Ihrer App hinzufügen.

Da ShapeDrawable eine eigene draw()-Methode hat, können Sie eine abgeleitete Klasse von View erstellen, die das ShapeDrawable Objekt während des onDraw()-Ereignisses zeichnet, wie im folgenden Codebeispiel gezeigt:

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);
  }
}

Sie können die Klasse CustomDrawableView im obigen Codebeispiel wie jede andere benutzerdefinierte Ansicht verwenden. Sie können sie beispielsweise programmatisch einer Aktivität in Ihrer App hinzufügen, wie im folgenden Beispiel gezeigt:

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);
}

Wenn Sie die benutzerdefinierte Ansicht stattdessen im XML-Layout verwenden möchten, muss die Klasse CustomDrawableView den Konstruktor View(Context, AttributeSet) überschreiben, der aufgerufen wird, wenn die Klasse aus XML aufgebläht wird. Das folgende Beispiel zeigt, wie Sie CustomDrawableView im XML-Layout deklarieren:

<com.example.shapedrawable.CustomDrawableView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />

Mit der Klasse ShapeDrawable können Sie wie bei vielen anderen Drawable-Typen im Paket android.graphics.drawable verschiedene Eigenschaften des Objekts mithilfe öffentlicher Methoden definieren. Beispiele für Eigenschaften, die Sie möglicherweise anpassen möchten, sind Alphatransparenz, Farbfilter, Dithering, Deckkraft und Farbe.

Sie können auch primitive Drawable-Formen mithilfe von XML-Ressourcen definieren. Weitere Informationen finden Sie unter Form-Drawable in Drawable-Ressourcentypen.

NinePatch-Drawables

Eine NinePatchDrawable-Grafik ist ein dehnbares Bitmap-Bild, das Sie als Hintergrund einer Ansicht verwenden können. Android ändert die Größe der Grafik automatisch an den Inhalt der Ansicht an. Ein Beispiel für die Verwendung eines NinePatch-Bilds ist der Hintergrund, der von Standard-Android-Buttons verwendet wird. Buttons müssen sich dehnen, um Strings unterschiedlicher Länge aufzunehmen. Eine NinePatch-Grafik ist ein Standard-PNG-Bild mit einem zusätzlichen 1-Pixel-Rahmen. Sie muss mit der Erweiterung 9.png im Verzeichnis res/drawable/ Ihres Projekts gespeichert werden.

Verwenden Sie den Rahmen, um die dehnbaren und statischen Bereiche des Bilds zu definieren. Sie geben einen dehnbaren Bereich an, indem Sie eine oder mehrere 1 Pixel breite schwarze Linien im linken und oberen Teil des Rahmens zeichnen. Die anderen Rahmenpixel sollten vollständig transparent oder weiß sein. Sie können beliebig viele dehnbare Bereiche haben. Die relative Größe der dehnbaren Bereiche bleibt gleich, sodass der größte Bereich immer der größte bleibt.

Sie können auch einen optionalen Drawable-Bereich des Bilds definieren (im Grunde die Padding-Linien), indem Sie eine Linie rechts und eine Linie unten zeichnen. Wenn ein View-Objekt die NinePatch-Grafik als Hintergrund festlegt und dann den Text der Ansicht angibt, wird es so gedehnt, dass der gesamte Text nur den Bereich einnimmt, der durch die rechte und untere Linie (falls vorhanden) festgelegt wird. Wenn die Padding-Linien nicht enthalten sind, verwendet Android die linke und obere Linie, um diesen Drawable-Bereich zu definieren.

Um den Unterschied zwischen den Linien zu verdeutlichen: Die linke und obere Linie definieren, welche Pixel des Bilds repliziert werden dürfen, um das Bild zu dehnen. Die untere und rechte Linie definieren den relativen Bereich innerhalb des Bilds, den der Inhalt der Ansicht einnehmen darf.

Abbildung 1 zeigt ein Beispiel für eine NinePatch-Grafik, die zum Definieren eines Buttons verwendet wird:

Bild des dehnbaren Bereichs und des Padding-Felds

Abbildung 1:Beispiel für eine NinePatch-Grafik, die einen Button definiert

Diese NinePatch-Grafik definiert einen dehnbaren Bereich mit der linken und oberen Linie und den Drawable-Bereich mit der unteren und rechten Linie. Im oberen Bild kennzeichnen die gepunkteten grauen Linien die Bereiche des Bilds, die repliziert werden, um das Bild zu dehnen. Das rosa Rechteck im unteren Bild kennzeichnet den Bereich, in dem der Inhalt der Ansicht zulässig ist. Wenn der Inhalt nicht in diesen Bereich passt, wird das Bild so gedehnt, dass er passt.

Mit dem Tool Draw 9-patch können Sie NinePatch-Bilder ganz einfach mit einem WYSIWYG-Grafikeditor erstellen. Es gibt sogar Warnungen aus, wenn in dem von Ihnen definierten Bereich für den dehnbaren Bereich aufgrund der Pixelreplikation Zeichenartefakte entstehen können.

Das folgende Beispiel-Layout-XML zeigt, wie Sie ein paar Buttons eine NinePatch-Grafik hinzufügen. Das NinePatch-Bild wird unter res/drawable/my_button_background.9.png gespeichert.

<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"/>

Die Attribute layout_width und layout_height sind auf wrap_content gesetzt, damit der Button genau um den Text passt.

Abbildung 2 zeigt die beiden Buttons, die aus dem oben gezeigten XML- und NinePatch-Bild gerendert wurden. Die Breite und Höhe des Buttons variieren mit dem Text und das Hintergrundbild wird entsprechend gedehnt.

Bild von kleinen und normal großen Schaltflächen

Abbildung 2:Buttons, die mit einer XML-Ressource und einer NinePatch-Grafik gerendert wurden

Benutzerdefinierte Drawables

Wenn Sie benutzerdefinierte Zeichnungen erstellen möchten, können Sie die Klasse Drawable (oder eine ihrer Unterklassen) erweitern.

Die wichtigste Methode, die Sie implementieren müssen, ist draw(Canvas) da sie das Canvas Objekt bereitstellt, das Sie verwenden müssen, um Ihre Zeichenanweisungen zu geben.

Der folgende Code zeigt eine einfache abgeleitete Klasse von Drawable, die einen Kreis zeichnet:

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;
    }
}

Dann können Sie Ihr Drawable an einer beliebigen Stelle hinzufügen, z. B. zu einem ImageView, wie hier gezeigt:

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));

Unter Android 7.0 (API-Level 24) und höher können Sie Instanzen Ihres benutzerdefinierten Drawables auch mit XML auf folgende Weise definieren:

  • Verwenden Sie den vollständig qualifizierten Klassennamen als XML-Elementnamen. Bei diesem Ansatz muss die benutzerdefinierte Drawable-Klasse eine öffentliche Klasse der obersten Ebene sein:
    <com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="#ffff0000" />
  • Verwenden Sie drawable als XML-Tag-Namen und geben Sie den vollständig qualifizierten Klassennamen aus dem Klassenattribut an. Dieser Ansatz kann sowohl für öffentliche Klassen der obersten Ebene als auch für öffentliche statische innere Klassen verwendet werden:
    <drawable xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.myapp.MyTopLevelClass$MyDrawable"
        android:color="#ffff0000" />

Drawables mit Farbe versehen

Unter Android 5.0 (API-Level 21) und höher können Sie Bitmaps und NinePatch-Bilder, die als Alphamasken definiert sind, mit Farbe versehen. Sie können sie mit Farbressourcen oder Designattributen versehen, die in Farbressourcen aufgelöst werden (z. B. ?android:attr/colorPrimary). Normalerweise erstellen Sie diese Assets nur einmal und färben sie automatisch passend zu Ihrem Design ein.

Sie können mit der Methode setTint() eine Farbe auf BitmapDrawable-, NinePatchDrawable- oder VectorDrawable-Objekte anwenden. Mit den Attributen android:tint und android:tintMode können Sie die Farbe und den Modus auch in Ihren Layouts festlegen.

Hervorstechende Farben aus einem Bild extrahieren

Die Android Support Library enthält die Klasse Palette, mit der Sie hervorstechende Farben aus einem Bild extrahieren können. Sie können Ihre Drawables als Bitmap laden und an Palette übergeben, um auf die Farben zuzugreifen. Weitere Informationen finden Sie unter Farben mit der Palette API auswählen.