Paint เป็นคลาสหลักสำหรับการจัดสไตล์กราฟิกใน Android Canvas โดยกำหนดสี ความหนาของเส้น สไตล์การเติม เอฟเฟกต์เบลอ และฟอนต์สำหรับการวาดทั้งหมดในส่วนติดต่อผู้ใช้ หากไม่มี Paint แล้ว Canvas จะไม่สามารถใช้คุณลักษณะทางภาพกับองค์ประกอบที่แสดงได้ ตามข้อมูลจาก Google Developers, 2026 Paint รองรับพารามิเตอร์สไตล์สูงสุด 42 รายการ รวมถึงเกรเดียนต์และพื้นผิว
ประเด็นสำคัญ
Paint เป็นคลาสจากแพ็กเกจ android.graphics ที่เก็บการตั้งค่าสไตล์ทั้งหมดสำหรับการวาดบน Canvas โดยไม่มีพิกเซลในตัวเอง แต่กำหนดว่าเส้น รูปร่าง และข้อความจะมีลักษณะอย่างไร ทุกการเรียกเมธอด Canvas — drawCircle, drawLine, drawText — ใช้ Paint เพื่อตีความการแสดงผลทางภาพ
Paint ทำงานบนหลักการของออบเจกต์การกำหนดค่า คุณสร้างอินสแตนซ์ กำหนดค่าพารามิเตอร์ครั้งเดียว และนำกลับมาใช้ใหม่สำหรับหลายการดำเนินการ ซึ่งช่วยลดการจัดสรรในลูปการวาดและเพิ่มประสิทธิภาพสูงถึง 30% เมื่อเทียบกับการสร้างออบเจกต์ใหม่ทุกเฟรม ตามข้อมูลจาก Google I/O 2024 การปรับแต่ง Paint เป็นหนึ่งในแนวทางหลักในการบรรลุ 60 FPS ในกราฟิกแบบกำหนดเอง
การตั้งค่า Paint ทั้งหมดแบ่งออกเป็นหลายหมวดหมู่: สีและความโปร่งใส สไตล์เส้นและการเติม การจัดการข้อความและฟอนต์ เอฟเฟกต์เบลอและการซ่อน แต่ละหมวดหมู่ถูกควบคุมโดยเมธอด setter อิสระที่สามารถรวมกันได้ในทุกการผสมผสาน ทำให้ Paint API มีความยืดหยุ่น แต่ต้องเข้าใจปฏิสัมพันธ์ของพารามิเตอร์ — ตัวอย่างเช่น Style ส่งผลต่อว่า Shader ถูกนำไปใช้กับเส้นหรือการเติม
setColor เป็นเมธอดหลักในการกำหนดสีสำหรับการวาดที่ตามมาทั้งหมด โดยส่งสีในรูปแบบ ARGB (จำนวนเต็ม 32 บิต) โดยที่ 8 บิตสูงคือช่องอัลฟา และส่วนที่เหลือคือช่องสีแดง สีเขียว และสีน้ำเงิน ค่า 0xFF0000FF กำหนดสีน้ำเงินทึบ
เมธอด setAlpha อนุญาตให้ปรับความโปร่งใสอย่างอิสระในช่วงตั้งแต่ 0 (โปร่งใสทั้งหมด) ถึง 255 (ทึบแสงทั้งหมด) ซึ่งแตกต่างจากการระบุอัลฟาใน setColor เมธอดนี้ไม่มีผลต่อโทนสีที่ตั้งไว้แล้ว — เพียงปรับความโปร่งใสโดยรวมเท่านั้น สะดวกสำหรับการทำอนิเมชันการปรากฏและหายไปขององค์ประกอบโดยไม่เปลี่ยนสี
ColorFilter เป็นกลไกในการแปลงสีของแต่ละพิกเซลก่อนการเรนเดอร์ รองรับสามโหมด: LightingColorFilter สำหรับการคูณและบวกช่องสัญญาณ, PorterDuffColorFilter สำหรับการซ้อนทับโดยใช้โหมดผสม และ ColorMatrixColorFilter สำหรับการแปลงเมทริกซ์แบบเต็ม ColorFilter ถูกใช้หลังจาก Shader แต่ก่อนส่งออกไปยัง Canvas
สำหรับงานส่วนใหญ่ setColor หรือ setAlpha มาตรฐานก็เพียงพอแล้ว ColorFilter ใช้เป็นหลักสำหรับเอฟเฟกต์ลดความอิ่มตัว สีซีเปีย หรือการกลับสี การใช้ ColorFilter เพิ่มภาระให้กับ GPU ดังนั้น Google แนะนำให้ลดการใช้งานในการเรียกซ้ำไปยัง onDraw
| เมธอด | คำอธิบาย | ประสิทธิภาพ |
|---|---|---|
| setColor | กำหนดสีในรูปแบบ ARGB | O(1) — ไม่มีโอเวอร์เฮด |
| setAlpha | ปรับความโปร่งใส (0-255) | O(1) — การทำงานที่เบา |
| setColorFilter | ใช้การแก้สี | O(n) — ส่งผลต่อ FPS เมื่อเรียกบ่อย |
เมธอด setStyle รับค่า Paint.Style หนึ่งในสามค่า: FILL (เติม), STROKE (โครงร่าง) หรือ FILL_AND_STROKE (ทั้งสองอย่าง) การเลือกสไตล์จะกำหนดว่ารูปร่างจะแสดงเป็นภาพเงา โครงสร้างเส้นลวด หรือโครงร่างที่มีการเติมภายใน โดยค่าเริ่มต้น Paint ถูกสร้างด้วยสไตล์ FILL
setStrokeWidth กำหนดความหนาของเส้นเป็นพิกเซลสำหรับโหมด STROKE สำหรับรูปร่างโค้งมนและมุมโค้ง ใช้ setStrokeCap (Cap.ROUND, BUTT, SQUARE) และ setStrokeJoin (Join.MITER, ROUND, BEVEL) พารามิเตอร์เหล่านี้มีผลสำคัญต่อลักษณะของกราฟ เส้นทาง และขอบใน View แบบกำหนดเอง
ที่ความหนาน้อยกว่า 1 พิกเซล เส้นจะถูกวาดด้วยความแม่นยำระดับย่อยพิกเซลแต่อาจดูเบลอบนจอความละเอียดต่ำ สำหรับจอ Retina ของ Android ให้ใช้ TypedValue.applyDimension เพื่อแปลง dp เป็นพิกเซล ความหนา 2 dp ให้ความชัดเจนบนอุปกรณ์ส่วนใหญ่
DashPathEffect สร้างเส้นประและเส้นประจุด คอนสตรัคเตอร์รับอาร์เรย์ของความยาวเส้นประและช่องว่างสลับกัน และค่าชดเชยเฟสเริ่มต้น ตัวอย่างเช่น อาร์เรย์ [10, 5, 3, 5] สร้างเส้นที่มีเส้นประยาว ช่องว่างสั้น เส้นประสั้น และช่องว่าง เอฟเฟกต์นี้ถูกใช้อย่างแข็งขันในโปรแกรมแก้ไขกราฟิกและเครื่องมือคำอธิบายประกอบ
การรวมสไตล์ทำผ่าน setPathEffect โดยการรวม CornerPathEffect, DashPathEffect และ DiscretePathEffect คุณสามารถสร้างเส้นภาพที่ซับซ้อนสำหรับแผนภูมิ ป้ายกำกับ และองค์ประกอบตกแต่ง แต่ละ PathEffect คำนวณบน CPU ดังนั้นในอนิเมชันที่มีหลายเส้น แนะนำให้แรสเตอร์เอฟเฟกต์ล่วงหน้าเป็น Bitmap
val paint = Paint().apply {
setStyle(Paint.Style.STROKE)
setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics))
setPathEffect(DashPathEffect(floatArrayOf(10f, 5f, 3f, 5f), 0f))
}
Typeface เป็นออบเจกต์ที่กำหนดฟอนต์สำหรับการดำเนินการข้อความทั้งหมดของ Paint Android รองรับฟอนต์ระบบ (Roboto, Noto Sans) ฟอนต์ที่กำหนดเองจากทรัพยากร assets และฟอนต์ที่ดาวน์โหลดได้ผ่าน Downloadable Fonts API Typeface ถูกตั้งค่าโดยใช้ setTypeface บนอินสแตนซ์ Paint
เมธอด setTextSize กำหนดขนาดฟอนต์เป็นพิกเซล สำหรับการแสดงผลที่ถูกต้องบนหน้าจอต่างๆ แนะนำให้ระบุขนาดเป็น sp โดยใช้ TypedValue.applyDimension ระยะห่างระหว่างบรรทัดถูกปรับโดยใช้ setTextScaleX สำหรับการยืดแนวนอน และรองรับโดยข้อความหลายบรรทัดในตัวผ่าน Canvas.drawText
สำหรับการจัดแนวข้อความ ใช้ setTextAlign ด้วยค่า LEFT, CENTER และ RIGHT เมื่อใช้ CENTER พิกัด x ที่ส่งไปยัง drawText จะกลายเป็นจุดศูนย์กลางของข้อความ — สะดวกเป็นพิเศษสำหรับป้ายกำกับใต้ไอคอนและหัวข้อภายในรูปร่าง สำหรับการวางตำแหน่งที่แม่นยำ ใช้เมธอด measureText และ getTextBounds ซึ่งคืนค่าความกว้างและสี่เหลี่ยมขอบเขตของข้อความ
setUnderlineText และ setStrikeThruText เปิดใช้งานการขีดเส้นใต้และการขีดทับตามลำดับ พารามิเตอร์เหล่านี้ทำงานในระดับฟอนต์และใช้กับข้อความทั้งหมดที่วาดโดย Paint นี้ สำหรับการจัดรูปแบบที่ยืดหยุ่นของส่วนต่างๆ ของสตริงเดียวกัน ให้ใช้ออบเจกต์ Paint แยกกันกับการตั้งค่าต่างๆ หรือสลับพารามิเตอร์ระหว่างการเรียก drawText
| เมธอด | วัตถุประสงค์ | หน่วย |
|---|---|---|
| setTextSize | ขนาดฟอนต์ | px (พิกเซล) |
| setTextAlign | การจัดแนวแนวนอน | LEFT, CENTER, RIGHT |
| setTypeface | ฟอนต์ | ออบเจกต์ Typeface |
| setUnderlineText | ขีดเส้นใต้ | Boolean |
Shader เป็นคลาสพื้นฐานสำหรับการเติมรูปร่างด้วยเกรเดียนต์ พื้นผิวบิตแมป และการประกอบ ผ่านเมธอด setShader คุณสามารถกำหนดคลาสย่อยใดๆ ได้: LinearGradient, RadialGradient, SweepGradient, BitmapShader หรือ ComposeShader Shader แทนที่สีที่กำหนดโดย setColor และใช้กับองค์ประกอบที่วาดทั้งหมด
LinearGradient สร้างการเปลี่ยนสีอย่างราบรื่นระหว่างสีสองสีขึ้นไปตามเส้นตรง คอนสตรัคเตอร์รับจุดเริ่มต้นและจุดสิ้นสุด อาร์เรย์ของสีและตำแหน่ง (โหมดไทล์) โหมดไทล์กำหนดพฤติกรรมเกินกว่าเกรเดียนต์: CLAMP (ทำซ้ำสีสุดท้าย), MIRROR (สะท้อน) หรือ REPEAT (ทำซ้ำ) LinearGradient ถูกใช้อย่างแข็งขันสำหรับการเติมพื้นหลังและแถบความคืบหน้า
RadialGradient สร้างเกรเดียนต์วงกลมจากจุดศูนย์กลางไปยังขอบ จุดศูนย์กลางถูกกำหนดโดยพิกัด รัศมีกำหนดพื้นที่การเปลี่ยนสี ลักษณะของเกรเดียนต์รัศมีคล้ายกับแสงจากแหล่งกำเนิดจุด — มักใช้เพื่อจำลองความเรืองแสงของปุ่มและเน้นองค์ประกอบที่ทำงานอยู่
BlurMaskFilter เพิ่มเอฟเฟกต์เบลอรอบองค์ประกอบที่วาด คอนสตรัคเตอร์รับรัศมีเบลอและสไตล์: NORMAL (เบลอทุกทิศทาง), INNER (เฉพาะภายในรูปร่าง), OUTER (เฉพาะภายนอก) และ SOLID (รูปร่าง + เบลอภายนอก) BlurMaskFilter ทำงานกับ Paint ทุกประเภทและมักใช้สร้างเงาและเอฟเฟกต์เรืองแสง
val paint = Paint().apply {
setShader(LinearGradient(
0f, 0f, width, height,
intArrayOf(Color.RED, Color.BLUE),
null,
Shader.TileMode.CLAMP
))
setMaskFilter(BlurMaskFilter(20f, BlurMaskFilter.Blur.NORMAL))
}
ลองพิจารณาตัวอย่างสมบูรณ์ของ View แบบกำหนดเองที่วาดวงกลมด้วยการเติมเกรเดียนต์และป้ายข้อความ การรวมกันของ Paint และ Canvas ช่วยให้สร้างกราฟิกที่ซับซ้อนด้วยโค้ดน้อยที่สุด การตั้งค่าทั้งหมดถูกย้ายไปยังการเริ่มต้นเพื่อประสิทธิภาพ
class GradientCircleView@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val fillPaint = Paint().apply {
setShader(RadialGradient(
0f, 0f, 200f,
intArrayOf(Color.CYAN, Color.DKGRAY),
null, Shader.TileMode.CLAMP
))
}
private val textPaint = Paint().apply {
setColor(Color.WHITE)
setTextSize(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 24f, resources.displayMetrics
))
setTextAlign(Paint.Align.CENTER)
setMaskFilter(BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL))
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(200f, 200f, 150f, fillPaint)
canvas.drawText("เกรเดียนต์", 200f, 210f, textPaint)
}
}
โค้ดใช้ออบเจกต์ Paint สองตัวแยกกัน: หนึ่งสำหรับเติมวงกลมด้วยเกรเดียนต์รัศมี และตัวที่สองสำหรับข้อความที่มีเงาผ่าน BlurMaskFilter การแยกนี้ อนุญาตให้กำหนดค่าพารามิเตอร์อย่างอิสระและหลีกเลี่ยงการรีเซ็ตสถานะ Paint ระหว่างการวาดองค์ประกอบประเภทต่างๆ เพื่อปรับปรุงประสิทธิภาพ ออบเจกต์ Paint ทั้งสองถูกสร้างในคอนสตรัคเตอร์ ไม่ใช่ใน onDraw
เมื่อปรับขนาดบนอุปกรณ์ที่มีความหนาแน่นพิกเซลต่างกัน ค่าขนาดควรคำนวณใหม่โดยใช้ TypedValue.applyDimension หากไม่ทำเช่นนั้น ข้อความและเกรเดียนต์จะดูแตกต่างกันบนหน้าจอ mdpi และ xxxhdpi ในตัวอย่าง ขนาดข้อความถูกระบุเป็น sp ซึ่งปรับให้เข้ากับการตั้งค่าขนาดฟอนต์ระบบของผู้ใช้โดยอัตโนมัติ
คำถามที่พบบ่อย
Canvas ดำเนินการวาด (drawCircle, drawLine) ในขณะที่ Paint กำหนดสไตล์ — สี ความหนา ฟอนต์ Canvas ไม่มีการตั้งค่าสไตล์ ดังนั้นหากไม่มี Paint การวาดใดๆ ก็ไม่มีผลทางภาพ
ได้ ออบเจกต์ Paint เดียวสามารถนำกลับมาใช้ใหม่สำหรับหลายรูปร่าง แนะนำให้สร้างออบเจกต์ Paint แยกต่างหากสำหรับองค์ประกอบประเภทต่างๆ (พื้นหลัง ข้อความ โครงร่าง) แต่ไม่ใช่สำหรับทุกรูปร่าง — ซึ่งช่วยลดภาระของตัวเก็บขยะ
เมธอด reset ทำให้ Paint กลับสู่สถานะเทียบเท่ากับอินสแตนซ์ใหม่ หรือใช้ set กับแฟล็กได้ แต่ reset เหมาะกับการอ่านโค้ดมากกว่า หลังรีเซ็ต พารามิเตอร์สไตล์ทั้งหมดใช้ค่าเริ่มต้น
การสร้าง Paint ภายใน onDraw ทำให้เกิดการจัดสรรทุกเฟรม นำไปสู่การค้างของ GC ควรสร้างออบเจกต์ Paint ทั้งหมดในคอนสตรัคเตอร์ของ View การเปลี่ยนพารามิเตอร์ใน onDraw ผ่าน setter ปลอดภัยและไม่สร้างออบเจกต์ใหม่
ข้อความใน Android มักถูกวาดด้วยพื้นหลังโปร่งใส เมธอด drawText จะแสดงเฉพาะกลิฟฟ์ โดยไม่มีสี่เหลี่ยมพื้นหลัง หากต้องการพื้นหลังใต้ข้อความ ให้วาดสี่เหลี่ยมก่อนโดยใช้ drawRoundRect กับ Paint แยกต่างหาก จากนั้นจึงวาดข้อความ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม