invalidate() — ما هو، آلية إعادة الرسم و invalidate(Rect)

المؤلف: IT Sectr نُشر: 2026-07-20 وقت القراءة: 7 دق

invalidate() هو أسلوب من فئة View في Android يقوم بوضع علامة على العرض على أنه بحاجة إلى إعادة الرسم. استدعاء invalidate() يؤدي إلى إعادة رسم العرض في أقرب دورة تحديث للشاشة، مما يجعله الآلية الأساسية لتحديث الحالة البصرية للمكونات المخصصة. وفقاً لوثائق Android Developers (2025)، يُستخدم invalidate() في 90% من طرق العرض المخصصة لمزامنة تغييرات البيانات مع العرض على الشاشة. يعمل الأسلوب بشكل غير متزامن — فهو فقط يضع علامة dirty ويعيد التحكم فوراً.

الخلاصة

  • invalidate() — طلب غير متزامن لإعادة رسم View في Android، يعمل عبر آلية علامة dirty
  • postInvalidate() — إصدار من invalidate() للاستدعاء من خيط خلفي، آمن للخيوط
  • invalidate(Rect) — إعادة رسم جزئي للمنطقة المحددة فقط لتحسين الأداء
  • onDraw() — الأسلوب الذي يستدعيه النظام بعد invalidate()، مشابه لـ draw(_:) في iOS
  • invalidate() vs requestLayout() — invalidate() يعيد رسم المحتوى، requestLayout() يعيد حساب الهندسة

ما هو invalidate() في Android

invalidate() هو أسلوب من فئة android.view.View يُخبر نظام Android بأن التمثيل المرئي للعرض قديم. بعد استدعاء الأسلوب، يقوم النظام بوضع علامة على العرض كـ dirty ويجدد إعادة رسمه في أقرب دورة تحديث للشاشة (عادة 16 مللي ثانية لـ 60 إطاراً في الثانية).

يأتي أسلوب invalidate() بأشكال مختلفة: بدون معاملات (إعادة رسم كاملة)، مع معامل Rect (جزئي)، ومع معاملات ltrb (left, top, right, bottom). جميع الإصدارات تعمل بشكل غير متزامن ويجب استدعاؤها من خيط UI. للاستدعاء من الخيوط الخلفية يوجد postInvalidate().

كيف تعمل إعادة الرسم عبر invalidate()

آلية إعادة الرسم في Android تعتمد على ViewRootImpl — مكون داخلي يربط هيكل العرض بالسطح للرسم. عند استدعاء 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)
    }
}

في هذا المثال، يستدعي عرض مخصص لرسم رسم بياني invalidate() عند تحديث البيانات. يقوم النظام بإعادة رسم هذا العرض فقط دون التأثير على العناصر الأخرى في التسلسل الهرمي. onDraw() يتلقى Canvas لرسم الخطوط عبر Path.

invalidate() مقابل postInvalidate()

الفرق الرئيسي بين invalidate() و postInvalidate() يكمن في أمان الخيوط. invalidate() يجب استدعاؤه فقط من خيط UI (الخيط الرئيسي). postInvalidate() يمكن استدعاؤه من أي خيط — فهو يرسل طلب إعادة رسم إلى خيط UI عبر Handler.

الخاصيةinvalidate()postInvalidate()
خيط الاستدعاءخيط UI (الخيط الرئيسي)أي خيط
الآليةتحديث مباشر لعلامة dirtyعبر 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) تسمح بتحديد منطقة إعادة الرسم. هذا أمر بالغ الأهمية للأداء: عندما يتغير جزء فقط من العرض (مثل حركة المؤشر، تغيير المؤشر)، ليست هناك حاجة لإعادة رسم العرض بالكامل.

يقوم النظام بتمرير المستطيل 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)
}

بدون إعادة رسم جزئي، كل حركة مؤشر كانت ستؤدي إلى إعادة رسم العرض بالكامل، وهو ما يعني لرسم بياني كبير إعادة رسم آلاف البكسلات بدلاً من بضع عشرات. invalidate(Rect) هي تقنية أساسية للمحررات، أسطح الرسم، والمكونات المتحركة.

invalidate() مقابل requestLayout(): ما الفرق

أحد الأخطاء الشائعة هو استدعاء requestLayout() حيث يكفي invalidate()، والعكس صحيح. الفرق جوهري: invalidate() يؤثر فقط على مرحلة draw، بينما requestLayout() يؤدي إلى دورة كاملة measure → layout → draw.

الجانبinvalidate()requestLayout()
مراحل الدورةdraw فقطmeasure + layout + draw
متى تستخدميتغير فقط التقديم (اللون، النص، الرسومات)يتغير حجم أو موضع العرض
الأداءخفيف — إعادة رسم فقطثقيل — إعادة حساب التسلسل الهرمي
التأثير على التسلسل الهرميفقط العرض الحاليقد يؤثر على الحاويات الأم

إذا قمت بتغيير النص في TextView، فإن invalidate() كافٍ لأن حجم العرض لا يتغير. إذا كان النص قد ينتقل إلى سطر جديد ويزيد الارتفاع، فأنت بحاجة إلى requestLayout(). Android Lint يساعد في تتبع هذه الأخطاء من خلال قواعد الأداء.

تحسين أداء invalidate()

الاستدعاءات المفرطة لـ invalidate() هي أحد الأسباب الرئيسية لضعف أداء طرق العرض المخصصة في Android. دعنا نستعرض تقنيات التحسين.

قلل من تكرار الاستدعاءات

إذا كانت البيانات تتحدث بتردد عالٍ (المستشعرات، الرسوم المتحركة، الفيديو)، لا تستدعِ invalidate() عند كل تغيير. استخدم ValueAnimator أو Choreographer.FrameCallback للمزامنة مع معدل تحديث الشاشة. هذا يضمن استدعاء invalidate() ليس أكثر من مرة واحدة لكل إطار.

استخدم تسريع العتاد

منذ API 14، يدعم Android تسريع العتاد عبر GPU. إذا كان عرضك المخصص يستخدم فقط 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() مرة أخرى. هذا يؤدي إلى استخدام 100% من المعالج وتساقط الإطارات. استخدم الرسوم المتحركة عبر ValueAnimator أو Choreographer.

ما الفرق بين invalidate() و postInvalidate()؟

invalidate() يعمل فقط في خيط UI ويحدث علامة dirty فوراً. postInvalidate() يرسل طلباً عبر Handler إلى خيط UI ويمكن استدعاؤه من أي خيط خلفي. إذا كنت في خيط UI — استخدم invalidate() للحصول على أقل زمن استجابة.

هل setText() في TextView يستدعي invalidate() تلقائياً؟

نعم، داخلياً يستدعي أسلوب setText() في TextView invalidate() بعد تحديث النص. إذا غير النص أبعاد العرض، يتم أيضاً استدعاء requestLayout(). لا يحتاج المطور إلى استدعاء invalidate() يدوياً عند العمل مع الأدوات القياسية.

كيف يؤثر invalidate() على الأداء عند 60 إطاراً في الثانية؟

كل استدعاء لـ invalidate() يجدول إعادة رسم في Vsync التالي (كل 16 مللي ثانية). إذا استغرق onDraw() أكثر من 16 مللي ثانية، يحدث تساقط للإطارات. حسّن onDraw() — قم بتخزين Bitmaps مؤقتاً، تجنب التخصيصات، واستخدم تسريع العتاد لتقديم GPU.

هل أحتاج لاستدعاء invalidate() بعد تغيير خصائص Paint؟

نعم، بعد تغيير خصائص Paint (اللون، السمك، النمط)، يجب استدعاء invalidate()، لأن العرض لا يتتبع تغييرات كائنات Paint تلقائياً. النظام لا يعلم أن Paint قد تغير ولن يستدعي onDraw() بدون طلب صريح.

الملخص

  • invalidate() — الآلية الأساسية لطلب إعادة رسم View في Android، يعمل بشكل غير متزامن عبر علامة dirty
  • 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. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا