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 إلى عدة فئات: اللون والشفافية، نمط الحدود والتعبئة، معالجة النص والخطوط، تأثيرات التمويه والأقنعة. تتحكم في كل فئة طرق ضبط مستقلة يمكن دمجها بأي تركيبة. ما يجعل API Paint مرنةً، ولكنه يتطلب فهم تفاعل المعلمات — على سبيل المثال، يؤثر 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). تؤثر هذه المعلمات بشكل حاسم على مظهر الرسوم البيانية والمسارات والحدود في العروض المخصصة.
عند سمك أقل من 1 بكسل، يتم رسم الخط بدقة دون البكسل ولكنه قد يبدو ضبابيًا على شاشات منخفضة الكثافة. لشاشات Retina في Android، استخدم TypedValue.applyDimension لتحويل dp إلى بكسلات. يوفر سمك 2 dp رؤية واضحة على معظم الأجهزة.
DashPathEffect ينشئ خطوطًا متقطعة ومنقطة. يقبل المنشئ مصفوفة من أطوال الشرطات والفجوات المتبادلة وإزاحة المرحلة الابتدائية. على سبيل المثال، تنشئ المصفوفة [10, 5, 3, 5] خطًا بشرطة طويلة، فجوة قصيرة، شرطة قصيرة وفجوة. يتم استخدام هذا التأثير بنشاط في محررات الرسوم وأدوات التعليق.
يتم الدمج بين الأنماط من خلال setPathEffect. من خلال الدمج بين CornerPathEffect، DashPathEffect و DiscretePathEffect، يمكنك إنشاء خطوط بصرية معقدة للرسوم البيانية والتسميات والعناصر الزخرفية. يتم حساب كل PathEffect على وحدة المعالجة المركزية، لذا في الرسوم المتحركة بالعديد من الخطوط، يوصى بتحويل التأثير مسبقًا إلى 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. يتم تعيين 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))
}
لننظر إلى مثال كامل لعرض مخصص يرسم دائرة بتعبئة متدرجة وتسمية نصية. يسمح الدمج بين 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 في منشئ العرض. تغيير المعلمات في onDraw عبر المحددات آمن ولا ينشئ كائنات جديدة.
يتم رسم النص في Android دائمًا بخلفية شفافة. تطبع الطريقة drawText الرموز فقط، دون مستطيل خلفية. إذا كانت خلفية تحت النص مطلوبة، ارسم مستطيلاً أولاً باستخدام drawRoundRect مع Paint منفصل، ثم النص.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.