Memotong Objek Canvas

Codelab ini adalah bagian dari kursus Lanjutan Android di Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika Anda mengerjakan codelab secara berurutan, tetapi ini tidak wajib. Semua codelab kursus tercantum di halaman landing codelab Android Lanjutan di Kotlin.

Pengantar

Untuk tujuan codelab ini, pemangkasan adalah cara untuk menentukan area gambar, kanvas, atau bitmap yang secara selektif digambar atau tidak digambar ke layar. Salah satu tujuan kliping adalah mengurangi overdraw. Overdraw adalah saat piksel di layar digambar lebih dari sekali untuk menampilkan gambar akhir. Saat mengurangi overdraw, Anda meminimalkan frekuensi penggambaran piksel atau region tampilan untuk memaksimalkan performa penggambaran. Anda juga dapat menggunakan clipping untuk membuat efek yang menarik dalam animasi dan desain antarmuka pengguna.

Misalnya, ketika Anda menggambar tumpukan kartu yang tumpang-tindih seperti yang ditunjukkan di bawah ini, daripada menggambar sepenuhnya setiap kartu dari bawah ke atas, biasanya lebih efisien untuk hanya menggambar bagian yang terlihat. "Biasanya&rut; karena operasi clipping juga memiliki biaya, dan secara keseluruhan, sistem Android melakukan banyak pengoptimalan gambar.

Untuk hanya menggambar bagian kartu yang terlihat, tentukan area pemangkasan untuk setiap kartu. Misalnya dalam diagram di bawah ini, saat kotak potong persegi panjang diterapkan ke gambar, hanya bagian di dalam persegi panjang yang ditampilkan.

Region pemangkasan biasanya berbentuk persegi panjang, tetapi dapat berupa bentuk atau kombinasi bentuk apa pun, bahkan teks. Anda juga dapat menentukan apakah Anda ingin wilayah di dalam wilayah kliping disertakan atau dikecualikan. Misalnya, Anda dapat membuat area kliping melingkar dan hanya menampilkan apa yang ada di luar lingkaran.

Dalam codelab ini, Anda akan bereksperimen dengan berbagai cara penyesuaian.

Yang harus sudah Anda ketahui

Anda harus memahami:

  • Cara membuat aplikasi dengan Activity dan menjalankannya menggunakan Android Studio.
  • Cara membuat dan menggambar pada Canvas.
  • Cara membuat View kustom, serta mengganti onDraw() dan onSizeChanged().

Yang akan Anda pelajari

  • Cara membuat klip objek untuk digambar di Canvas.
  • Cara menyimpan dan memulihkan status gambar kanvas.
  • Cara menerapkan transformasi ke kanvas dan ke teks.

Yang akan Anda lakukan

  • Membuat aplikasi yang menggambar bentuk terpotong di layar yang menunjukkan berbagai cara klip dan hasilnya pada visibilitas bentuk tersebut.
  • Anda juga akan menggambar beberapa teks yang diterjemahkan dan miring.

Aplikasi ClippingExample menunjukkan cara menggunakan dan menggabungkan bentuk untuk menentukan bagian kanvas yang ditampilkan dalam tampilan. Aplikasi akhir Anda akan terlihat seperti screenshot di bawah ini.

Anda akan membuat aplikasi ini dari awal, jadi Anda harus menyiapkan project, menentukan dimensi dan string, serta mendeklarasikan beberapa variabel.

Langkah 1: Buat project ClippingExample

  1. Buat project Kotlin bernama ClippingExample dengan template Empty Activity. Gunakan com.example.android untuk awalan nama paket.
  2. Buka MainActivity.kt.
  3. Pada metode onCreate(), ganti tampilan konten default dan tetapkan tampilan konten ke instance ClippedView yang baru. Ini akan menjadi tampilan khusus Anda untuk contoh klip yang akan Anda buat berikutnya.
setContentView(ClippedView(this))
  1. Di tingkat yang sama dengan MainActivity.kt, buat file dan class Kotlin baru untuk tampilan kustom bernama ClippedView yang memperluas View. Berikan tanda tangan yang ditampilkan di bawah ini. Semua pekerjaan Anda lainnya akan berada di dalam ClippedView ini. Anotasi @JvmOverloads menginstruksikan compiler Kotlin untuk menghasilkan overload untuk fungsi ini yang menggantikan nilai parameter default.
class ClippedView @JvmOverloads constructor(
   context: Context,
   attrs: AttributeSet? = null,
   defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}

Langkah 2: Tambahkan dimensi dan resource string

  1. Tentukan dimensi yang akan Anda gunakan untuk tampilan yang terpotong dalam file resource baru di res/values/dimens.xml. Dimensi default ini di-hardcode dan diubah ukurannya agar sesuai dengan layar yang cukup kecil.
<?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>

Agar aplikasi terlihat bagus di layar yang lebih besar (dan agar lebih mudah melihat detail), Anda dapat membuat file dimens dengan nilai lebih besar yang hanya berlaku untuk layar yang lebih besar.

  1. Di Android Studio, klik kanan folder values dan pilih New > Values resource file.
  2. Pada dialog New Resource File, panggil file dimens. Pada Qualifier yang tersedia, pilih Lebar Layar Terkecil dan klik tombol >> untuk menambahkannya ke Penentu yang Dipilih. Masukkan 480 ke kotak Lebar layar terkecil, lalu klik Oke.

  1. File akan ditampilkan di folder nilai seperti yang ditunjukkan di bawah ini.

  1. Jika file tidak dapat dilihat, beralihlah ke tampilan Project Files aplikasi. Jalur lengkap file baru adalah seperti yang ditunjukkan di bawah: ClippingExample/app/src/main/res/values-sw480dp/dimens.xml.

  1. Ganti konten default file values-sw480dp/dimens.xml dengan dimensi di bawah.
<?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>
  1. Di strings.xml, tambahkan string berikut. Ini akan digunakan untuk menampilkan teks di kanvas.
<string name="clipping">Clipping</string>
<string name="translated">translated text</string>
<string name="skewed">"Skewed and "</string>

Langkah 3: Buat dan inisialisasi Paint dan objek Path

  1. Beralih kembali ke tampilan Android project Anda.
  2. Di ClippedView, tentukan variabel Paint yang akan digambar. Aktifkan anti-aliasing, dan gunakan lebar guratan dan ukuran teks yang ditetapkan dalam dimensi, seperti yang ditunjukkan di bawah.
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)
}
  1. Di ClippedView, buat dan inisialisasi Path untuk menyimpan secara lokal jalur yang telah digambar. Impor android.graphics.Path.
private val path = Path()

Langkah 4: Menyiapkan bentuk

Dalam aplikasi ini, Anda menampilkan beberapa baris dan dua kolom bentuk yang terpotong dengan berbagai cara.

Semuanya memiliki kesamaan:

  • Persegi panjang (persegi) yang berfungsi sebagai penampung
  • Garis diagonal di persegi panjang yang besar
  • Lingkaran
  • String teks singkat

Pada langkah ini, Anda menyiapkan dimensi untuk bentuk tersebut dari resource, sehingga Anda hanya perlu mendapatkan dimensi satu kali saat menggunakannya nanti.

  1. Pada ClippedView, di bawah path, tambahkan variabel untuk dimensi untuk clipping persegi panjang di sekitar seluruh kumpulan bentuk.
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)
  1. Tambahkan variabel untuk inset persegi panjang dan offset persegi kecil.
private val rectInset = resources.getDimension(R.dimen.rectInset)
private val smallRectOffset = resources.getDimension(R.dimen.smallRectOffset)
  1. Tambahkan variabel untuk radius lingkaran. Ini adalah jari-jari lingkaran yang digambar di dalam persegi panjang.
private val circleRadius = resources.getDimension(R.dimen.circleRadius)
  1. Tambahkan offset dan ukuran teks untuk teks yang digambar di dalam persegi panjang.
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)

Langkah 4: Siapkan lokasi baris dan kolom

Bentuk aplikasi ini ditampilkan dalam dua kolom dan empat baris, yang ditentukan berdasarkan nilai dimensi yang disiapkan di atas. Matematika untuk ini bukan bagian dari codelab ini, tetapi lihat saat Anda menyalin ke kode yang diberikan di langkah ini.

  1. Siapkan koordinat untuk dua kolom.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight
  1. Tambahkan koordinat untuk setiap baris, termasuk baris terakhir untuk teks yang ditransformasi.
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)
  1. Jalankan aplikasi Anda. Aplikasi akan terbuka dengan layar putih kosong di bawah nama aplikasi.

Di onDraw(), Anda memanggil metode untuk menggambar tujuh kotak terpotong yang berbeda seperti yang ditunjukkan pada screenshot aplikasi di bawah ini. Semua persegi panjang digambar dengan cara yang sama; satu-satunya perbedaan adalah area clipping dan lokasi yang ditetapkan pada layar.

Algoritme yang digunakan untuk menggambar persegi panjang berfungsi seperti yang ditunjukkan di diagram dan penjelasan di bawah ini. Singkatnya, Anda menggambar rangkaian persegi panjang dengan memindahkan asal Canvas. Secara konseptual, ini terdiri dari langkah-langkah berikut:

(1) Pertama, terjemahkan Canvas ke tempat Anda ingin menggambar persegi panjang. Artinya, Anda dapat memindahkan tempat asal Canvas persegi panjang dan semua bentuk lainnya, yaitu sistem koordinatnya.

(2) Kemudian, Anda menggambar persegi panjang di tempat asal kanvas baru. Artinya, Anda menggambar bentuk di lokasi yang sama dalam sistem koordinat yang diterjemahkan. Ini jauh lebih sederhana dan sedikit lebih efisien.

(3) Terakhir, Anda memulihkan Canvas ke Origin aslinya.

Berikut algoritme yang akan Anda terapkan:

  1. Di onDraw(), panggil fungsi untuk mengisi Canvas dengan warna latar belakang abu-abu dan menggambar bentuk asli.
  2. Panggil fungsi untuk setiap persegi panjang yang terpotong dan teks yang akan digambar.

Untuk setiap persegi panjang atau teks:

  1. Simpan status Canvas saat ini sehingga Anda dapat mereset ke status awal tersebut.
  2. Terjemahkan Origin kanvas ke lokasi tempat Anda ingin menggambar.
  3. Menerapkan bentuk kliping dan jalur.
  4. Gambar persegi panjang atau teks.
  5. Pulihkan status Canvas.

Langkah: Mengganti onDraw()

  1. Ganti onDraw() seperti yang ditunjukkan pada kode di bawah. Anda memanggil fungsi untuk setiap bentuk yang Anda gambar, yang nantinya akan diterapkan.
 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)
    }
  1. Buat stub untuk setiap fungsi gambar sehingga kode akan terus dikompilasi. Anda dapat menyalin kode di bawah ini.
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){
}

Aplikasi menggambar persegi panjang dan bentuk yang sama tujuh kali, pertama tanpa pemangkasan, lalu enam kali dengan berbagai jalur pemotongan yang diterapkan. Metode drawClippedRectangle() mempertimbangkan kode untuk menggambar satu persegi panjang, seperti yang ditunjukkan di bawah ini.

Langkah 1: Buat metode drawClippedRectangle()

  1. Buat metode drawClippedRectangle() yang menggunakan argumen canvas dari jenis Canvas.
private fun drawClippedRectangle(canvas: Canvas) {
}
  1. Di dalam metode drawClippedRectangle(), setel batas persegi panjang klip untuk seluruh bentuk. Menerapkan persegi panjang penyesuaian yang membatasi gambar persegi saja.
canvas.clipRect(
       clipRectLeft,clipRectTop,
       clipRectRight,clipRectBottom
)

Metode Canvas.clipRect(...) mengurangi area layar tempat operasi gambar berikutnya dapat menulis. Ini menetapkan batas kliping menjadi persimpangan spasial dari kotak kliping saat ini dan persegi panjang yang diteruskan ke clipRect(). Ada banyak varian dari metode clipRect() yang menerima formulir berbeda untuk region dan memungkinkan operasi yang berbeda pada kotak kliping.

  1. Isi canvas dengan warna putih. Ya. Seluruh kanvas, karena Anda tidak menggambar persegi panjang, Anda akan memotong! Karena kotak kliping, hanya wilayah yang ditentukan oleh persegi panjang pemangkasan yang diisi, yang membuat persegi panjang putih. Sisa permukaan tetap berwarna abu-abu.
canvas.drawColor(Color.WHITE)
  1. Ubah warna menjadi merah dan gambar garis diagonal di dalam persegi panjang kliping.
paint.color = Color.RED
canvas.drawLine(
   clipRectLeft,clipRectTop,
   clipRectRight,clipRectBottom,paint
)
  1. Atur warna menjadi hijau dan gambar lingkaran di dalam kotak kliping.
paint.color = Color.GREEN
canvas.drawCircle(
   circleRadius,clipRectBottom - circleRadius,
   circleRadius,paint
)
  1. Setel warna menjadi biru dan gambar teks sejajar dengan tepi kanan persegi panjang kliping. Gunakan canvas.drawText() untuk menggambar teks.
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
)

Langkah 2: Mengimplementasikan metode drawBackAndUnclippedRectangle()

  1. Untuk melihat cara kerja metode drawClippedRectangle(), gambar persegi panjang tanpa penjepit pertama dengan mengimplementasikan metode drawBackAndUnclippedRectangle() seperti yang ditunjukkan di bawah ini. Simpan canvas, terjemahkan ke posisi baris dan kolom pertama, gambar dengan memanggil drawClippedRectangle(), lalu pulihkan canvas ke status sebelumnya.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
   canvas.drawColor(Color.GRAY)
   canvas.save()
   canvas.translate(columnOne,rowOne)
   drawClippedRectangle(canvas)
   canvas.restore()
}
  1. Jalankan aplikasi. Anda akan melihat persegi panjang putih pertama dengan lingkaran, garis merah, dan teks dengan latar belakang abu-abu.

Dalam metode contoh clipping berikut, Anda menerapkan berbagai kombinasi area penyesuaian untuk mencapai efek grafis dan mempelajari cara menggabungkan area klip untuk membuat bentuk apa pun yang diperlukan.

Setiap metode ini mengikuti pola yang sama.

  1. Menyimpan status kanvas saat ini: canvas.save()

Konteks aktivitas mempertahankan tumpukan status gambar. Status gambar terdiri dari matriks transformasi saat ini dan area kliping saat ini. Anda dapat menyimpan status saat ini, melakukan tindakan yang mengubah status gambar (seperti menerjemahkan atau memutar kanvas), lalu memulihkan status gambar yang tersimpan. (Catatan: Ini seperti perintah "stash" di git!).

Jika gambar Anda mencakup transformasi, rantai dan mengurungkan transformasi dengan membalikkannya akan rentan terhadap error. Misalnya, jika Anda menerjemahkan, meregangkan, lalu memutarnya, prosesnya akan rumit. Sebagai gantinya, simpan status kanvas, terapkan transformasi, gambar, lalu pulihkan status sebelumnya.

Misalnya, Anda dapat menentukan wilayah kliping, dan menyimpan status tersebut. Kemudian terjemahkan kanvas, tambahkan area klip, dan putar. Setelah selesai menggambar, Anda dapat memulihkan status pemangkasan asli, dan Anda dapat melanjutkan untuk melakukan transformasi terjemahan dan kemiringan yang berbeda, seperti yang ditunjukkan pada diagram.

  1. Terjemahkan asal kanvas ke koordinat baris/kolom: canvas.translate()

Jauh lebih mudah untuk memindahkan asal kanvas dan menggambar hal yang sama dalam sistem koordinat baru daripada memindahkan semua elemen untuk digambar. (Tips: Anda dapat menggunakan teknik yang sama untuk memutar elemen.)

  1. Terapkan transformasi ke path, jika ada.
  2. Terapkan penyesuaian: canvas.clipPath(path)
  3. Gambar bentuk: drawClippedRectangle() or drawText()
  4. Memulihkan status kanvas sebelumnya: canvas.restore()

Langkah 1: Mengimplementasikan drawDifferenceClippingExample(canvas)

Tambahkan kode untuk menggambar persegi panjang kedua, yang menggunakan perbedaan antara dua persegi panjang pemangkasan untuk membuat efek bingkai gambar.

Gunakan kode di bawah ini yang akan melakukan hal berikut:

  1. Simpan foto kanvas.
  2. Terjemahkan asal kanvas ke ruang terbuka di baris pertama, kolom kedua, ke kanan persegi panjang pertama.
  3. Terapkan dua persegi panjang pemangkasan. Operator DIFFERENCE mengurangi persegi panjang kedua dari yang pertama.
  1. Panggil metode drawClippedRectangle() untuk menggambar kanvas yang dimodifikasi.
  2. Memulihkan status kanvas.
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()
}
  1. Jalankan aplikasi dan seharusnya akan terlihat seperti ini.

Langkah 2: Terapkan drawCircularClippingExample(kanvas)

Selanjutnya, tambahkan kode untuk menggambar persegi panjang yang menggunakan area kliping melingkar yang dibuat dari jalur melingkar, yang pada dasarnya membuang (tidak menggambar) lingkaran dan dengan demikian menampilkan latar belakang abu-abu sebagai gantinya.

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

Langkah 3: Terapkan drawIntersectionClippingExample(canvas)

Berikutnya, tambahkan kode untuk menggambar persimpangan dua persegi panjang yang terpotong di baris dan kolom kedua.

Perhatikan bahwa bergantung pada resolusi layar Anda, tampilan wilayah ini akan bervariasi. Lakukan eksperimen dengan dimensi smallRectOffset untuk mengubah ukuran wilayah yang terlihat. smallRectOffset yang lebih kecil menghasilkan wilayah yang lebih besar di layar.

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

Langkah 4: Implementasikan drawCombinedClippingExample(canvas)

Selanjutnya, gabungkan bentuk, lingkaran, dan persegi panjang, lalu gambar jalur apa pun untuk menentukan area kliping.

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

Langkah 5: Terapkan drawRoundedRectangleClippingExample(kanvas)

Selanjutnya, tambahkan persegi panjang bersudut bulat yang biasanya digunakan sebagai klip.

  1. Di tingkat atas, buat dan inisialisasi variabel persegi panjang. RectF adalah class yang menyimpan koordinat persegi panjang di floating point.
private var rectF = RectF(
   rectInset,
   rectInset,
   clipRectRight - rectInset,
   clipRectBottom - rectInset
)
  1. Implementasikan fungsi drawRoundedRectangleClippingExample(). Fungsi addRoundRect() mengambil persegi panjang, nilai untuk nilai x dan y radius sudut, dan arah untuk memutar kontur persegi panjang. Path.Direction menentukan cara bentuk tertutup (misalnya, persegi panjang, oval) diorientasikan saat ditambahkan ke jalur. CCW adalah singkatan dari berlawanan arah jarum jam.
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()
}

Langkah 6: Implementasikan drawOutsideClippingExample(kanvas)

Jepit bagian luar di sekitar persegi panjang dengan menggandakan inset persegi panjang pemotongan.

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

Langkah 7: Terapkan drawTranslatedTextExample(canvas)

Menggambar teks tidak jauh berbeda dengan bentuk lainnya, dan Anda dapat menerapkan transformasi ke teks. Misalnya, Anda dapat menerjemahkan teks dengan menerjemahkan kanvas dan menggambar teks.

  1. Implementasikan fungsi di bawah.
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()
}
  1. Jalankan aplikasi untuk melihat teks terjemahan.

Langkah 8: Terapkan drawSkewedTextExample(canvas)

Anda juga dapat mendistorsi teks. Yaitu, mendistorsinya dengan berbagai cara.

  1. Buat fungsi di bawah di 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()
}
  1. Jalankan aplikasi untuk melihat teks miring yang digambar sebelum teks yang diterjemahkan.

Metode quickReject() Canvas memungkinkan Anda memeriksa apakah persegi panjang atau jalur yang ditentukan akan berada sepenuhnya di luar wilayah yang saat ini terlihat, setelah semua transformasi diterapkan.

Metode quickReject() sangat berguna saat Anda membuat gambar yang lebih kompleks dan harus melakukannya secepat mungkin. Dengan quickReject(), Anda dapat memutuskan secara efisien objek mana yang tidak perlu Anda gambar sama sekali, dan tidak perlu menulis logika persimpangan sendiri.

  • Metode quickReject() menampilkan true jika persegi panjang atau jalur tidak terlihat sama sekali di layar. Untuk tumpang-tindih sebagian, Anda masih harus melakukan pemeriksaan sendiri.
  • EdgeType adalah AA (Antialiased: Perlakukan tepi dengan membulatkan, karena mungkin antialiased) atau BW (Black-White: Perlakukan tepi hanya dengan membulatkan ke batas piksel terdekat) untuk hanya membulatkan ke piksel terdekat.

Ada beberapa versi quickReject(), dan Anda juga dapat menemukannya di dokumentasi.

boolean

quickTolak(float left, float top, float right, float bottom, Canvas.EdgeType type)

boolean

quickTolak(RectF rect, Canvas.EdgeType type)

boolean

quickTolak(Jalur path, Canvas.EdgeType type)

Dalam latihan ini, Anda akan menggambar dalam baris baru, di bawah teks, dan di dalam clipRect, seperti sebelumnya.

  • Pertama, Anda memanggil quickReject() dengan inClipRectangle persegi panjang, yang tumpang tindih dengan clipRect. Jadi, quickReject() akan menampilkan false, clipRect akan diisi dengan BLACK, dan persegi panjang inClipRectangle akan digambar.

  • Kemudian, ubah kode dan panggil quickReject(), dengan notInClipRectangle. quickReject() sekarang menampilkan nilai benar, dan clipRect diisi dengan WHITE, dan notInClipRectangle tidak digambar.

Bila Anda memiliki gambar yang rumit, gambar ini dapat dengan cepat memberi tahu Anda, bentuk mana yang sepenuhnya berada di luar area kliping, dan yang mungkin harus Anda lakukan penghitungan tambahan, serta gambar, karena gambar tersebut berada sebagian atau seluruhnya di dalam area kliping.

Langkah: Bereksperimen dengan quickTolak()

  1. Di tingkat teratas, buat variabel untuk koordinat y baris tambahan.
   private val rejectRow = rowFour + rectInset + 2*clipRectBottom
  1. Tambahkan fungsi drawQuickRejectExample() berikut ke ClippedView. Baca kode karena berisi semua yang perlu Anda ketahui untuk menggunakan quickReject().
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()
}
  1. Di onDraw(), batalkan komentar pemanggilan drawQuickRejectExample().
  2. Jalankan aplikasi, dan Anda akan melihat persegi panjang hitam, yang merupakan area kliping yang terisi, dan bagian dari inClipRectangle, karena kedua persegi panjang tumpang tindih, sehingga quickReject() menampilkan false dan inClipRectangle digambar.

  1. Di drawQuickRejectExample(), ubah kode untuk menjalankan quickReject() terhadap notInClipRectangle.Now quickReject() menampilkan true dan area kliping diisi dengan warna putih.

Mendownload kode untuk codelab yang sudah selesai.

$  git clone https://github.com/googlecodelabs/android-kotlin-drawing-clipping


Atau, Anda dapat mendownload repositori sebagai file Zip, mengekstraknya, dan membukanya di Android Studio.

Download Zip

  • Context aktivitas mempertahankan status yang mempertahankan transformasi dan area klip untuk Canvas.
  • Gunakan canvas.save() dan canvas.restore() untuk menggambar dan kembali ke status asli kanvas Anda.
  • Untuk menggambar beberapa bentuk pada kanvas, Anda dapat menghitung lokasinya, atau Anda dapat memindahkan (menerjemahkan) asal permukaan gambar Anda. Parameter yang terakhir ini dapat mempermudah pembuatan metode utilitas untuk urutan gambar berulang.
  • Clipping Region dapat berupa bentuk, kombinasi bentuk, atau jalur.
  • Anda dapat menambahkan, mengurangi, dan memotong area pemangkasan untuk mendapatkan wilayah yang benar-benar dibutuhkan.
  • Anda dapat menerapkan transformasi pada teks dengan mengubah kanvas.
  • Metode quickReject() Canvas memungkinkan Anda memeriksa apakah kotak atau jalur yang ditentukan akan berada sepenuhnya di luar wilayah yang saat ini terlihat.

Kursus Udacity:

Dokumentasi developer Android:

Lihat juga seri artikel Arsitektur Grafis untuk penjelasan mendalam tentang bagaimana framework Android menggambar di layar.

Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Terserah instruktur untuk melakukan hal berikut:

  • Tugaskan pekerjaan rumah jika diperlukan.
  • Berkomunikasi dengan siswa cara mengirimkan tugas pekerjaan rumah.
  • Beri nilai tugas pekerjaan rumah.

Instruktur dapat menggunakan saran ini sesedikit atau sebanyak yang mereka inginkan, dan harus bebas memberikan pekerjaan rumah lain yang dirasa sesuai.

Jika Anda mengerjakan codelab ini sendiri, silakan gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Jawab pertanyaan berikut

Pertanyaan 1

Metode apa yang Anda panggil untuk secara efisien mengecualikan bentuk agar tidak digambar?

excludeFromDrawing()

quickReject()

onDraw()

clipRect()

Pertanyaan 2

Canvas.save() dan Canvas.restore() menyimpan serta memulihkan informasi?

▢ Warna, lebar garis, dll.

▢ Hanya transformasi saat ini

▢ Area transformasi dan kliping saat ini

▢ Hanya area kliping saat ini

Pertanyaan 3

Paint.Align menentukan:

▢ Cara menyelaraskan bentuk gambar berikut

▢ Dari mana asal asal teks

▢ Tempat penyesuaiannya di area klip

▢ Sisi teks mana yang akan diratakan ke asal

Untuk link ke codelab lainnya dalam kursus ini, lihat halaman landing codelab Android Lanjutan di Kotlin.