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 menggantionDraw()
danonSizeChanged()
.
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
- Buat project Kotlin bernama
ClippingExample
dengan template Empty Activity. Gunakancom.example.android
untuk awalan nama paket. - Buka
MainActivity.kt
. - Pada metode
onCreate()
, ganti tampilan konten default dan tetapkan tampilan konten ke instanceClippedView
yang baru. Ini akan menjadi tampilan khusus Anda untuk contoh klip yang akan Anda buat berikutnya.
setContentView(ClippedView(this))
- Di tingkat yang sama dengan
MainActivity.kt
, buat file dan class Kotlin baru untuk tampilan kustom bernamaClippedView
yang memperluasView
. Berikan tanda tangan yang ditampilkan di bawah ini. Semua pekerjaan Anda lainnya akan berada di dalamClippedView
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
- 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.
- Di Android Studio, klik kanan folder values dan pilih New > Values resource file.
- 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.
- File akan ditampilkan di folder nilai seperti yang ditunjukkan di bawah ini.
- 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
.
- 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>
- 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
- Beralih kembali ke tampilan Android project Anda.
- Di
ClippedView
, tentukan variabelPaint
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)
}
- Di
ClippedView
, buat dan inisialisasiPath
untuk menyimpan secara lokal jalur yang telah digambar. Imporandroid.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.
- Pada
ClippedView
, di bawahpath
, 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)
- 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)
- Tambahkan variabel untuk radius lingkaran. Ini adalah jari-jari lingkaran yang digambar di dalam persegi panjang.
private val circleRadius = resources.getDimension(R.dimen.circleRadius)
- 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.
- Siapkan koordinat untuk dua kolom.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight
- 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)
- 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:
- Di
onDraw()
, panggil fungsi untuk mengisiCanvas
dengan warna latar belakang abu-abu dan menggambar bentuk asli. - Panggil fungsi untuk setiap persegi panjang yang terpotong dan teks yang akan digambar.
Untuk setiap persegi panjang atau teks:
- Simpan status
Canvas
saat ini sehingga Anda dapat mereset ke status awal tersebut. - Terjemahkan
Origin
kanvas ke lokasi tempat Anda ingin menggambar. - Menerapkan bentuk kliping dan jalur.
- Gambar persegi panjang atau teks.
- Pulihkan status
Canvas
.
Langkah: Mengganti onDraw()
- 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)
}
- 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()
- Buat metode
drawClippedRectangle()
yang menggunakan argumencanvas
dari jenisCanvas
.
private fun drawClippedRectangle(canvas: Canvas) {
}
- 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.
- 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)
- Ubah warna menjadi merah dan gambar garis diagonal di dalam persegi panjang kliping.
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)
- Atur warna menjadi hijau dan gambar lingkaran di dalam kotak kliping.
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)
- 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()
- Untuk melihat cara kerja metode
drawClippedRectangle()
, gambar persegi panjang tanpa penjepit pertama dengan mengimplementasikan metodedrawBackAndUnclippedRectangle()
seperti yang ditunjukkan di bawah ini. Simpancanvas
, terjemahkan ke posisi baris dan kolom pertama, gambar dengan memanggildrawClippedRectangle()
, lalu pulihkancanvas
ke status sebelumnya.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
canvas.drawColor(Color.GRAY)
canvas.save()
canvas.translate(columnOne,rowOne)
drawClippedRectangle(canvas)
canvas.restore()
}
- 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.
- 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.
- 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.)
- Terapkan transformasi ke
path
, jika ada. - Terapkan penyesuaian:
canvas.clipPath(path)
- Gambar bentuk:
drawClippedRectangle() or drawText()
- 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:
- Simpan foto kanvas.
- Terjemahkan asal kanvas ke ruang terbuka di baris pertama, kolom kedua, ke kanan persegi panjang pertama.
- Terapkan dua persegi panjang pemangkasan. Operator
DIFFERENCE
mengurangi persegi panjang kedua dari yang pertama.
- Panggil metode
drawClippedRectangle()
untuk menggambar kanvas yang dimodifikasi. - 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()
}
- 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.
- 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
)
- Implementasikan fungsi
drawRoundedRectangleClippingExample()
. FungsiaddRoundRect()
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.
- 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()
}
- Jalankan aplikasi untuk melihat teks terjemahan.
Langkah 8: Terapkan drawSkewedTextExample(canvas)
Anda juga dapat mendistorsi teks. Yaitu, mendistorsinya dengan berbagai cara.
- 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()
}
- 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()
menampilkantrue
jika persegi panjang atau jalur tidak terlihat sama sekali di layar. Untuk tumpang-tindih sebagian, Anda masih harus melakukan pemeriksaan sendiri. EdgeType
adalahAA
(Antialiased: Perlakukan tepi dengan membulatkan, karena mungkin antialiased) atauBW
(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.
| quickTolak |
| quickTolak |
| quickTolak |
Dalam latihan ini, Anda akan menggambar dalam baris baru, di bawah teks, dan di dalam clipRect
, seperti sebelumnya.
- Pertama, Anda memanggil
quickReject()
denganinClipRectangle
persegi panjang, yang tumpang tindih denganclipRect
. Jadi,quickReject()
akan menampilkan false,clipRect
akan diisi denganBLACK
, dan persegi panjanginClipRectangle
akan digambar.
- Kemudian, ubah kode dan panggil
quickReject()
, dengannotInClipRectangle
.quickReject()
sekarang menampilkan nilai benar, danclipRect
diisi denganWHITE
, dannotInClipRectangle
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()
- Di tingkat teratas, buat variabel untuk koordinat y baris tambahan.
private val rejectRow = rowFour + rectInset + 2*clipRectBottom
- Tambahkan fungsi
drawQuickRejectExample()
berikut keClippedView
. Baca kode karena berisi semua yang perlu Anda ketahui untuk menggunakanquickReject()
.
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()
}
- Di
onDraw()
, batalkan komentar pemanggilandrawQuickRejectExample()
. - 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, sehinggaquickReject()
menampilkanfalse
daninClipRectangle
digambar.
- Di
drawQuickRejectExample()
, ubah kode untuk menjalankanquickReject()
terhadapnotInClipRectangle.
NowquickReject()
menampilkantrue
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.
Context
aktivitas mempertahankan status yang mempertahankan transformasi dan area klip untukCanvas
.- Gunakan
canvas.save()
dancanvas.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:
- Class
Canvas
- Class
Bitmap
- Class
View
- Class
Paint
- Konfigurasi
Bitmap.config
- Operator
Region.Op
- Class
Path
- Class
Canvas
- Class
Bitmap
- Class
View
- Class
Paint
- Konfigurasi
Bitmap.config
- Operator
Region.Op
- Class
Path
- Alat grafis
android.graphics
- Konfigurasi
Bitmap.Config
Canvas
- Kanvas dan Drawable
- Apa yang dilakukan canvas.translate() melakukan
- Memahami save() dan restore() untuk konteks Canvas
- clipping
- overdraw.
@JvmOverloads
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.