Omówienie elementów rysunkowych

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak wyświetlać grafikę w funkcji Compose

Jeśli musisz wyświetlić w aplikacji obrazy statyczne, możesz użyć klasy Drawable i jej podklas do rysowania kształtów oraz obrazów. Drawable to ogólna abstrakcja dla: coś, co można narysować. Różne podklasy pomagają w przypadku konkretnego obrazu w scenariuszach. Możesz je rozszerzać, aby definiować własne obiekty rysowalne. które zachowują się w unikalny sposób.

Poza użyciem konstruktorów klas są jeszcze 2 sposoby definiowania i tworzenia instancji Drawable:

  • Powiększ zasób graficzny (plik bitmapy) zapisany w projekcie.
  • Dodaj komponent XML, który określa właściwości możliwe do rysowania.

Uwaga: Zamiast tego możesz użyć obiektu rysowania wektorowego, który definiuje obraz ze zbiorem punktów, linii i krzywych, a także powiązanych informacji o kolorach. Umożliwia to obiekty rysowane wektorowo możliwość skalowania w różnych rozmiarach bez utraty jakości. Więcej informacji znajdziesz w sekcji Wektor elementów rysowanych.

Tworzenie elementów rysunkowych na podstawie obrazów zasobów

Możesz dodać grafikę do swojej aplikacji, odwołując się do pliku graficznego zasobów projektu. Obsługiwane typy plików to PNG (preferowany), JPG (dopuszczalne) i GIF (odradzamy). Ikony, logo aplikacji i inne elementy graficzne, np. używane dobrze nadają się do tej techniki.

Aby użyć zasobu obrazu, dodaj plik do res/drawable/ w katalogu projektu. Gdy Twój projekt znajdzie się w projekcie, możesz odwoływać się do obrazu z kodu lub układu XML. Tak czy inaczej, jest to użycie identyfikator zasobu, który jest nazwą pliku bez rozszerzenia typu pliku. Dla: Określ my_image.png jako my_image.

Uwaga: zasoby graficzne umieszczone w pliku Katalog res/drawable/ może być automatycznie optymalizowany przy użyciu bezstratna kompresja obrazu wykonywana za pomocą narzędzia aapt podczas kompilacji proces tworzenia konta. Może to być na przykład plik PNG w prawdziwym kolorze, który nie wymaga więcej niż 256 kolorów. można przekonwertować na 8-bitowy plik PNG z paletą kolorów. W efekcie powstanie obraz takiej samej jakości, ale wymagający mniej pamięci. W efekcie pliki binarne obrazów umieszczone w tym katalogu mogą ulec zmianie podczas kompilacji. Jeśli planujesz przeczytać w postaci strumienia bitowego, aby przekonwertować go na format bitmapy, umieść obrazy w res/raw/, w którym narzędzie aapt nie je modyfikować.

Ten fragment kodu pokazuje, jak utworzyć obiekt ImageView, w którym obraz utworzony z zasobu rysowanego i dodany do układu:

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

W innych przypadkach zasób obrazu może być obsługiwany jako obiekt Drawable, jak w tym przykładzie: przykład:

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

Ostrzeżenie: każdy unikalny zasób w projekcie. mogą utrzymywać tylko jeden stan, niezależnie od tego, ile różnych obiektów utworzyć dla niego instancję. Jeśli na przykład utworzysz instancję 2 obiektów Drawable z tego samego zasobu obrazu i zmienisz właściwość (np. alfa) jednego obiektu, to wpłynie to również z drugiej strony. Gdy mamy do czynienia z wieloma instancjami zasobu graficznego, bezpośredniego przekształcenia obiektu Drawable, musisz wykonać starszy animację.

Poniższy fragment kodu XML pokazuje, jak dodać zasób rysowalny do elementu ImageView w układzie XML:

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

Więcej informacji o korzystaniu z zasobów projektu znajdziesz w artykule Zasoby.

Uwaga: jeśli jako źródła obiektów rysowanych używasz zasobów graficznych, Zadbaj o to, aby obrazy miały odpowiedni rozmiar przy różnej gęstości pikseli. Jeśli nieprawidłowe obrazy zostaną powiększone, by pasowały, co może spowodować pojawienie się artefaktów w obiektach rysowanych. Więcej informacji znajdziesz w artykule Obsługa różnych opcji gęstości pikseli.

Tworzenie elementów rysunkowych na podstawie zasobów XML

Jeśli występuje Drawable który chcesz utworzyć, który nie jest początkowo zależny od zmiennych zdefiniowanych przez lub interakcji użytkownika, dobrym rozwiązaniem będzie zdefiniowanie atrybutu Drawable w pliku XML. Równomierny jeśli spodziewasz się, że Drawable zmieni swoje właściwości podczas interakcji użytkownika z aplikacją, zastanów się nad zdefiniowaniem obiektu w pliku XML, ponieważ możesz modyfikować właściwości po Obiekt został utworzony.

Po zdefiniowaniu pliku Drawable w formacie XML zapisz go w pliku Katalog res/drawable/ Twojego projektu. Poniższy przykład pokazuje kod XML, który definiuje TransitionDrawable zasób, który dziedziczy z zasobu 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>

Następnie pobierz i utwórz instancję obiektu, wywołując Resources#getDrawable() i przekazując identyfikator zasobu Twojego pliku XML. Dowolne Drawable podklasa obsługujące metodę inflate(), można zdefiniować w pliku XML i utworzyć jej instancję przez Twoją aplikację.

Każda klasa rysowalna, która obsługuje inflację kodu XML, wykorzystuje określone atrybuty XML które pozwalają zdefiniować właściwości obiektu. Poniższy kod tworzy instancję TransitionDrawable i ustawia ją jako treść Obiekt 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.

Więcej informacji o obsługiwanych atrybutach XML znajdziesz w klasach wymienionych powyżej.

Elementy rysowane kształtami

Obiekt ShapeDrawable może być dobrym rozwiązaniem gdy chcesz dynamicznie rysować dwuwymiarową grafikę. Dostępne opcje automatyczne rysowanie kształtów podstawowych w obiekcie ShapeDrawable i stosuj style, których potrzebuje Twoja aplikacja.

ShapeDrawable jest podklasą klasy Drawable. Dlatego możesz użyć parametru ShapeDrawable wszędzie tam, gdzie spodziewana jest wartość Drawable. Dla: można na przykład ustawić tło za pomocą obiektu ShapeDrawable widoku danych, przekazując go do metody setBackgroundDrawable() widoku. Możesz też narysować kształt własny widok niestandardowy i dodać go do układu w aplikacji.

ShapeDrawable ma własną metodę draw(), więc możesz utworzyć podklasa View, która pobiera ShapeDrawable podczas zdarzenia onDraw(), jak widać w ten przykładowy kod:

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

Możesz użyć klasy CustomDrawableView w przykładowym kodzie jak każdy inny widok niestandardowy. Możesz na przykład: automatycznie dodać go do aktywności w aplikacji, jak w tym przykładzie przykład:

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

Jeśli zamiast tego chcesz użyć widoku niestandardowego z układem XML, użyj funkcji Klasa CustomDrawableView musi zastąpić konstruktor View(Context, AttributeSet), który jest wywoływany, gdy klasa jest została powiększona z pliku XML. Poniższy przykład pokazuje, jak zadeklarować CustomDrawableView w układzie XML:

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

Klasa ShapeDrawable, podobnie jak wiele innych typy rysowalne w pakiecie android.graphics.drawable pozwalają na zdefiniować różne właściwości obiektu za pomocą metod publicznych. Przykład właściwości, które warto dostosować, to przezroczystość alfa, filtr kolorów, przezroczystość i kolor.

Możesz też definiować podstawowe kształty rysowane za pomocą zasobów XML. Więcej Więcej informacji można znaleźć w sekcji Kształt, który można rysować w Typy zasobów rysowalnych.

Elementy rysowane NinePatch

Grafika NinePatchDrawable to rozciągnięty obraz bitmapy, którego można użyć jako tła widoku. Android, automatycznie zmienia rozmiar grafiki w celu dostosowania jej do zawartości widoku. An przykład użycia obrazu NinePatch to tło w standardowym Androidzie Przyciski – muszą się rozciągać, tak aby można było zmieścić ciągi znaków o różnej długości. O Grafika NinePatch to standardowy obraz PNG z dodatkowym ramką o szerokości 1 piksela. Musisz go zapisać za pomocą rozszerzenia 9.png w pliku Katalog res/drawable/ Twojego projektu.

Użyj obramowania, aby określić rozciągane i statyczne obszary obrazu. Oznaczysz sekcję rozciąganą, rysując co najmniej jeden obszar o szerokości 1 piksela. czarne linie w lewej i górnej części obramowania (pozostałe piksele obramowania powinien być całkowicie przezroczysty lub biały). Możesz mieć tyle rozciąganych sekcji według potrzeb. Względny rozmiar sekcji rozciąganych pozostaje taki sam, więc Największa sekcja zawsze pozostaje największa.

Możesz też zdefiniować opcjonalną rysowaną część obrazu (w ten sposób linie dopełnienia), rysując linię po prawej stronie i linię na dole. Jeśli Obiekt View ustawia grafikę NinePatch jako tło a następnie określa tekst widoku, rozciąga się tak, że cały tekst zajmuje tylko obszar wyznaczony przez prawa i dolne linie (jeśli są uwzględnione). Jeśli nie ma linii dopełnienia, Android używa linii lewej i górnej do określić ten obszar rysowalny.

Aby ułatwić określenie różnicy między liniami, linia lewa i górna definiują które piksele obrazu mogą być replikowane, aby rozciągać . Linie dolne i prawe określają względny obszar obrazu, który mogą zajmować zawartość widoku.

Ilustracja 1 pokazuje przykład grafiki NinePatch użytej do zdefiniowania przycisku:

Obraz obszaru rozciąganego
i pole dopełnienia

Rysunek 1. Przykład grafiki NinePatch definiujący przycisk,

Ta grafika NinePatch definiuje jeden rozciągany obszar, który obejmuje górną i lewą stronę oraz linie dolne i prawy obszar, który można rysować. Na obrazie u góry szare przerywane linie wskazują obszary obrazu, które są replikowane aby go rozciągnąć. Różowy prostokąt na obrazku u dołu wskazuje, region, w którym dozwolona jest zawartość widoku. Jeśli nie do tego obszaru, obraz zostanie rozciągnięty, aby je dopasować.

Narzędzie Draw 9-patch oferuje to bardzo przydatny sposób tworzenia obrazów NinePatch, za pomocą grafiki WYSIWYG. redaktorem. Ostrzeżenia są wyświetlane nawet wtedy, gdy region zdefiniowany dla elementu rozciąganego na obszar, gdzie piksel może powstawać artefaktów rysowania. replikacji danych.

Poniższy przykładowy kod XML układu pokazuje, jak dodać grafikę NinePatch na kilka przycisków. Obraz NinePatch jest zapisywany w 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"/>

Pamiętaj, że pola layout_width i layout_height atrybuty mają wartość wrap_content, by przycisk był dobrze dopasowany wokół tekstu.

Rysunek 2 przedstawia 2 przyciski renderowane z obrazów XML i NinePatch. przedstawione powyżej. Zwróć uwagę, że szerokość i wysokość przycisku zmienia się w zależności od tekstu. a obraz tła zostanie rozciągnięty, aby go dopasować.

Obraz malutki
przycisków standardowej wielkości

Rysunek 2. Przyciski renderowane za pomocą kodu XML i grafika NinePatch

Niestandardowe elementy rysowane

Aby utworzyć własne rysunki, możesz to zrobić, rozszerzając klasę Drawable (lub dowolną jej podklasę).

Najważniejszą metodą jest draw(Canvas) ponieważ zapewnia to obiekt Canvas, którego musisz użyć do podania instrukcje rysowania.

Ten kod pokazuje prostą podklasę klasy Drawable które tworzy okrąg:

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

Potem możesz dodać obiekt rysowany w dowolnym miejscu, np. ImageView jak tutaj:

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

W Androidzie 7.0 (poziom interfejsu API 24) i nowszych możesz też zdefiniować instancje niestandardowego elementu rysowanego z XML w następujący sposób:

  • Używanie pełnej i jednoznacznej nazwy klasy jako nazwy elementu XML. W tym podejściu Klasa obiektowa do rysowania musi być publiczną klasą najwyższego poziomu:
    <com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="#ffff0000" />
    
  • użycie drawable jako nazwy tagu XML i określenie w pełni kwalifikowanej klasy. nazwa z atrybutu class. Tę metodę można stosować zarówno w przypadku publicznych klas najwyższego poziomu, jak publiczne statyczne klasy wewnętrzne:
    <drawable xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.myapp.MyTopLevelClass$MyDrawable"
        android:color="#ffff0000" />
    
.

Dodaj odcień do elementów, które można rysować

W Androidzie 5.0 (poziom interfejsu API 21) lub nowszym można zabarwić mapy bitowe i 9 poprawek zdefiniowane jako przez maski alfa. Możesz zabarwić je za pomocą zasobów kolorów lub atrybutów motywu, które mają kolor zasobów (na przykład ?android:attr/colorPrimary). Zwykle tworzysz te komponenty tylko raz i automatycznie pokolorować je, by pasowały do motywu.

Możesz zastosować odcień w odcieniach BitmapDrawable, NinePatchDrawable i VectorDrawable za pomocą metody setTint(). Dostępne opcje możesz też ustawić kolor i tryb odcienia w układach za pomocą przycisków android:tint android:tintMode.

Wyodrębnij z obrazu najważniejsze kolory

Biblioteka pomocy Androida zawiera klasę Palette, która umożliwia wyodrębnianie z obrazu dobrze widocznych kolorów. Możesz załadować elementy rysowane jako obiekt Bitmap i przekazać je usłudze Palette, aby uzyskać dostęp do kolorów. Więcej informacji można znaleźć w sekcji Wybieranie kolorów za pomocą Palette API.