มุมมองแบบกำหนดเองและการวาดในการพัฒนามือถือ: สาระสำคัญ, API ใดบ้างและวิธีการทำงาน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-08-01 เวลาอ่าน: 11 นาที

มุมมองแบบกำหนดเองเป็นพื้นฐานของอินเทอร์เฟซที่ไม่ซ้ำใครของแอปพลิเคชันมือถือใดๆ มุมมองแบบกำหนดเองในแอปพลิเคชันมือถือช่วยให้คุณสร้างกราฟิก แอนิเมชัน และองค์ประกอบของคุณเองที่ไม่มีในคอมโพเนนต์มาตรฐาน ตาม Apple Developer, 2025 drawRect ยังคงเป็นเมธอดหลักของการวาดแบบกำหนดเองในการพัฒนามือถือ iOS บน Android onDraw กับ Canvas ทำหน้าที่เดียวกัน และใน Flutter — CustomPainter

ประเด็นสำคัญ

  • มุมมองแบบกำหนดเอง — คอมโพเนนต์ที่ผู้ใช้กำหนดพร้อมตรรกะการวาด ขนาด และพฤติกรรมของตัวเองในแอปพลิเคชันมือถือ
  • drawRect — เมธอด UIView บน iOS สำหรับการวาดแบบกำหนดเองผ่าน Core Graphics และ CGContext
  • onDraw — เมธอด View บน Android สำหรับการวาดผ่าน Canvas และ Paint พร้อมระบบการวัด onMeasure
  • CustomPainter — คลาส Flutter สำหรับการวาดบน Canvas ด้วยเมธอด paint และ shouldRepaint
  • การวัด — กระบวนการกำหนดขนาดของมุมมองผ่าน sizeThatFits บน iOS, onMeasure บน Android และ computeDryLayout ใน Flutter

มุมมองแบบกำหนดเองคืออะไรและทำไมจึงจำเป็นในแอปพลิเคชันมือถือ

มุมมองแบบกำหนดเองคือคอมโพเนนต์อินเทอร์เฟซที่ผู้ใช้กำหนดซึ่งแทนที่เมธอดการวาดและการวัดมาตรฐานเพื่อสร้างกราฟิกที่ไม่ซ้ำใคร กราฟ แอนิเมชัน แผนภาพ องค์ประกอบแชทแบบกำหนดเอง และหน้ากาก AR — นี่คือสถานการณ์การใช้งานทั่วไปของมุมมองแบบกำหนดเองในแอปพลิเคชันมือถือ คอมโพเนนต์ UI มาตรฐาน (Button, TextView, Label) ครอบคลุม 80% ของงาน แต่ 20% ที่เหลือต้องการการควบคุมการวาดอย่างสมบูรณ์ในการพัฒนามือถือ การวาดในการพัฒนามือถือเป็นทักษะที่แยกนักพัฒนา junior ออกจาก senior

เมื่อใดที่มุมมองแบบกำหนดเองมีความเหมาะสม

มุมมองแบบกำหนดเองในแอปพลิเคชันมือถือจำเป็นสำหรับ กราฟิก 2D ที่ไม่ซ้ำใคร แอนิเมชันที่ซับซ้อน แผนภูมิ และการแสดงภาพข้อมูล หาก UIKit มาตรฐานไม่มีองค์ประกอบที่ต้องการ — จะสร้างมุมมองแบบกำหนดเอง ในแอปพลิเคชันมือถือ มุมมองแบบกำหนดเองใช้สำหรับแถบความคืบหน้า ปุ่มแบบกำหนดเอง การเติมสีแบบเกรเดียนต์ ไอคอนเคลื่อนไหว และแคนวาสสำหรับวาดด้วยนิ้ว สิ่งสำคัญคือไม่ใช้มากเกินไป — สำหรับสไตล์ง่ายๆ เพียงกำหนดค่าคอมโพเนนต์มาตรฐานผ่าน Appearance API ก็เพียงพอ

มุมมองแบบกำหนดเองบน iOS: drawRect และ Core Graphics

บน iOS มุมมองแบบกำหนดเองถูกสร้างขึ้นโดยการสืบทอด UIView และแทนที่เมธอด drawRect drawRect ถูกเรียกในการเรนเดอร์ครั้งแรกและหลังจากการเรียก setNeedsDisplay() แต่ละครั้ง ภายใน drawRect นักพัฒนาได้รับ CGContext ผ่าน UIGraphicsGetCurrentContext() — แคนวาสสำหรับเส้น รูปร่าง เกรเดียนต์ และข้อความ Core Graphics (Quartz 2D) เป็นเอนจินเรนเดอร์ระดับต่ำบน CPU ที่ให้การควบคุมทุกพิกเซลอย่างสมบูรณ์ UIBezierPath เป็นตัวห่อหุ้มที่สะดวกสำหรับการสร้างเส้นทางด้วยไวยากรณ์ที่กระชับ

swift
class CircleView: UIView {
    private var fillColor: UIColor = .systemBlue

    override func drawRect(_ rect: CGRect) {
        guard let ctx = UIGraphicsGetCurrentContext() else { return }
        ctx.setFillColor(fillColor.cgColor)
        ctx.addEllipse(in: bounds.insetBy(dx: 4, dy: 4))
        ctx.drawPath(using: .fill)
    }

    func updateColor(_ newColor: UIColor) {
        fillColor = newColor
        setNeedsDisplay()
    }
}

Core Graphics: CGContext, CGGradient และการแปลง

Core Graphics ให้ CGContext — แคนวาสสำหรับการดำเนินการวาดทั้งหมด CGGradient สร้างเกรเดียนต์เชิงเส้นและรัศมี ด้วยการสนับสนุน CGColorSpace (sRGB, Display P3) การแปลงดำเนินการผ่าน CGAffineTransform — เมทริกซ์ 3x3 สำหรับการหมุน การปรับขนาด และการเลื่อน CGContextTranslateCTM, CGContextRotateCTM และ CGContextScaleCTM เปลี่ยนแปลงระบบพิกัด Core Graphics ทำงานบน CPU ดังนั้นกราฟิกที่ซับซ้อนสามารถโหลดโปรเซสเซอร์ — ใช้ Metal สำหรับการเร่ง GPU

UIBezierPath สำหรับสร้างรูปร่าง

UIBezierPath เป็นตัวห่อหุ้มเชิงวัตถุเหนือ CGPath เมธอด addArc(withCenter:radius:startAngle:endAngle:clockwise:) สร้างส่วนโค้งและวงกลม addCurve สร้างเส้นโค้งเบซิเอร์ เส้นทางสนับสนุนการดำเนินการรวม การตัด และการลบผ่าน addClip UIBezierPath สะดวกสำหรับมาสก์แบบกำหนดเอง (maskView) เส้นทางตัด และรูปร่างที่ซับซ้อน — ดาว หัวใจ ไอคอนแบบกำหนดเอง

มุมมองแบบกำหนดเองบน Android: onDraw และ Canvas

บน Android มุมมองแบบกำหนดเองขยาย View ด้วยการแทนที่ onDraw(Canvas) Canvas มีเมธอดการวาดทั้งหมดในการพัฒนามือถือ: drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath และ drawRoundRect Paint กำหนดค่าพารามิเตอร์ — สี ความหนาของเส้น สไตล์ (FILL, STROKE, FILL_AND_STROKE) การลดรอยหยัก (isAntiAlias) และขนาดข้อความ onDraw ถูกเรียกทุกครั้งที่ invalidate() และไม่ควรมีการดำเนินการหนัก — การคำนวณทั้งหมดถูกแคช

kotlin
class CustomCircleView(context: Context, attrs: AttributeSet?)
    : View(context, attrs) {

    private val paint = Paint().apply {
        style = Paint.Style.FILL
        color = Color.BLUE
        isAntiAlias = true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val cx = width / 2f
        val cy = height / 2f
        val radius = minOf(cx, cy) - 4f
        canvas.drawCircle(cx, cy, radius, paint)
    }
}

Path และแอนิเมชันบน Android

Path บน Android เทียบเท่ากับ UIBezierPath: addCircle, addRect, addOval, lineTo, cubicTo และ quadTo PathMeasure คำนวณความยาวเส้นทางและพิกัดที่จุดใดๆ — มีประโยชน์สำหรับการเคลื่อนไหวของวัตถุตามเส้นทาง สำหรับแอนิเมชัน ใช้ ValueAnimator กับ invalidate() ใน callback ตัวปรับแต่งสามารถเปลี่ยนสี รัศมี ตำแหน่ง — คุณสมบัติใดๆ ที่ส่งผลต่อการวาด มุมมองแบบกำหนดเองบน Android ยังสนับสนุน StateListDrawable และ animated-vector สำหรับแอนิเมชันผ่าน XML

Shader และเกรเดียนต์ใน Canvas

เกรเดียนต์บน Android ถูกตั้งค่าผ่าน Shader — LinearGradient, RadialGradient และ SweepGradient Shader ถูกกำหนดให้กับ Paint ผ่าน paint.setShader(shader) LinearGradient สร้างเกรเดียนต์เชิงเส้นระหว่างสองจุดพร้อมตำแหน่งสีที่ไม่บังคับ RadialGradient — รัศมีจากศูนย์กลางถึงขอบ SweepGradient — รูปกรวยรอบจุดศูนย์กลาง Shader.TileMode (CLAMP, REPEAT, MIRROR) กำหนดพฤติกรรมนอกขอบเขตพื้นที่

CustomPainter ใน Flutter: การวาดบน Canvas

Flutter ใช้ CustomPainter — คลาสที่มีเมธอด paint(Canvas canvas, Size size) และ shouldRepaint Canvas ใน Flutter ให้เมธอดเดียวกันกับใน Android: drawCircle, drawRect, drawLine, drawPath, drawParagraph และ drawImage Paint ใน Flutter กำหนดค่าสี ความหนา สไตล์ และการลดรอยหยัก Flutter ไม่ใช้ drawRect หรือ onDraw — การวาดทั้งหมดผ่าน CustomPainter ซึ่งฝังอยู่ในวิดเจ็ต CustomPaint

dart
class CirclePainter extends CustomPainter {
  final Color color;

  CirclePainter({required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = color
      ..style = PaintingStyle.fill
      ..isAntiAlias = true;
    final radius = size.shortestSide / 2 - 4;
    canvas.drawCircle(size.center(Offset.zero), radius, paint);
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) =>
    oldDelegate.color != color;
}

shouldRepaint และ RepaintBoundary

shouldRepaint กำหนดว่า CustomPainter จำเป็นต้องวาดใหม่หรือไม่ คืนค่า true เฉพาะเมื่อข้อมูลที่ส่งผลต่อการเรนเดอร์เปลี่ยนแปลง — สี ขนาด ข้อมูลแผนภูมิ RepaintBoundary แยกการวาดใหม่เป็นเลเยอร์แยก — ใช้สำหรับวิดเจ็ตที่เปลี่ยนแปลงน้อยครั้ง Flutter ไม่มี setNeedsDisplay หรือ invalidate — การวาดใหม่ถูกเรียกผ่าน setState หรือ ValueNotifier ที่ฟังการเปลี่ยนแปลง

เกรเดียนต์และการแปลงใน Flutter

Flutter สนับสนุน LinearGradient, RadialGradient และ SweepGradient — ชนิดเดียวกับใน Android Gradient สร้าง Shader สำหรับ Paint ผ่าน createShader(Rect bounds) การแปลงดำเนินการผ่าน canvas.rotate, canvas.scale, canvas.translate และ canvas.skew Flutter ใช้เมทริกซ์การแปลง 4x4 (Matrix4) สำหรับวิดเจ็ต Transform แอนิเมชันการวาดใน Flutter ทำด้วย AnimationController ร่วมกับ setState เพื่อวาด CustomPainter ใหม่

การวัดมุมมองแบบกำหนดเอง: เมธอด sizeThatFits, onMeasure และ computeDryLayout

การวัดมุมมองแบบกำหนดเองที่ถูกต้องเป็นพื้นฐานของพฤติกรรมอินเทอร์เฟซที่คาดเดาได้ บน iOS ขนาดของมุมมองถูกกำหนดผ่าน sizeThatFits และ intrinsicContentSize sizeThatFits คืนค่าขนาดที่เหมาะสมที่สุดสำหรับข้อจำกัดที่กำหนด intrinsicContentSize — ขนาดธรรมชาติของเนื้อหาโดยไม่มีข้อจำกัดภายนอก บน Android การวัดถูกนำไปใช้ใน onMeasure ด้วยพารามิเตอร์ MeasureSpec (UNSPECIFIED, EXACTLY, AT_MOST) ใน Flutter ขนาดถูกส่งไปยัง paint(Canvas, Size) และสามารถคำนวณผ่าน computeDryLayout หรือ LayoutBuilder

sizeThatFits และ intrinsicContentSize บน iOS

intrinsicContentSize คืนค่า CGSize — ขนาดธรรมชาติของมุมมองตามเนื้อหา setContentHuggingPriority และ setContentCompressionResistancePriority ควบคุมพฤติกรรมระหว่างการยืดและการบีบ sizeThatFits ถูกเรียกโดยระบบเพื่อคำนวณขนาดใน Auto Layout สำหรับมุมมองแบบกำหนดเอง ให้แทนที่ intrinsicContentSize หากคอมโพเนนต์ของคุณมีขนาดธรรมชาติ — ตัวอย่างเช่น วงกลมควรเป็น 100x100 เสมอ

onMeasure และ MeasureSpec บน Android

onMeasure รับ widthMeasureSpec และ heightMeasureSpec MeasureSpec ประกอบด้วยโหมดและขนาด: UNSPECIFIED (ไม่มีข้อจำกัด มุมมองเลือกขนาดของตัวเอง), EXACTLY (ขนาดที่แน่นอนจากพาเรนต์), AT_MOST (ขนาดสูงสุด) setMeasuredDimension(width, height) บันทึกผลการวัด เมื่อข้อมูลเปลี่ยนแปลง ให้เรียก requestLayout() เพื่อวัดใหม่ ViewGroup ที่ซ้อนกันต้องการการแทนที่ onLayout เพื่อจัดเรียงองค์ประกอบย่อย

แพลตฟอร์มเมธอดการวัดการเรียกเมื่อเปลี่ยนแปลงค่าที่คืน
iOSsizeThatFits / intrinsicContentSizesetNeedsLayout / invalidateIntrinsicContentSizeCGSize
AndroidonMeasure / setMeasuredDimensionrequestLayout / invalidatevoid (บันทึกขนาด)
FluttercomputeDryLayoutsetState / LayoutBuilderSize

computeDryLayout ใน Flutter

computeDryLayout เป็นเมธอด RenderObject สำหรับคำนวณขนาดโดยไม่ต้องมีรอบเลย์เอาต์เต็ม LayoutBuilder.getSize — ทางเลือกสำหรับการรับพื้นที่ว่างในเมธอด build ใน Flutter ขนาดแคนวาสถูกส่งไปยัง paint เป็น Size และไม่ต้องการการวัดแยก — CustomPainter วาดภายในขอบเขตที่ส่ง อย่างไรก็ตาม สำหรับ RenderObject แบบกำหนดเอง คุณต้องแทนที่ performLayout และ computeDryLayout

คำถามที่พบบ่อย

drawRect บน iOS คืออะไรและเรียกเมื่อใด?

drawRect เป็นเมธอด UIView สำหรับการวาดแบบกำหนดเองผ่าน Core Graphics มันถูกเรียกในการเรนเดอร์ครั้งแรกของมุมมองและหลังจากการเรียก setNeedsDisplay() แต่ละครั้ง ใช้ CGContext ภายใน drawRect สำหรับเส้น รูปร่าง และข้อความ

วิธีการวาดบน Android โดยใช้ Canvas?

สร้างคลาสที่ขยาย View แทนที่ onDraw(Canvas canvas) และใช้ Canvas API: drawCircle, drawRect, drawPath Paint กำหนดค่าสี ความหนา และสไตล์ สำหรับการอัปเดต ให้เรียก invalidate()

CustomPainter ใน Flutter คืออะไร?

CustomPainter เป็นคลาส Flutter สำหรับการวาดแบบกำหนดเองบน Canvas ใช้เมธอด paint(Canvas, Size) และ shouldRepaint วางผ่านวิดเจ็ต CustomPaint Flutter ไม่มี drawRect — การวาดทั้งหมดผ่าน CustomPainter

วิธีการวัดขนาดของมุมมองแบบกำหนดเอง?

บน iOS ใช้ sizeThatFits และ intrinsicContentSize บน Android — onMeasure กับ MeasureSpec ใน Flutter ขนาดถูกส่งไปยัง paint(Canvas, Size) หรือคำนวณผ่าน computeDryLayout แต่ละแพลตฟอร์มใช้เมธอดการวัดของตัวเอง

เมื่อใดควรใช้มุมมองแบบกำหนดเองแทนคอมโพเนนต์มาตรฐาน?

มุมมองแบบกำหนดเอง จำเป็นสำหรับกราฟิกที่ไม่ซ้ำใคร แอนิเมชัน แผนภูมิ และองค์ประกอบที่ไม่สามารถนำไปใช้กับ UIKit หรือ View มาตรฐาน สำหรับสไตล์ง่ายๆ เพียงกำหนดค่าคอมโพเนนต์มาตรฐาน — อย่าทำให้สถาปัตยกรรมซับซ้อนโดยไม่จำเป็น

สรุป

  • มุมมองแบบกำหนดเอง — พื้นฐานของอินเทอร์เฟซที่ไม่ซ้ำใครสำหรับกราฟ แอนิเมชัน และองค์ประกอบที่ไม่มาตรฐานในแอปพลิเคชันมือถือ
  • drawRect — เมธอด iOS สำหรับการวาดผ่าน Core Graphics ด้วย CGContext และ UIBezierPath
  • onDraw — เมธอด Android สำหรับการวาดผ่าน Canvas และ Paint ด้วยการสนับสนุน Path และ Shader
  • CustomPainter — คลาส Flutter สำหรับการวาดบน Canvas ด้วย shouldRepaint และ RepaintBoundary
  • การวัด — การตรวจสอบขนาดผ่าน sizeThatFits (iOS), onMeasure (Android) และ computeDryLayout (Flutter)
  • การวาดในการพัฒนามือถือเป็นไปตามรูปแบบเดียวกันบนทุกแพลตฟอร์ม: รับบริบท กำหนดสไตล์ และวาดพื้นฐาน
  • แคชวัตถุการวาด (Paint, Path) — อย่าสร้างภายใน drawRect, onDraw หรือ paint เพื่อประสิทธิภาพที่เสถียร

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ