Matrix — to klasa z Android SDK (android.graphics.Matrix), reprezentująca przekształcenie afiniczne poprzez macierz 3×3. Jest używana do transformacji obiektów graficznych — przesuwania, skalowania, obracania i pochylania. Według Android Developers Documentation (2026), Matrix jest podstawowym narzędziem do pracy z transformacjami w Canvas, Bitmap i Drawable, zapewniając precyzyjne zarządzanie współrzędnymi przy niestandardowym rysowaniu.
Najważniejsze
Matrix (android.graphics.Matrix) — to klasa Android SDK, reprezentująca macierz matematyczną 3×3, używaną do transformacji dwuwymiarowych współrzędnych. Matrix jest podstawowym elementem konstrukcyjnym dla wszystkich przekształceń graficznych w Androidzie — od przesuwania i skalowania Canvas po złożone perspektywiczne zniekształcenia Bitmap.
W przeciwieństwie do iOS, gdzie używane są oddzielne struktury CGAffineTransform (afiniczne) i CATransform3D (3D), Android łączy przekształcenia afiniczne i perspektywiczne w jednej klasie Matrix. Macierz 3×3 pozwala reprezentować zarówno standardowe operacje afiniczne (ostatni wiersz [0, 0, 1]), jak i perspektywiczne zniekształcenia, gdy ostatni wiersz zawiera dowolne wartości. Daje to więcej elastyczności, ale wymaga zrozumienia matematyki macierzy.
Matrix jest ściśle zintegrowany z Canvas API. Gdy programista wywołuje canvas.translate(), canvas.scale() lub canvas.rotate(), te metody wewnętrznie modyfikują macierz kontekstu graficznego. Bieżącą macierz Canvas można uzyskać przez canvas.getMatrix(), a ręcznie zastosować niestandardową — przez canvas.setMatrix() lub canvas.concat().
Matrix obsługuje cztery podstawowe typy przekształceń afinicznych: translate, scale, rotate, skew (pochylenie). Każdy typ ma metody dla pre-mnożenia (pre), post-mnożenia (post) i ustawiania (set). Różnica między preTranslate a postTranslate polega na kolejności mnożenia macierzy, co wpływa na końcowe przekształcenie przy łączeniu.
| Operacja | Metoda | Parametry | Opis |
|---|---|---|---|
| Translate | setTranslate / preTranslate / postTranslate | dx, dy | Przesunięcie wzdłuż osi X i Y |
| Scale | setScale / preScale / postScale | sx, sy, px, py | Skalowanie z punktem odniesienia |
| Rotate | setRotate / preRotate / postRotate | degrees, px, py | Obrót wokół punktu odniesienia |
| Skew | setSkew / preSkew / postSkew | kx, ky | Pochylenie (przesunięcie) wzdłuż osi |
Translate zmienia położenie wszystkich punktów o zadane przesunięcie dx, dy. Dodatnie dx przesuwa w prawo, dy — w dół (w układzie współrzędnych ekranu Androida oś Y skierowana jest w dół). Translate nie zmienia kształtu obiektu — tylko jego pozycję. To najprostsza i najczęściej używana operacja do pozycjonowania elementów.
Scale zmienia rozmiar obiektu ze współczynnikami sx (wzdłuż X) i sy (wzdłuż Y). Parametry px, py określają punkt odniesienia skalowania (pivot point). Jeśli px, py nie są podane, skalowanie wykonywane jest względem początku układu współrzędnych (0, 0). Przy sx = sy obiekt skalowany jest równomiernie. Ujemne wartości tworzą odbicie.
Rotate obraca obiekt o zadany kąt (w stopniach) wokół punktu odniesienia (px, py). Dodatnia wartość stopni odpowiada obrotowi zgodnie z ruchem wskazówek zegara (w przeciwieństwie do iOS/macOS, gdzie dodatni kąt oznacza obrót przeciwny do ruchu wskazówek zegara). Jeśli px, py nie są podane, obrót wykonywany jest wokół początku układu współrzędnych.
Skew (przesunięcie) deformuje obiekt, pochylając go wzdłuż osi X (kx) lub osi Y (ky). Wartość 1.0 pochyla o 45 stopni. Skew jest używany do tworzenia efektów pochylenia, imitacji trójwymiarowego widoku (pseudo-3D) i zniekształceń perspektywy.
Matrix przechowuje elementy macierzy 3×3 w wewnętrznej tablicy 9 wartości zmiennoprzecinkowych (float[9]). Kolejność przechowywania — column-major (wg kolumn), co odpowiada standardowemu matematycznemu przedstawieniu. Dostęp do poszczególnych elementów odbywa się poprzez stałe: Matrix.MSCALE_X, Matrix.MSKEW_Y, Matrix.MTRANS_X i tak dalej.
Macierz jest zorganizowana następująco: pierwsze dwie kolumny (MSCALE_X, MSKEW_X, MSKEW_Y, MSCALE_Y) odpowiadają za skalowanie, obrót i pochylenie. Trzecia kolumna (MTRANS_X, MTRANS_Y, MPERSP_2) — za przesunięcie i perspektywę. Dla przekształcenia afinicznego MPERSP_0 i MPERSP_1 są równe 0, a MPERSP_2 jest równy 1.
Do pobierania wartości macierzy używane jest getValues(float[] values), które wypełnia tablicę 9 elementów. Do ustawiania — setValues(float[] values). Pozwala to manipulować macierzą bezpośrednio dla złożonych niestandardowych przekształceń wykraczających poza standardowe metody translate, scale, rotate i skew.
Matrix udostępnia kilka metod do klasyfikacji bieżącego stanu. isIdentity() sprawdza, czy macierz jest jednostkowa. isAffine() — czy zawiera tylko przekształcenia afiniczne (MPERSP_0 = 0, MPERSP_1 = 0, MPERSP_2 = 1). Te sprawdzenia są przydatne do optymalizacji: jeśli macierz jest jednostkowa, transformację można pominąć.
Matrix obsługuje kilka kategorii operacji: ustawianie (set), pre-mnożenie (pre), post-mnożenie (post), konkatenacja (concat), inwersja (invert) i mapowanie (map). Każda kategoria rozwiązuje swoje zadanie przy budowaniu złożonych przekształceń.
preTranslate mnoży bieżącą macierz z lewej strony przez macierz przesunięcia: result = translate × current. postTranslate mnoży z prawej strony: result = current × translate. Różnica polega na kolejności stosowania: pre-operacje są stosowane przed bieżącymi transformacjami, post-operacje — po. Analogicznie działają preScale, postScale, preRotate, postRotate oraz preSkew, postSkew.
invert(Matrix inverse) oblicza macierz odwrotną. Jeśli macierz źródłowa jest zdegenerowana (wyznacznik równy zero), zwracana jest wartość false. concat(Matrix other) mnoży bieżącą macierz przez przekazaną: set = set × other. Dla odwrotnej kolejności użyj setConcat(other, this).
mapRect(RectF rect) przekształca prostokąt i zwraca wynik w nowym RectF. mapPoints(float[] dst, float[] src) przekształca tablicę punktów. mapVectors(float[] dst, float[] src) przekształca wektory — bez uwzględniania przesunięcia (translate). mapRect jest przydatny do obliczania bounding box po transformacji.
Matrix jest aktywnie używana w dwóch kluczowych scenariuszach grafiki Androida: transformacja Canvas i transformacja Bitmap. W obu przypadkach Matrix określa, jak dokładnie zostanie zmieniony wynik graficzny.
Canvas udostępnia metody canvas.setMatrix(matrix) do całkowitej wymiany bieżącej macierzy i canvas.concat(matrix) do mnożenia bieżącego przekształcenia przez przekazane. Po zastosowaniu Matrix całe kolejne rysowanie — wywołania drawBitmap, drawRect, drawPath — jest wykonywane w przekształconym układzie współrzędnych. Canvas przechowuje również własny stos zapisanych stanów poprzez save() i restore().
Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height, Matrix matrix, boolean filter) tworzy nową Bitmap z zastosowaniem zadanego przekształcenia. To potężne narzędzie do tworzenia przeskalowanych, obróconych lub zniekształconych kopii obrazów bez utraty oryginalnej Bitmap. Parametr filter kontroluje wygładzanie przy interpolacji pikseli.
setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) — unikalna funkcja Matrix, niemająca bezpośrednich odpowiedników w CGAffineTransform. Oblicza macierz przekształcającą zadane punkty z jednego wielokąta w drugi. Przy pointCount = 4 tworzy pełne przekształcenie perspektywiczne (projective), pozwalające nakładać Bitmap ze zniekształceniem perspektywy.
Rozważmy praktyczne przykłady użycia Matrix do transformacji grafiki w aplikacji Androida w Kotlin.
Przykład tworzy Matrix dla połączonego przekształcenia: przesunięcie o (100, 50), skalowanie 1.5 raza i obrót o 45 stopni. Użycie postTranslate po preRotate i preScale zmienia kolejność stosowania operacji.
val matrix = Matrix()
matrix.preScale(1.5f, 1.5f)
matrix.preRotate(45f)
matrix.postTranslate(100f, 50f)
// Zastosowanie do punktów
val pts = floatArrayOf(0f, 0f, 100f, 100f)
matrix.mapPoints(pts)
// Zastosowanie do prostokąta
val rect = RectF(0f, 0f, 200f, 100f)
matrix.mapRect(rect)
Przykład demonstruje zastosowanie Matrix do Canvas w onDraw do obracania i skalowania koła wokół jego środka. Zapisywanie i przywracanie stanu Canvas gwarantuje, że transformacja nie wpłynie na kolejne elementy rysowania.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = 100f
canvas.save()
val matrix = Matrix()
matrix.postTranslate(cx, cy)
matrix.postRotate(30f)
matrix.postScale(1.2f, 1.2f)
canvas.concat(matrix)
val paint = Paint().apply {
color = Color.BLUE
style = Paint.Style.FILL
}
canvas.drawCircle(0f, 0f, radius, paint)
canvas.restore()
}
Przykład używa setPolyToPoly do stworzenia perspektywicznego zniekształcenia Bitmap. Cztery punkty źródłowe (narożniki oryginalnego prostokąta) są mapowane na cztery punkty docelowe, tworzące trapez. Rezultat — efekt obrazu nachylonego w przestrzeni.
fun applyPerspective(
bitmap: Bitmap,
skew: Float
): Bitmap {
val w = bitmap.width.toFloat()
val h = bitmap.height.toFloat()
val src = floatArrayOf(0f, 0f, w, 0f,
0f, h, w, h)
val dst = floatArrayOf(skew, 0f, w - skew, 0f,
0f, h, w, h)
val matrix = Matrix()
matrix.setPolyToPoly(src, 0, dst, 0, 4)
return Bitmap.createBitmap(
bitmap, 0, 0,
w.toInt(), h.toInt(),
matrix, true
)
}
Często zadawane pytania
Matrix działa z dwuwymiarowymi przekształceniami afinicznymi i perspektywicznymi poprzez jedną macierz 3×3. Camera (android.graphics.Camera) — to klasa wyższego poziomu imitująca trójwymiarowy aparat: tworzy obrót 3D, a następnie projektuje wynik do 2D Matrix. Camera używa Matrix wewnętrznie.
Utwórz Matrix z postRotate(90f) i przekaż do Bitmap.createBitmap(original, 0, 0, w, h, matrix, true). Pamiętaj, że przy obrocie o 90 stopni szerokość i wysokość wyniku zamieniają się miejscami. Dla obrotu o dowolny kąt może być potrzebne przycięcie, aby uniknąć pustych obszarów.
Użyj matrix.invert(resultMatrix). Metoda zwraca true, jeśli inwersja jest udana, i zapisuje odwrotną macierz w resultMatrix. Inwersja może być niemożliwa, jeśli macierz jest zdegenerowana (np. przy zerowym skalowaniu). Zawsze sprawdzaj zwracaną wartość.
pre mnoży nowe przekształcenie z lewej strony: result = operation × current — operacja jest stosowana przed bieżącymi. post mnoży z prawej strony: result = current × operation — operacja jest stosowana po. Dla sekwencji preRotate + postTranslate: najpierw obrót, potem przesunięcie.
Tak, Matrix jest używany w Jetpack Compose poprzez klasę android.graphics.Matrix w interakcji z Canvas Compose. W Compose dostępne są graphicsLayer do transformacji (scale, rotationX, translationX i inne), ale dla złożonych przekształceń Bitmap stosuje się Matrix w kodzie drawWithContent lub drawBehind.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również