invalidate() — มันคืออะไร กลไกการวาดใหม่และ invalidate(Rect)

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

invalidate() เป็นเมธอดของคลาส View ใน Android ที่ทำเครื่องหมายวิวว่าต้องการการวาดใหม่ การเรียก invalidate() จะทำให้เกิดการวาดวิวใหม่ในรอบการรีเฟรชหน้าจอถัดไป ทำให้มันเป็นกลไกหลักในการอัปเดตสถานะภาพของคอมโพเนนต์ที่กำหนดเอง ตามเอกสาร Android Developers (2025) invalidate() ถูกใช้ใน 90% ของ View ที่กำหนดเองเพื่อซิงค์การเปลี่ยนแปลงข้อมูลกับการแสดงผลบนหน้าจอ เมธอดทำงานแบบอะซิงโครนัส — มันเพียงตั้งค่า dirty flag และคืนการควบคุมทันที

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

  • invalidate() — คำขอแบบอะซิงโครนัสเพื่อวาด View ใหม่ใน Android ทำงานผ่านกลไก dirty flag
  • postInvalidate() — เวอร์ชันของ invalidate() สำหรับเรียกจากเธรดพื้นหลัง ปลอดภัยต่อเธรด
  • invalidate(Rect) — การวาดใหม่บางส่วนเฉพาะพื้นที่ที่ระบุเพื่อเพิ่มประสิทธิภาพ
  • onDraw() — เมธอดที่ระบบเรียกหลังจาก invalidate() คล้ายกับ draw(_:) ใน iOS
  • invalidate() vs requestLayout() — invalidate() วาดเนื้อหาใหม่ requestLayout() คำนวณเรขาคณิตใหม่

invalidate() ใน Android คืออะไร

invalidate() เป็นเมธอดของคลาส android.view.View ที่บอกระบบ Android ว่าการแสดงผลทางภาพของวิวล้าสมัย หลังจากเรียกเมธอดแล้ว ระบบจะทำเครื่องหมายวิวเป็น dirty และกำหนดเวลาการวาดใหม่ในรอบการรีเฟรชหน้าจอถัดไป (ปกติ 16 ms สำหรับ 60 FPS)

เมธอด invalidate() มีหลายรูปแบบ: ไม่มีพารามิเตอร์ (วาดใหม่ทั้งหมด), มีพารามิเตอร์ Rect (บางส่วน) และมีพารามิเตอร์ ltrb (left, top, right, bottom) ทุกเวอร์ชันทำงานแบบอะซิงโครนัสและต้องเรียกจากเธรด UI สำหรับการเรียกจากเธรดพื้นหลังมี postInvalidate()

การวาดใหม่ผ่าน invalidate() ทำงานอย่างไร

กลไกการวาดใหม่ใน Android ขึ้นอยู่กับ ViewRootImpl — คอมโพเนนต์ภายในที่เชื่อมต่อลำดับชั้น View กับ Surface สำหรับการวาด เมื่อเรียก invalidate() ViewRootImpl จะทำเครื่องหมายพื้นที่วิวเป็น dirty และส่งคำขอวาดใหม่ผ่าน Choreographer — บริการระบบที่ซิงค์การวาดกับอัตราการรีเฟรชหน้าจอ

รอบการวาดใหม่

Choreographer รับสัญญาณจาก Vsync และเริ่มกระบวนการสามขั้นตอน: measure, layout, draw อย่างไรก็ตาม invalidate() มีผลเฉพาะเฟส draw เท่านั้น — เฟส measure และ layout จะไม่ถูกดำเนินการเว้นแต่จะเรียก requestLayout() นี่คือความแตกต่างสำคัญ: invalidate() เบากว่า requestLayout() เพราะไม่คำนวณเรขาคณิตใหม่

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

    private var dataPoints: List<Float> = emptyList()
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    fun updateData(newPoints: List<Float>) {
        dataPoints = newPoints
        invalidate() // คำขอวาดใหม่
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.color = Color.BLUE
        paint.strokeWidth = 4f
        paint.style = Paint.Style.STROKE

        // กำลังวาดเส้นกราฟ
        val path = Path()
        dataPoints.forEachIndexed { index, value ->
            val x = index * width / max(dataPoints.size - 1, 1)
            val y = height - value * height
            if (index == 0) path.moveTo(x, y)
            else path.lineTo(x, y)
        }
        canvas.drawPath(path, paint)
    }
}

ในตัวอย่างนี้ View ที่กำหนดเองสำหรับวาดกราฟเรียก invalidate() เมื่อข้อมูลถูกอัปเดต ระบบจะวาดเฉพาะ View นี้ใหม่โดยไม่ส่งผลต่อองค์ประกอบอื่นในลำดับชั้น onDraw() จะได้รับ Canvas สำหรับวาดเส้นผ่าน Path

invalidate() vs postInvalidate()

ความแตกต่างหลักระหว่าง invalidate() และ postInvalidate() อยู่ที่ความปลอดภัยของเธรด invalidate() ต้องเรียกจากเธรด UI (เธรดหลัก) เท่านั้น postInvalidate() สามารถเรียกจากเธรดใดก็ได้ — มันส่งคำขอวาดใหม่ไปยังเธรด UI ผ่าน Handler

คุณลักษณะinvalidate()postInvalidate()
เธรดที่เรียกเธรด UI (เธรดหลัก)เธรดใดก็ได้
กลไกอัปเดต dirty flag โดยตรงผ่าน Handler.post() ไปยังเธรด UI
ความหน่วงน้อยที่สุด ในรอบปัจจุบันจนถึงรอบถัดไปของเธรด UI
ประสิทธิภาพสูงโอเวอร์เฮดของ Handler เล็กน้อย
คำแนะนำใช้ invalidate() เสมอสำหรับเธรด UIเฉพาะเธรดพื้นหลัง

ในทางปฏิบัติ postInvalidate() ใช้ในสถานการณ์การโหลดข้อมูลเครือข่าย การประมวลผลผลลัพธ์เซนเซอร์ หรือการคำนวณพื้นหลัง หากคุณอยู่ในเธรด UI — ให้ใช้ invalidate() เสมอเพื่อความหน่วงน้อยที่สุด

kotlin
    // เรียกจากเธรด UI
view.invalidate()

    // เรียกจากเธรดพื้นหลัง
Thread {
    // การคำนวณหนัก
    val result = performHeavyCalculation()
    runOnUiThread {
        updateUi(result)
    }
}.start()

การวาดใหม่บางส่วนผ่าน invalidate(Rect)

invalidate(Rect) และ invalidate(int l, int t, int r, int b) อนุญาตให้จำกัดพื้นที่การวาดใหม่ นี่เป็นสิ่งสำคัญสำหรับประสิทธิภาพ: เมื่อเพียงบางส่วนของ View เปลี่ยนแปลง (เช่น การเคลื่อนที่ของเคอร์เซอร์ การเปลี่ยนตัวบ่งชี้) ไม่จำเป็นต้องวาดวิวทั้งหมดใหม่

ระบบส่งสี่เหลี่ยม dirty ที่ระบุไปยัง onDraw() ผ่าน canvas.clipBounds ภายใน onDraw() คุณสามารถตรวจสอบ clipBounds และวาดเฉพาะภายในพื้นที่นั้น แม้ว่า Android Canvas จะตัดการวาดนอกสี่เหลี่ยม dirty โดยอัตโนมัติ

kotlin
    // อัปเดตบางส่วน: เฉพาะพื้นที่เคอร์เซอร์
private val cursorRect = Rect()

fun moveCursorTo(newX: Int, newY: Int) {
    // ทำให้ตำแหน่งเก่าไม่ถูกต้อง
    invalidate(cursorRect)

    cursorRect.set(newX - 5, newY - 5,
                  newX + 5, newY + 5)

    // ทำให้ตำแหน่งใหม่ไม่ถูกต้อง
    invalidate(cursorRect)
}

หากไม่มีการวาดใหม่บางส่วน ทุกการเคลื่อนที่ของเคอร์เซอร์จะวาด View ทั้งหมดใหม่ ซึ่งสำหรับกราฟขนาดใหญ่หมายถึงการวาดพิกเซลหลายพันแทนที่จะเป็นไม่กี่สิบ invalidate(Rect) เป็นเทคนิคจำเป็นสำหรับโปรแกรมแก้ไข แคนวาสวาดภาพ และคอมโพเนนต์เคลื่อนไหว

invalidate() vs requestLayout(): ต่างกันอย่างไร

ข้อผิดพลาดทั่วไปคือการเรียก requestLayout() ในที่ที่แค่ invalidate() ก็เพียงพอ และในทางกลับกัน ความแตกต่างเป็นพื้นฐาน: invalidate() มีผลเฉพาะเฟส draw ในขณะที่ requestLayout() เริ่มรอบเต็ม measure → layout → draw

ด้านinvalidate()requestLayout()
เฟสของรอบเฉพาะ drawmeasure + layout + draw
เมื่อไรใช้เฉพาะการเรนเดอร์เปลี่ยน (สี ข้อความ กราฟิก)ขนาดหรือตำแหน่งของวิวเปลี่ยน
ประสิทธิภาพเบา — วาดใหม่เท่านั้นหนัก — คำนวณลำดับชั้นใหม่
ผลกระทบต่อลำดับชั้นเฉพาะวิวปัจจุบันอาจส่งผลต่อคอนเทนเนอร์แม่

หากคุณเปลี่ยนข้อความใน TextView แค่ invalidate() ก็เพียงพอเพราะขนาดวิวไม่เปลี่ยน หากข้อความอาจขึ้นบรรทัดใหม่และเพิ่มความสูง จำเป็นต้องใช้ requestLayout() Android Lint ช่วยติดตามข้อผิดพลาดเหล่านี้ผ่านกฎประสิทธิภาพ

การเพิ่มประสิทธิภาพของ invalidate()

การเรียก invalidate() มากเกินไปเป็นสาเหตุหลักประการหนึ่งของประสิทธิภาพต่ำของ View ที่กำหนดเองใน Android มาดูเทคนิคการเพิ่มประสิทธิภาพกัน

ลดความถี่ในการเรียก

หากข้อมูลอัปเดตด้วยความถี่สูง (เซนเซอร์ แอนิเมชัน วิดีโอ) อย่าเรียก invalidate() ทุกครั้งที่มีการเปลี่ยนแปลง ใช้ ValueAnimator หรือ Choreographer.FrameCallback เพื่อซิงค์กับอัตราการรีเฟรชหน้าจอ ซึ่งรับประกันว่า invalidate() จะถูกเรียกไม่เกินหนึ่งครั้งต่อเฟรม

ใช้การเร่งด้วยฮาร์ดแวร์

ตั้งแต่ API 14 Android รองรับการเร่งด้วยฮาร์ดแวร์ผ่าน GPU หาก View ที่กำหนดเองของคุณใช้เฉพาะ Canvas API (drawRect, drawCircle, drawPath) การเร่งจะทำงานอย่างโปร่งใส สำหรับการดำเนินการที่เข้ากันได้กับ DisplayList invalidate() จะถูกประมวลผลเร็วขึ้นอย่างมาก

kotlin
// ใช้ Choreographer สำหรับซิงค์ Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
    updateAnimation(frameTimeNanos)
    invalidate()
    Choreographer.getInstance().postFrameCallback(this)
}

fun startAnimation() {
    Choreographer.getInstance().postFrameCallback(frameCallback)
}

ใช้ invalidate(Rect) สำหรับการอัปเดตเป้าหมาย หลีกเลี่ยงการเรียก invalidate() จาก onDraw() (ลูปไม่มีที่สิ้นสุด) และวัดประสิทธิภาพผ่าน GPU Profile Rendering บนอุปกรณ์เสมอ ซึ่งจะแสดงเวลาเรนเดอร์ที่แน่นอนของแต่ละเฟรมและช่วยระบุพื้นที่ที่มีปัญหา

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

สามารถเรียก invalidate() จาก onDraw() ได้หรือไม่?

ไม่ได้ การเรียก invalidate() ภายใน onDraw() จะสร้างลูปการวาดใหม่ไม่มีที่สิ้นสุด: onDraw() เรียก invalidate() ซึ่งจะเรียก onDraw() อีกครั้ง ทำให้ CPU ใช้งาน 100% และเฟรมดรอป ใช้แอนิเมชันผ่าน ValueAnimator หรือ Choreographer

invalidate() แตกต่างจาก postInvalidate() อย่างไร?

invalidate() ทำงานเฉพาะในเธรด UI และอัปเดต dirty flag ทันที postInvalidate() ส่งคำขอผ่าน Handler ไปยังเธรด UI และสามารถเรียกจากเธรดพื้นหลังใดก็ได้ หากคุณอยู่ในเธรด UI — ใช้ invalidate() เพื่อความหน่วงน้อยที่สุด

setText() ใน TextView เรียก invalidate() โดยอัตโนมัติหรือไม่?

ใช่ ภายในเมธอด setText() ของ TextView เรียก invalidate() หลังจากอัปเดตข้อความ หากข้อความเปลี่ยนขนาดวิว จะเรียก requestLayout() เพิ่มเติมด้วย นักพัฒนาไม่จำเป็นต้องเรียก invalidate() ด้วยตนเองเมื่อทำงานกับวิดเจ็ตมาตรฐาน

invalidate() ส่งผลต่อประสิทธิภาพที่ 60 FPS อย่างไร?

แต่ละการเรียก invalidate() กำหนดเวลาการวาดใหม่ใน Vsync ถัดไป (ทุก 16 ms) หาก onDraw() ใช้เวลานานกว่า 16 ms จะเกิดเฟรมดรอป เพิ่มประสิทธิภาพ onDraw() — แคช Bitmap หลีกเลี่ยงการจัดสรร และใช้การเร่งด้วยฮาร์ดแวร์สำหรับการเรนเดอร์ GPU

จำเป็นต้องเรียก invalidate() หลังจากเปลี่ยนคุณสมบัติ Paint หรือไม่?

ใช่ หลังจากเปลี่ยนคุณสมบัติ Paint (สี ความหนา สไตล์) คุณต้องเรียก invalidate() เพราะ View ไม่ติดตามการเปลี่ยนแปลงของออบเจ็กต์ Paint โดยอัตโนมัติ ระบบไม่ทราบว่า Paint เปลี่ยนไปแล้วและจะไม่เรียก onDraw() โดยไม่มีการร้องขออย่างชัดแจ้ง

สรุป

  • invalidate() — กลไกหลักในการขอวาด View ใหม่ใน Android ทำงานแบบอะซิงโครนัสผ่าน dirty flag
  • postInvalidate() — เวอร์ชันปลอดภัยต่อเธรดสำหรับเรียกจากเธรดพื้นหลัง ใช้ Handler
  • invalidate(Rect) — การวาดใหม่บางส่วนเฉพาะพื้นที่ที่ระบุ สำคัญต่อประสิทธิภาพสำหรับการเปลี่ยนแปลงเป้าหมาย
  • requestLayout() — เริ่มรอบเต็ม measure + layout + draw หนักกว่า invalidate() อย่างมาก
  • Choreographer — บริการระบบสำหรับซิงค์ Vsync แนะนำสำหรับแอนิเมชันกับ invalidate()
  • Hardware Acceleration — การเร่ง GPU พร้อมใช้งานตั้งแต่ API 14 เพิ่มความเร็วการประมวลผล invalidate() สำหรับ Canvas API
  • GPU Profile Rendering — เครื่องมือวัดประสิทธิภาพสำหรับวัดเวลาเรนเดอร์และระบุเมธอด onDraw() ที่ช้า

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

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

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

อ่านเพิ่มเติม