Te warsztaty są częścią kursu Zaawansowany Android w Kotlinie. Najwięcej korzyści z tego kursu uzyskasz, jeśli przejdziesz wszystkie ćwiczenia w kolejności, ale nie jest to obowiązkowe. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej ćwiczeń z zaawansowanego Androida w Kotlinie.
Wprowadzenie
Na potrzeby tego laboratorium przycinanie to sposób definiowania obszarów obrazu, płótna lub mapy bitowej, które są selektywnie rysowane lub nie rysowane na ekranie. Jednym z celów przycinania jest zmniejszenie nadmiernego rysowania. Nadmierne rysowanie to sytuacja, w której piksel na ekranie jest rysowany więcej niż raz, aby wyświetlić ostateczny obraz. Zmniejszając nadmierne rysowanie, minimalizujesz liczbę rysowań piksela lub obszaru wyświetlania, aby zmaksymalizować wydajność rysowania. Przycinanie możesz też wykorzystać do tworzenia ciekawych efektów w projektowaniu interfejsu i animacji.
Na przykład podczas rysowania stosu nakładających się na siebie kart, jak pokazano poniżej, zamiast rysować każdą kartę od dołu do góry, zwykle bardziej efektywne jest rysowanie tylko widocznych części. „Zazwyczaj”, ponieważ operacje przycinania też mają swój koszt, a system Android przeprowadza wiele optymalizacji rysowania.

Aby rysować tylko widoczne części kart, dla każdej z nich określasz obszar przycinania. Na przykład na poniższym diagramie po zastosowaniu prostokąta przycinania do obrazu wyświetlana jest tylko część znajdująca się w tym prostokącie.

Obszar przycinania jest zwykle prostokątem, ale może mieć dowolny kształt lub kombinację kształtów, a nawet tekst. Możesz też określić, czy obszar wewnątrz regionu przycinania ma być uwzględniony czy wykluczony. Możesz na przykład utworzyć okrągły obszar przycinania i wyświetlać tylko to, co znajduje się poza nim.
W tym laboratorium kodu poeksperymentujesz z różnymi sposobami przycinania.
Co warto wiedzieć
Musisz znać:
- Jak utworzyć aplikację z
Activityi uruchomić ją w Android Studio. - Jak utworzyć i narysować
Canvas. - Jak utworzyć niestandardowy
Viewi zastąpićonDraw()orazonSizeChanged().
Czego się nauczysz
- Jak przycinać obiekty, aby rysować na
Canvas. - Jak zapisywać i przywracać stany rysowania na kanwie.
- Jak stosować przekształcenia na obszarze roboczym i tekście.
Jakie zadania wykonasz
- Utwórz aplikację, która rysuje na ekranie przycięte kształty, pokazując różne sposoby przycinania i ich wpływ na widoczność tych kształtów.
- Narysujesz też przetłumaczony i przekrzywiony tekst.
Aplikacja ClippingExample pokazuje, jak używać i łączyć kształty, aby określać, które części obszaru rysowania mają być wyświetlane w widoku. Gotowa aplikacja będzie wyglądać jak na zrzucie ekranu poniżej.

Aplikację utworzysz od podstaw, więc musisz skonfigurować projekt, zdefiniować wymiary i ciągi znaków oraz zadeklarować niektóre zmienne.
Krok 1. Utwórz projekt ClippingExample
- Utwórz projekt w Kotlinie o nazwie
ClippingExamplena podstawie szablonu Pusta aktywność. Użyjcom.example.androidjako prefiksu nazwy pakietu. - Otwórz pokój
MainActivity.kt. - W metodzie
onCreate()zastąp domyślny widok treści i ustaw widok treści na nową instancjęClippedView. Będzie to Twój widok niestandardowy dla przykładów wycinków, które utworzysz w następnym kroku.
setContentView(ClippedView(this))- Na tym samym poziomie co
MainActivity.ktutwórz nowy plik i klasę Kotlin dla widoku niestandardowego o nazwieClippedView, który rozszerzaView. Nadaj mu podpis widoczny poniżej. Pozostała część pracy będzie wykonywana w tymClippedView. Adnotacja@JvmOverloadsinformuje kompilator języka Kotlin, aby wygenerował przeciążenia tej funkcji, które zastępują domyślne wartości parametrów.
class ClippedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}Krok 2. Dodaj wymiary i zasoby tekstowe
- Zdefiniuj wymiary, których będziesz używać w przypadku przyciętych widoków, w nowym pliku zasobów w
res/values/dimens.xml. Te domyślne wymiary są zakodowane na stałe i dopasowane do dość małego ekranu.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">90dp</dimen>
<dimen name="clipRectBottom">90dp</dimen>
<dimen name="clipRectTop">0dp</dimen>
<dimen name="clipRectLeft">0dp</dimen>
<dimen name="rectInset">8dp</dimen>
<dimen name="smallRectOffset">40dp</dimen>
<dimen name="circleRadius">30dp</dimen>
<dimen name="textOffset">20dp</dimen>
<dimen name="strokeWidth">4dp</dimen>
<dimen name="textSize">18sp</dimen>
</resources>Aby aplikacja dobrze wyglądała na większym ekranie (i aby łatwiej było zobaczyć szczegóły), możesz utworzyć plik dimens z większymi wartościami, który będzie stosowany tylko na większych ekranach.
- W Android Studio kliknij prawym przyciskiem myszy folder values i wybierz New > Values resource file (Nowy > Plik zasobów wartości).
- W oknie Nowy plik zasobów nadaj plikowi nazwę
dimens. W sekcji Dostępne kwalifikatory wybierz Najmniejsza szerokość ekranu i kliknij przycisk >>, aby dodać go do sekcji Wybrane kwalifikatory. Wpisz 480 w polu Najmniejsza szerokość ekranu i kliknij OK.

- Plik powinien być widoczny w folderze wartości, jak pokazano poniżej.

- Jeśli nie widzisz pliku, przełącz się na widok Pliki projektu aplikacji. Pełna ścieżka do nowego pliku jest taka jak poniżej:
ClippingExample/app/src/main/res/values-sw480dp/dimens.xml.

- Zastąp domyślną zawartość pliku
values-sw480dp/dimens.xmlwymiarami podanymi poniżej.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">120dp</dimen>
<dimen name="clipRectBottom">120dp</dimen>
<dimen name="rectInset">10dp</dimen>
<dimen name="smallRectOffset">50dp</dimen>
<dimen name="circleRadius">40dp</dimen>
<dimen name="textOffset">25dp</dimen>
<dimen name="strokeWidth">6dp</dimen>
</resources>- W sekcji
strings.xmldodaj te ciągi znaków. Będą one używane do wyświetlania tekstu na obszarze roboczym.
<string name="clipping">Clipping</string>
<string name="translated">translated text</string>
<string name="skewed">"Skewed and "</string>Krok 3. Utwórz i zainicjuj obiekty Paint i Path
- Wróć do widoku Android w projekcie.
- W sekcji
ClippedViewzdefiniuj zmiennąPaint, której chcesz używać do rysowania. Włącz wygładzanie i użyj szerokości linii oraz rozmiaru tekstu zdefiniowanych w wymiarach, jak pokazano poniżej.
private val paint = Paint().apply {
// Smooth out edges of what is drawn without affecting shape.
isAntiAlias = true
strokeWidth = resources.getDimension(R.dimen.strokeWidth)
textSize = resources.getDimension(R.dimen.textSize)
}- W
ClippedViewutwórz i zainicjujPath, aby lokalnie przechowywać ścieżkę narysowanego kształtu. Importujandroid.graphics.Path.
private val path = Path()Krok 4. Skonfiguruj kształty
W tej aplikacji wyświetlasz kilka wierszy i 2 kolumny kształtów przyciętych na różne sposoby.
Wszystkie te usługi mają wspólne cechy:
- Duży prostokąt (kwadrat) pełniący funkcję kontenera
- linia ukośna przecinająca duży prostokąt,
- Okrąg
- krótki ciąg tekstu,

W tym kroku skonfigurujesz wymiary tych kształtów z zasobów, aby później pobierać je tylko raz.
- W
ClippedViewponiżejpathdodaj zmienne wymiarów prostokąta przycinania wokół całego zestawu kształtów.
private val clipRectRight = resources.getDimension(R.dimen.clipRectRight)
private val clipRectBottom = resources.getDimension(R.dimen.clipRectBottom)
private val clipRectTop = resources.getDimension(R.dimen.clipRectTop)
private val clipRectLeft = resources.getDimension(R.dimen.clipRectLeft)- Dodaj zmienne dla wcięcia prostokąta i przesunięcia małego prostokąta.
private val rectInset = resources.getDimension(R.dimen.rectInset)
private val smallRectOffset = resources.getDimension(R.dimen.smallRectOffset)- Dodaj zmienną dla promienia okręgu. Jest to promień okręgu narysowanego wewnątrz prostokąta.
private val circleRadius = resources.getDimension(R.dimen.circleRadius)- Dodaj przesunięcie i rozmiar tekstu, który ma być rysowany wewnątrz prostokąta.
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)Krok 4. Skonfiguruj lokalizacje wierszy i kolumn
Kształty tej aplikacji są wyświetlane w 2 kolumnach i 4 wierszach, co zależy od wartości wymiarów skonfigurowanych powyżej. Obliczenia matematyczne nie są częścią tych ćwiczeń, ale przyjrzyj się im podczas kopiowania kodu podanego w tym kroku.
- Skonfiguruj współrzędne dla 2 kolumn.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight- Dodaj współrzędne dla każdego wiersza, w tym ostatniego wiersza przekształconego tekstu.
private val rowOne = rectInset
private val rowTwo = rowOne + rectInset + clipRectBottom
private val rowThree = rowTwo + rectInset + clipRectBottom
private val rowFour = rowThree + rectInset + clipRectBottom
private val textRow = rowFour + (1.5f * clipRectBottom)- Uruchom aplikację. Powinna się otworzyć z pustym białym ekranem pod nazwą aplikacji.

W onDraw() wywołujesz metody rysowania 7 różnych przyciętych prostokątów, jak pokazano na zrzucie ekranu aplikacji poniżej. Wszystkie prostokąty są rysowane w ten sam sposób. Różnią się tylko zdefiniowanymi regionami przycinania i położeniem na ekranie.

Algorytm używany do rysowania prostokątów działa zgodnie ze schematem i wyjaśnieniem poniżej. Podsumowując, rysujesz serię prostokątów, przesuwając punkt początkowy Canvas. W teorii obejmuje to te kroki:

(1) Najpierw przesuń Canvas w miejsce, w którym chcesz narysować prostokąt. Oznacza to, że zamiast obliczać, gdzie należy narysować kolejny prostokąt i wszystkie inne kształty, przesuwasz Canvaspunkt początkowy, czyli jego układ współrzędnych.
(2) Następnie narysuj prostokąt w nowym punkcie początkowym obszaru roboczego. Oznacza to, że rysujesz kształty w tym samym miejscu w przetłumaczonym układzie współrzędnych. Jest to znacznie prostsze i nieco bardziej wydajne.
(3) Na koniec przywróć Canvas do pierwotnego Origin.
Oto algorytm, który musisz wdrożyć:
- W
onDraw()wywołaj funkcję, aby wypełnićCanvasszarym kolorem tła i narysować oryginalne kształty. - Wywołaj funkcję dla każdego wyciętego prostokąta i tekstu do narysowania.
W przypadku każdego prostokąta lub tekstu:
- Zapisz bieżący stan
Canvas, aby móc przywrócić go do stanu początkowego. - Przesuń
Originna płótnie w miejsce, w którym chcesz rysować. - Stosowanie kształtów i ścieżek przycinania.
- Narysuj prostokąt lub tekst.
- Przywróć stan urządzenia
Canvas.
Krok: zastąpienie metody onDraw()
- Zastąp
onDraw(), jak pokazano w poniższym kodzie. Dla każdego rysowanego kształtu wywołujesz funkcję, którą zaimplementujesz później.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawBackAndUnclippedRectangle(canvas)
drawDifferenceClippingExample(canvas)
drawCircularClippingExample(canvas)
drawIntersectionClippingExample(canvas)
drawCombinedClippingExample(canvas)
drawRoundedRectangleClippingExample(canvas)
drawOutsideClippingExample(canvas)
drawSkewedTextExample(canvas)
drawTranslatedTextExample(canvas)
// drawQuickRejectExample(canvas)
}- Utwórz stuby dla każdej funkcji rysowania, aby kod nadal się kompilował. Kod możesz skopiować poniżej.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
}
private fun drawDifferenceClippingExample(canvas: Canvas){
}
private fun drawCircularClippingExample(canvas: Canvas){
}
private fun drawIntersectionClippingExample(canvas: Canvas){
}
private fun drawCombinedClippingExample(canvas: Canvas){
}
private fun drawRoundedRectangleClippingExample(canvas: Canvas){
}
private fun drawOutsideClippingExample(canvas: Canvas){
}
private fun drawTranslatedTextExample(canvas: Canvas){
}
private fun drawSkewedTextExample(canvas: Canvas){
}
private fun drawQuickRejectExample(canvas: Canvas){
}Aplikacja rysuje ten sam prostokąt i kształty 7 razy: najpierw bez przycinania, a potem 6 razy z zastosowaniem różnych ścieżek przycinania. Metoda drawClippedRectangle() wyodrębnia kod rysowania jednego prostokąta, jak pokazano poniżej.

Krok 1. Utwórz metodę drawClippedRectangle()
- Utwórz metodę
drawClippedRectangle(), która przyjmuje argumentcanvastypuCanvas.
private fun drawClippedRectangle(canvas: Canvas) {
}- W metodzie
drawClippedRectangle()ustaw granice prostokąta przycinania dla całego kształtu. Zastosuj prostokąt przycinania, który ogranicza rysowanie tylko do kwadratu.
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)Metoda Canvas.clipRect(...) zmniejsza obszar ekranu, na którym można wykonywać kolejne operacje rysowania. Ustawia granice przycinania jako przecięcie przestrzenne bieżącego prostokąta przycinania i prostokąta przekazanego do clipRect(). Istnieje wiele wariantów metody clipRect(), które akceptują różne formy regionów i umożliwiają różne operacje na prostokącie przycinania.
- Wypełnij
canvaskolorem białym. Tak. Całe płótno, ponieważ nie rysujesz prostokątów, tylko przycinasz. Ze względu na prostokąt przycinania wypełniany jest tylko obszar zdefiniowany przez ten prostokąt, co powoduje utworzenie białego prostokąta. Pozostała część powierzchni pozostanie szara.
canvas.drawColor(Color.WHITE)- Zmień kolor na czerwony i narysuj linię po przekątnej wewnątrz prostokąta przycinania.
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)- Ustaw kolor na zielony i narysuj okrąg w prostokącie przycinania.
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)- Ustaw kolor na niebieski i narysuj tekst wyrównany do prawej krawędzi prostokąta przycinania. Użyj ikony
canvas.drawText(), aby narysować tekst.
paint.color = Color.BLUE
// Align the RIGHT side of the text with the origin.
paint.textSize = textSize
paint.textAlign = Paint.Align.RIGHT
canvas.drawText(
context.getString(R.string.clipping),
clipRectRight,textOffset,paint
)Krok 2. Zaimplementuj metodę drawBackAndUnclippedRectangle()
- Aby zobaczyć działanie metody
drawClippedRectangle(), narysuj pierwszy nieprzycięty prostokąt, implementując metodędrawBackAndUnclippedRectangle()w sposób pokazany poniżej. Zapiszcanvas, przetłumacz na pozycję pierwszego wiersza i pierwszej kolumny, narysuj, wywołującdrawClippedRectangle(), a następnie przywróćcanvasdo poprzedniego stanu.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
canvas.drawColor(Color.GRAY)
canvas.save()
canvas.translate(columnOne,rowOne)
drawClippedRectangle(canvas)
canvas.restore()
}- Uruchom aplikację. Powinien pojawić się pierwszy biały prostokąt z okręgiem, czerwoną linią i tekstem na szarym tle.

W przykładach przycinania poniżej zastosowano różne kombinacje regionów przycinania, aby uzyskać efekty graficzne. Dowiesz się też, jak łączyć regiony przycinania, aby tworzyć dowolne kształty.
Każda z tych metod jest oparta na tym samym wzorcu.
- Zapisz bieżący stan obszaru roboczego:
canvas.save()
Kontekst aktywności utrzymuje stos stanów rysowania. Stan rysowania obejmuje bieżącą macierz przekształcenia i bieżący region przycinania. Możesz zapisać bieżący stan, wykonać działania, które zmieniają stan rysunku (np. przesunąć lub obrócić obszar roboczy), a następnie przywrócić zapisany stan rysunku. (Uwaga: to polecenie jest podobne do polecenia „stash” w systemie git).
Jeśli rysunek zawiera przekształcenia, łączenie i cofanie przekształceń przez ich odwracanie jest podatne na błędy. Jeśli na przykład przetłumaczysz, rozciągniesz, a następnie obrócisz element, szybko stanie się to skomplikowane. Zamiast tego zapisz stan obszaru roboczego, zastosuj przekształcenia, narysuj element, a następnie przywróć poprzedni stan.
Możesz na przykład zdefiniować region przycinania i zapisać ten stan. Następnie przetłumacz obszar roboczy, dodaj region przycinania i obróć go. Po narysowaniu czegoś możesz przywrócić pierwotny stan wycinania i wykonać inne przekształcenie przez przesunięcie i przekształcenie przez pochylenie, jak pokazano na diagramie.

- Przetłumacz początek obszaru rysowania na współrzędne wiersza i kolumny:
canvas.translate()
Przesunięcie początku obszaru rysowania i narysowanie tego samego w nowym układzie współrzędnych jest dużo prostsze niż przesuwanie wszystkich elementów do narysowania. (Wskazówka: możesz użyć tej samej techniki do obracania elementów).
- W razie potrzeby zastosuj przekształcenia do elementu
path. - Zastosuj przycinanie:
canvas.clipPath(path) - Narysuj kształty:
drawClippedRectangle() or drawText() - Przywróć poprzedni stan obszaru roboczego:
canvas.restore()
Krok 1. Zaimplementuj funkcję drawDifferenceClippingExample(canvas)
Dodaj kod, aby narysować drugi prostokąt, który wykorzystuje różnicę między dwoma prostokątami przycinania do utworzenia efektu ramki obrazu.

Użyj poniższego kodu, który wykonuje te czynności:
- Zapisz obszar roboczy.
- Przesuń początek obszaru rysowania do otwartej przestrzeni w pierwszym wierszu, drugiej kolumnie, po prawej stronie pierwszego prostokąta.
- Zastosuj 2 prostokąty przycinania. Operator
DIFFERENCEodejmuje drugi prostokąt od pierwszego.
- Aby narysować zmodyfikowany obszar rysowania, wywołaj metodę
drawClippedRectangle(). - Przywróć stan obszaru roboczego.
private fun drawDifferenceClippingExample(canvas: Canvas) {
canvas.save()
// Move the origin to the right for the next rectangle.
canvas.translate(columnTwo,rowOne)
// Use the subtraction of two clipping rectangles to create a frame.
canvas.clipRect(
2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset
)
// The method clipRect(float, float, float, float, Region.Op
// .DIFFERENCE) was deprecated in API level 26. The recommended
// alternative method is clipOutRect(float, float, float, float),
// which is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
canvas.clipRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset,
Region.Op.DIFFERENCE
)
} else {
canvas.clipOutRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset
)
}
drawClippedRectangle(canvas)
canvas.restore()
}- Uruchom aplikację. Powinna wyglądać tak:

Krok 2. Wdróż funkcję drawCircularClippingExample(canvas)
Następnie dodaj kod, który narysuje prostokąt z okrągłym obszarem przycinania utworzonym na podstawie okrągłej ścieżki. W ten sposób usuniesz (nie narysujesz) okrąg, a zamiast niego pojawi się szare tło.

private fun drawCircularClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowTwo)
// Clears any lines and curves from the path but unlike reset(),
// keeps the internal data structure for faster reuse.
path.rewind()
path.addCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,Path.Direction.CCW
)
// The method clipPath(path, Region.Op.DIFFERENCE) was deprecated in
// API level 26. The recommended alternative method is
// clipOutPath(Path), which is currently available in
// API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipPath(path, Region.Op.DIFFERENCE)
} else {
canvas.clipOutPath(path)
}
drawClippedRectangle(canvas)
canvas.restore()
}Krok 3. Wdróż funkcję drawIntersectionClippingExample(canvas)
Następnie dodaj kod, który narysuje przecięcie 2 prostokątów przycinających w drugim wierszu i kolumnie.


Pamiętaj, że wygląd tego obszaru będzie się różnić w zależności od rozdzielczości ekranu. Eksperymentuj z wymiarem smallRectOffset, aby zmieniać rozmiar widocznego obszaru. Mniejsza wartość smallRectOffset powoduje powiększenie regionu na ekranie.
private fun drawIntersectionClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowTwo)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight - smallRectOffset,
clipRectBottom - smallRectOffset
)
// The method clipRect(float, float, float, float, Region.Op
// .INTERSECT) was deprecated in API level 26. The recommended
// alternative method is clipRect(float, float, float, float), which
// is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom,
Region.Op.INTERSECT
)
} else {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom
)
}
drawClippedRectangle(canvas)
canvas.restore()
}
Krok 4. Wdróż funkcję drawCombinedClippingExample(canvas)
Następnie połącz kształty, okrąg i prostokąt, i narysuj dowolną ścieżkę, aby zdefiniować obszar przycinania.

private fun drawCombinedClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowThree)
path.rewind()
path.addCircle(
clipRectLeft + rectInset + circleRadius,
clipRectTop + circleRadius + rectInset,
circleRadius,Path.Direction.CCW
)
path.addRect(
clipRectRight / 2 - circleRadius,
clipRectTop + circleRadius + rectInset,
clipRectRight / 2 + circleRadius,
clipRectBottom - rectInset,Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
Krok 5. Wdróż funkcję drawRoundedRectangleClippingExample(canvas)
Następnie dodaj zaokrąglony prostokąt, który jest często używanym kształtem przycinania.

- Na najwyższym poziomie utwórz i zainicjuj zmienną prostokąta.
RectFto klasa, która zawiera współrzędne prostokąta w postaci liczb zmiennoprzecinkowych.
private var rectF = RectF(
rectInset,
rectInset,
clipRectRight - rectInset,
clipRectBottom - rectInset
)- Zaimplementuj funkcję
drawRoundedRectangleClippingExample(). FunkcjaaddRoundRect()przyjmuje prostokąt, wartości x i y promienia zaokrąglenia rogu oraz kierunek, w którym ma być rysowany kontur zaokrąglonego prostokąta.Path.Directionokreśla, jak zamknięte kształty (np. prostokąty, owale) są orientowane po dodaniu do ścieżki.CCWoznacza obrót w lewo.
private fun drawRoundedRectangleClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowThree)
path.rewind()
path.addRoundRect(
rectF,clipRectRight / 4,
clipRectRight / 4, Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
Krok 6. Wdróż funkcję drawOutsideClippingExample(canvas)
Przytnij obszar na zewnątrz prostokąta, podwajając wcięcia prostokąta przycinania.

private fun drawOutsideClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne,rowFour)
canvas.clipRect(2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset)
drawClippedRectangle(canvas)
canvas.restore()
}
Krok 7. Wdróż funkcję drawTranslatedTextExample(canvas)
Tekst rysowany nie różni się od innych kształtów i możesz stosować do niego przekształcenia. Możesz na przykład przetłumaczyć tekst, tłumacząc obszar roboczy i rysując tekst.

- Zaimplementuj funkcję poniżej.
private fun drawTranslatedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.GREEN
// Align the RIGHT side of the text with the origin.
paint.textAlign = Paint.Align.LEFT
// Apply transformation to canvas.
canvas.translate(columnTwo,textRow)
// Draw text.
canvas.drawText(context.getString(R.string.translated),
clipRectLeft,clipRectTop,paint)
canvas.restore()
}- Uruchom aplikację, aby zobaczyć przetłumaczony tekst.

Krok 8. Wdróż drawSkewedTextExample(canvas)
Możesz też pochylić tekst. czyli zniekształcać je na różne sposoby.

- Utwórz poniższą funkcję w języku
ClippedView.
private fun drawSkewedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.YELLOW
paint.textAlign = Paint.Align.RIGHT
// Position text.
canvas.translate(columnTwo, textRow)
// Apply skew transformation.
canvas.skew(0.2f, 0.3f)
canvas.drawText(context.getString(R.string.skewed),
clipRectLeft, clipRectTop, paint)
canvas.restore()
}- Uruchom aplikację, aby zobaczyć przekrzywiony tekst narysowany przed przetłumaczonym tekstem.

Metoda quickReject() Canvas umożliwia sprawdzenie, czy po zastosowaniu wszystkich przekształceń określony prostokąt lub ścieżka znajdzie się całkowicie poza aktualnie widocznymi regionami.
Metoda quickReject() jest niezwykle przydatna, gdy tworzysz bardziej złożone rysunki i musisz to zrobić jak najszybciej. Dzięki quickReject() możesz skutecznie decydować, których obiektów nie musisz w ogóle rysować, i nie musisz pisać własnej logiki przecięcia.
- Metoda
quickReject()zwraca wartośćtrue, jeśli prostokąt lub ścieżka nie byłyby w ogóle widoczne na ekranie. W przypadku częściowego pokrywania się treści nadal musisz samodzielnie sprawdzić, czy nie doszło do naruszenia. - Wartość
EdgeTypetoAA(Wygładzanie: krawędzie są zaokrąglane, ponieważ mogą być wygładzone) lubBW(Czarno-białe: krawędzie są zaokrąglane do najbliższej granicy piksela).
Istnieje kilka wersji quickReject(), które znajdziesz też w dokumentacji.
| quickReject |
| quickReject |
| quickReject |
W tym ćwiczeniu narysujesz nowy wiersz poniżej tekstu i wewnątrz znaku clipRect, tak jak wcześniej.
- Najpierw dzwonisz do
quickReject(), rysując prostokątinClipRectangle, który nakłada się naclipRect. ZatemquickReject()zwraca wartość fałszywą,clipRectjest wypełniony znakiemBLACK, a prostokątinClipRectanglejest narysowany.

- Następnie zmień kod i wywołaj funkcję
quickReject(), używającnotInClipRectangle.quickReject()zwraca teraz wartość „prawda”, aclipRectjest wypełniony wartościąWHITE, anotInClipRectanglenie jest rysowany.

W przypadku złożonych rysunków możesz szybko sprawdzić, które kształty znajdują się całkowicie poza obszarem przycinania, a w przypadku których konieczne będzie wykonanie dodatkowych obliczeń i rysowania, ponieważ znajdują się one częściowo lub całkowicie w obszarze przycinania.
Krok: Eksperymentowanie z funkcją quickReject()
- Na najwyższym poziomie utwórz zmienną dla współrzędnych y dodatkowego wiersza.
private val rejectRow = rowFour + rectInset + 2*clipRectBottom- Dodaj do pliku
ClippedViewtę funkcjędrawQuickRejectExample(). Przeczytaj kod, ponieważ zawiera on wszystko, co musisz wiedzieć, aby korzystać zquickReject().
private fun drawQuickRejectExample(canvas: Canvas) {
val inClipRectangle = RectF(clipRectRight / 2,
clipRectBottom / 2,
clipRectRight * 2,
clipRectBottom * 2)
val notInClipRectangle = RectF(RectF(clipRectRight+1,
clipRectBottom+1,
clipRectRight * 2,
clipRectBottom * 2))
canvas.save()
canvas.translate(columnOne, rejectRow)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
if (canvas.quickReject(
inClipRectangle, Canvas.EdgeType.AA)) {
canvas.drawColor(Color.WHITE)
}
else {
canvas.drawColor(Color.BLACK)
canvas.drawRect(inClipRectangle, paint
)
}
canvas.restore()
}- W pliku
onDraw()odkomentuj wywołanie funkcjidrawQuickRejectExample(). - Uruchom aplikację. Zobaczysz czarny prostokąt, który jest wypełnionym obszarem przycinania, oraz fragmenty znaku
inClipRectangle, ponieważ oba prostokąty się nakładają, więc funkcjaquickReject()zwraca wartośćfalsei rysuje znakinClipRectangle.

- W
drawQuickRejectExample()zmień kod, aby uruchomićquickReject()nanotInClipRectangle.. TerazquickReject()zwracatrue, a obszar przycinania jest wypełniony białym kolorem.

Pobierz kod ukończonego ćwiczenia.
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-clipping
Możesz też pobrać repozytorium jako plik ZIP, rozpakować go i otworzyć w Android Studio.
Contextaktywności zachowuje stan, który przechowuje przekształcenia i obszary przycinania dlaCanvas.- Użyj narzędzi
canvas.save()icanvas.restore(), aby rysować i wrócić do pierwotnego stanu obszaru roboczego. - Aby narysować kilka kształtów na obszarze roboczym, możesz obliczyć ich lokalizację lub przenieść (przesunąć) punkt początkowy powierzchni rysowania. To drugie rozwiązanie może ułatwić tworzenie metod narzędziowych dla powtarzających się sekwencji rysowania.
- Obszary przycinania mogą mieć dowolny kształt, kombinację kształtów lub ścieżkę.
- Możesz dodawać, odejmować i przecinać regiony przycinania, aby uzyskać dokładnie ten region, którego potrzebujesz.
- Możesz przekształcać tekst, przekształcając obszar roboczy.
- Metoda
quickReject()Canvasumożliwia sprawdzenie, czy określony prostokąt lub ścieżka znajdują się całkowicie poza aktualnie widocznymi regionami.
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
CanvaszajęciaBitmapzajęciaViewzajęciaPaintzajęcia- Konfiguracje
Bitmap.config - Operatory
Region.Op PathzajęciaCanvaszajęciaBitmapzajęciaViewzajęciaPaintzajęcia- Konfiguracje
Bitmap.config - Operatory
Region.Op Pathzajęciaandroid.graphicsnarzędzia graficzne,- Konfiguracje
Bitmap.ConfigCanvas - Canvas i obiekty Drawable
- Jak działa funkcja canvas.translate()
- Wyjaśnienie funkcji save() i restore() w kontekście Canvas
- clipping
- overdraw.
@JvmOverloads
Szczegółowe wyjaśnienie, jak framework Androida rysuje obraz na ekranie, znajdziesz w serii artykułów Architektura grafiki.
W tej sekcji znajdziesz listę możliwych zadań domowych dla uczniów, którzy wykonują ten moduł w ramach kursu prowadzonego przez instruktora. Nauczyciel musi:
- W razie potrzeby przypisz pracę domową.
- Poinformuj uczniów, jak przesyłać projekty.
- Oceń zadania domowe.
Instruktorzy mogą korzystać z tych sugestii w dowolnym zakresie i mogą zadawać inne zadania domowe, które uznają za odpowiednie.
Jeśli wykonujesz ten kurs samodzielnie, możesz użyć tych zadań domowych, aby sprawdzić swoją wiedzę.
Odpowiedz na te pytania
Pytanie 1
Jakiej metody używasz, aby skutecznie wykluczyć kształty z rysowania?
▢ excludeFromDrawing()
▢ quickReject()
▢ onDraw()
▢ clipRect()
Pytanie 2
Canvas.save() i Canvas.restore() zapisują i przywracają które informacje?
▢ Kolor, szerokość linii itp.
▢ Tylko bieżące transformacje
▢ Aktualne przekształcenia i obszar przycinania
▢ Tylko bieżący region przycinania
Pytanie 3
Paint.Align określa:
▢ Jak wyrównać te kształty rysunku
▢ Po której stronie punktu początkowego jest rysowany tekst
▢ Miejsce wyrównania w regionie przycinania
▢ Po której stronie tekstu ma być wyrównanie do punktu początkowego
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z zaawansowanego Androida w Kotlinie.