الرسم على عناصر Canvas

هذا الدرس العملي حول الترميز هو جزء من دورة "تطبيقات متقدّمة متوافقة مع نظام Android باستخدام لغة Kotlin". ستستفيد إلى أقصى حدّ من هذه الدورة التدريبية إذا تابعت دروس الترميز بالتسلسل، ولكن هذا ليس إلزاميًا. يمكنك الاطّلاع على جميع دورات الترميز في الدورة التدريبية على الصفحة المقصودة لدورات الترميز في "تطبيقات متقدمة متوافقة مع نظام Android باستخدام لغة Kotlin".

مقدمة

في نظام التشغيل Android، تتوفّر لك عدة أساليب لتنفيذ رسومات ثنائية الأبعاد ورسوم متحركة مخصّصة في طرق العرض.

بالإضافة إلى استخدام عناصر قابلة للرسم، يمكنك إنشاء رسومات ثنائية الأبعاد باستخدام طرق الرسم في الفئة Canvas. Canvas هو سطح رسم ثنائي الأبعاد يوفّر طرقًا للرسم. ويكون ذلك مفيدًا عندما يحتاج تطبيقك إلى إعادة رسم نفسه بانتظام، لأنّ ما يراه المستخدم يتغير بمرور الوقت. في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية إنشاء لوحة رسم وعرضها في View.

تشمل أنواع العمليات التي يمكنك تنفيذها على لوحة العرض ما يلي:

  • املأ لوحة العرض بالكامل باللون.
  • رسم أشكال، مثل المستطيلات والأقواس والمسارات التي تم تصميمها على النحو المحدّد في عنصر Paint يحتوي العنصر Paint على معلومات حول النمط واللون الخاصين بكيفية رسم الأشكال الهندسية (مثل الخط والمستطيل والشكل البيضاوي والمسارات)، أو على سبيل المثال، نوع خط النص.
  • تطبيق عمليات تحويل، مثل الترجمة أو تغيير الحجم أو عمليات التحويل المخصّصة
  • القص، أي تطبيق شكل أو مسار على لوحة العرض لتحديد الأجزاء المرئية منها

طريقة التفكير في عملية الرسم على Android (بشكل مبسط للغاية)

الرسم في نظام التشغيل Android أو أي نظام حديث آخر هو عملية معقّدة تتضمّن طبقات من التجريدات وعمليات تحسين وصولاً إلى الأجهزة. إنّ طريقة عرض Android هي موضوع شيّق تمّت الكتابة عنه كثيرًا، وتفاصيله تتجاوز نطاق هذا الدرس التطبيقي حول الترميز.

في سياق هذا الدرس العملي والتطبيق الذي يعتمد على لوحة الرسم للعرض في وضع ملء الشاشة، يمكنك التفكير في ذلك بالطريقة التالية.

  1. تحتاج إلى طريقة لعرض ما ترسمه. يمكن أن يكون هذا أحد طرق العرض التي يوفّرها نظام التشغيل Android. أو، في هذا الدرس التطبيقي حول الترميز، يمكنك إنشاء طريقة عرض مخصّصة تعمل كطريقة عرض المحتوى لتطبيقك (MyCanvasView).
  2. يتضمّن هذا العرض، مثل جميع طرق العرض، لوحة عرض خاصة به (canvas).
  3. لتنفيذ أبسط طريقة للرسم على لوحة عرض، عليك إلغاء طريقة onDraw() والرسم على لوحة العرض.
  4. عند إنشاء رسم، عليك تخزين ما رسمته سابقًا في ذاكرة التخزين المؤقت. هناك عدة طرق لتخزين البيانات مؤقتًا، إحداها في صورة نقطية (extraBitmap)، والأخرى هي حفظ سجلّ لما رسمته كإحداثيات وتعليمات.
  5. لرسم الصورة النقطية المخزّنة مؤقتًا (extraBitmap) باستخدام واجهة برمجة التطبيقات للرسم على لوحة العرض، عليك إنشاء لوحة عرض مخزّنة مؤقتًا (extraCanvas) للصورة النقطية المخزّنة مؤقتًا.
  6. بعد ذلك، يمكنك الرسم على لوحة العرض المؤقت (extraCanvas)، ما يؤدي إلى الرسم على الصورة النقطية للعرض المؤقت (extraBitmap).
  7. لعرض كل ما تم رسمه على الشاشة، عليك إخبار لوحة عرض (canvas) العرض برسم الصورة النقطية المخزّنة مؤقتًا (extraBitmap).

ما يجب معرفته

  • كيفية إنشاء تطبيق يتضمّن نشاطًا وتصميمًا أساسيًا وتشغيله باستخدام "استوديو Android"
  • كيفية ربط معالجات الأحداث بطرق العرض
  • كيفية إنشاء طريقة عرض مخصّصة

أهداف الدورة التعليمية

  • كيفية إنشاء Canvas والرسم عليه استجابةً للمس المستخدم

الإجراءات التي ستنفذّها

  • أنشئ تطبيقًا يرسم خطوطًا على الشاشة استجابةً للمس المستخدم للشاشة.
  • تسجيل أحداث الحركة، ورسم خطوط على لوحة عرض تظهر في عرض مخصّص بملء الشاشة على الشاشة استجابةً لهذه الأحداث

يستخدم تطبيق MiniPaint طريقة عرض مخصّصة لعرض خط استجابةً للمسات المستخدم، كما هو موضّح في لقطة الشاشة أدناه.

الخطوة 1: إنشاء مشروع MiniPaint

  1. أنشئ مشروع Kotlin جديدًا باسم MiniPaint يستخدم نموذج نشاط فارغ.
  2. افتح ملف app/res/values/colors.xml وأضِف اللونَين التاليَين.
<color name="colorBackground">#FFFF5500</color>
<color name="colorPaint">#FFFFEB3B</color>
  1. فتح "styles.xml"
  2. في العنصر الرئيسي لنمط AppTheme المحدّد، استبدِل DarkActionBar بـ NoActionBar. يزيل هذا الإعداد شريط الإجراءات، ما يتيح لك الرسم بملء الشاشة.
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

الخطوة 2: إنشاء فئة MyCanvasView

في هذه الخطوة، عليك إنشاء طريقة عرض مخصّصة، MyCanvasView، للرسم.

  1. في حزمة app/java/com.example.android.minipaint، أنشئ New > Kotlin File/Class باسم MyCanvasView.
  2. اجعل الفئة MyCanvasView توسّع الفئة View وتمرّر context: Context. قبول عمليات الاستيراد المقترَحة
import android.content.Context
import android.view.View

class MyCanvasView(context: Context) : View(context) {
}

الخطوة 3: ضبط MyCanvasView كطريقة عرض المحتوى

لعرض ما سترسمه في MyCanvasView، عليك ضبطه كطريقة عرض المحتوى في MainActivity.

  1. افتح ملف strings.xml وحدِّد سلسلة لاستخدامها في وصف محتوى العرض.
<string name="canvasContentDescription">Mini Paint is a simple line drawing app.
   Drag your fingers to draw. Rotate the phone to clear.</string>
  1. فتح "MainActivity.kt"
  2. في onCreate()، احذف setContentView(R.layout.activity_main).
  3. أنشئ مثيلاً من MyCanvasView.
val myCanvasView = MyCanvasView(this)
  1. تحت ذلك، اطلب ملء الشاشة لتنسيق myCanvasView. يمكنك إجراء ذلك من خلال ضبط العلامة SYSTEM_UI_FLAG_FULLSCREEN على myCanvasView. بهذه الطريقة، تملأ طريقة العرض الشاشة بالكامل.
myCanvasView.systemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN
  1. أضِف وصفًا للمحتوى.
myCanvasView.contentDescription = getString(R.string.canvasContentDescription)
  1. أسفل ذلك، اضبط طريقة عرض المحتوى على myCanvasView.
setContentView(myCanvasView)
  1. شغِّل تطبيقك. ستظهر لك شاشة بيضاء تمامًا، لأنّ لوحة العرض ليس لها حجم ولم ترسم أي شيء بعد.

الخطوة 1: تجاوز onSizeChanged()

يستدعي نظام Android الطريقة onSizeChanged() كلما تغيّر حجم العرض. بما أنّ العرض يبدأ بدون حجم، يتم أيضًا استدعاء طريقة onSizeChanged() الخاصة بالعرض بعد أن ينشئ النشاط العرض ويوسّعه لأول مرة. لذلك، تُعدّ onSizeChanged() هذه الطريقة المكان المثالي لإنشاء لوحة العرض وإعدادها.

  1. في MyCanvasView، على مستوى الفئة، حدِّد متغيّرات للوحة الرسم والصورة النقطية. اتّصِل بالرقمين extraCanvas وextraBitmap. هذه هي الصورة النقطية ولوحة الرسم لتخزين ما تم رسمه سابقًا مؤقتًا.
private lateinit var extraCanvas: Canvas
private lateinit var extraBitmap: Bitmap
  1. حدِّد متغيّرًا على مستوى الفئة backgroundColor للون الخلفية في لوحة الرسم، واضبط قيمته الأولية على colorBackground الذي حدّدته سابقًا.
private val backgroundColor = ResourcesCompat.getColor(resources, R.color.colorBackground, null)
  1. في MyCanvasView، ألغِ طريقة onSizeChanged(). يتم استدعاء طريقة معاودة الاتصال هذه من خلال نظام التشغيل Android مع أبعاد الشاشة المتغيرة، أي مع عرض وارتفاع جديدَين (للتغيير إلى) وعرض وارتفاع قديمَين (للتغيير منهما).
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
   super.onSizeChanged(width, height, oldWidth, oldHeight)
}
  1. داخل onSizeChanged()، أنشئ مثيلاً من Bitmap باستخدام العرض والارتفاع الجديدَين، وهما حجم الشاشة، وعيّنهما إلى extraBitmap. الوسيطة الثالثة هي إعدادات ألوان الصورة النقطية. تخزّن ARGB_8888 كل لون في 4 بايت ويُنصح باستخدامها.
extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
  1. أنشئ مثيلاً من Canvas من extraBitmap وأسنده إلى extraCanvas.
 extraCanvas = Canvas(extraBitmap)
  1. تحديد لون الخلفية الذي سيتم ملء extraCanvas به
extraCanvas.drawColor(backgroundColor)
  1. بالنظر إلى onSizeChanged()، يتم إنشاء صورة نقطية ولوحة رسم جديدتين في كل مرة يتم فيها تنفيذ الدالة. أنت بحاجة إلى صورة نقطية جديدة لأنّ الحجم قد تغيّر. ومع ذلك، يحدث تسرُّب للذاكرة، ما يؤدي إلى بقاء الصور النقطية القديمة. لحلّ هذه المشكلة، أعِد استخدام extraBitmap قبل إنشاء العنصر التالي عن طريق إضافة هذا الرمز مباشرةً بعد طلب super.
if (::extraBitmap.isInitialized) extraBitmap.recycle()

الخطوة 2: تجاوز onDraw()

يتم تنفيذ جميع أعمال الرسم الخاصة بـ MyCanvasView في onDraw().

للبدء، اعرض لوحة الرسم، واملأ الشاشة بلون الخلفية الذي ضبطته في onSizeChanged().

  1. تجاوز onDraw() ورسم محتوى extraBitmap المخزّن مؤقتًا على لوحة الرسم المرتبطة بطريقة العرض تتوفّر الطريقة drawBitmap() Canvas بعدّة إصدارات. في هذا الرمز، يمكنك تقديم الصورة النقطية وإحداثيات x وy (بالبكسل) للزاوية العلوية اليسرى وnull للـ Paint، لأنّك ستضبط ذلك لاحقًا.
override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
canvas.drawBitmap(extraBitmap, 0f, 0f, null)
}


يُرجى العِلم أنّ لوحة العرض التي يتم تمريرها إلى onDraw() ويستخدمها النظام لعرض الصورة النقطية تختلف عن تلك التي أنشأتها في طريقة onSizeChanged() واستخدمتها للرسم على الصورة النقطية.

  1. شغِّل تطبيقك. من المفترض أن ترى الشاشة بأكملها مملوءة بلون الخلفية المحدّد.

من أجل الرسم، تحتاج إلى كائن Paint يحدّد طريقة تصميم العناصر عند رسمها، وكائن Path يحدّد ما يتم رسمه.

الخطوة 1: تهيئة عنصر Paint

  1. في MyCanvasView.kt، على مستوى الملف الأعلى، حدِّد قيمة ثابتة لعرض الخط.
private const val STROKE_WIDTH = 12f // has to be float
  1. على مستوى الفئة MyCanvasView، حدِّد المتغيّر drawColor لتخزين اللون الذي سيتم الرسم به، واضبط قيمته الأولية على المورد colorPaint الذي حدّدته سابقًا.
private val drawColor = ResourcesCompat.getColor(resources, R.color.colorPaint, null)
  1. على مستوى الفئة، أضِف أدناه متغيّرًا paint لكائن Paint وقم بتهيئة هذا المتغيّر على النحو التالي.
// Set up the paint with which to draw.
private val paint = Paint().apply {
   color = drawColor
   // Smooths out edges of what is drawn without affecting shape.
   isAntiAlias = true
   // Dithering affects how colors with higher-precision than the device are down-sampled.
   isDither = true
   style = Paint.Style.STROKE // default: FILL
   strokeJoin = Paint.Join.ROUND // default: MITER
   strokeCap = Paint.Cap.ROUND // default: BUTT
   strokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)
}
  • color paint هو drawColor الذي حدّدته سابقًا.
  • تحدّد isAntiAlias ما إذا كان سيتم تطبيق تسوية الحواف. يؤدي ضبط isAntiAlias على true إلى تنعيم حواف ما يتم رسمه بدون التأثير في الشكل.
  • isDither، عندما true، يؤثر في طريقة تقليل عدد عيّنات الألوان التي تتميز بدقة أعلى من دقة الجهاز. على سبيل المثال، التمويه هو الطريقة الأكثر شيوعًا لتقليل نطاق ألوان الصور إلى 256 لونًا (أو أقل).
  • تحدّد السمة style نوع الرسم الذي سيتم تنفيذه على حد خارجي، وهو في الأساس خط. تحدّد Paint.Style ما إذا كان الشكل الأساسي الذي يتم رسمه سيتم ملؤه أو رسم خطوطه الخارجية أو كليهما (باللون نفسه). الإعداد التلقائي هو ملء العنصر الذي يتم تطبيق الطلاء عليه. (يؤدي "التعبئة" إلى تلوين الجزء الداخلي من الشكل، بينما يتبع "الحد" المخطط التفصيلي للشكل).
  • تحدّد strokeJoin من Paint.Join طريقة ربط الخطوط وقطاعات المنحنيات في مسار ذي حدود خارجية. القيمة التلقائية هي MITER.
  • تضبط strokeCap شكل نهاية الخط ليكون غطاءً، وتحدّد Paint.Cap كيفية تحديد بداية ونهاية الخطوط والمسارات التي تمّت تعبئتها. القيمة التلقائية هي BUTT.
  • تحدّد strokeWidth عرض الخط بالبكسل. القيمة التلقائية هي عرض خط رفيع جدًا، لذا يتم ضبطها على الثابت STROKE_WIDTH الذي حدّدته سابقًا.

الخطوة 2: تهيئة عنصر Path

Path هو مسار ما يرسمه المستخدم.

  1. في MyCanvasView، أضِف متغيّرًا path واضبط قيمته الأولية على كائن Path لتخزين المسار الذي يتم رسمه عند تتبُّع لمسة المستخدم على الشاشة. استيراد android.graphics.Path لـ Path
private var path = Path()

الخطوة 1: الردّ على الحركة على الشاشة

يتم استدعاء الطريقة onTouchEvent() في طريقة العرض كلما لمس المستخدم الشاشة.

  1. في MyCanvasView، ألغِ طريقة onTouchEvent() لتخزين إحداثيات x وy التي تم تمريرها في event. بعد ذلك، استخدِم تعبير when للتعامل مع أحداث الحركة عند النقر على الشاشة والتحرّك عليها ورفع الإصبع عنها. هذه هي الأحداث التي تهمّ رسم خط على الشاشة. بالنسبة إلى كل نوع من أنواع الأحداث، استدعِ طريقة مساعدة، كما هو موضّح في الرمز البرمجي أدناه. راجِع مستندات الفئة MotionEvent للحصول على قائمة كاملة بأحداث اللمس.
override fun onTouchEvent(event: MotionEvent): Boolean {
   motionTouchEventX = event.x
   motionTouchEventY = event.y

   when (event.action) {
       MotionEvent.ACTION_DOWN -> touchStart()
       MotionEvent.ACTION_MOVE -> touchMove()
       MotionEvent.ACTION_UP -> touchUp()
   }
   return true
}
  1. على مستوى الفئة، أضِف المتغيّرَين motionTouchEventX وmotionTouchEventY الناقصَين لتخزين الإحداثيات x وy لحدث اللمس الحالي مؤقتًا (إحداثيات MotionEvent). اضبط قيمتها الأولية على 0f.
private var motionTouchEventX = 0f
private var motionTouchEventY = 0f
  1. أنشئ رموزًا أولية للدوال الثلاث touchStart() وtouchMove() وtouchUp().
private fun touchStart() {}

private fun touchMove() {}

private fun touchUp() {}
  1. يجب أن يتم إنشاء التعليمات البرمجية وتشغيلها، ولكن لن يظهر لك أي شيء مختلف عن الخلفية الملونة حتى الآن.

الخطوة 2: تنفيذ touchStart()

يتم استدعاء هذا الإجراء عندما يلمس المستخدم الشاشة لأول مرة.

  1. على مستوى الفئة، أضِف متغيرات لتخزين أحدث قيمتَين لـ x وy مؤقتًا. بعد أن يتوقف المستخدم عن الحركة ويرفع إصبع اللمس، تصبح هذه النقطة هي نقطة البداية للمسار التالي (الجزء التالي من الخط الذي سيتم رسمه).
private var currentX = 0f
private var currentY = 0f
  1. نفِّذ طريقة touchStart() على النحو التالي. أعِد ضبط path، وانتقِل إلى إحداثيات x-y لحدث اللمس (motionTouchEventX وmotionTouchEventY)، وعيِّن currentX وcurrentY لهذه القيمة.
private fun touchStart() {
   path.reset()
   path.moveTo(motionTouchEventX, motionTouchEventY)
   currentX = motionTouchEventX
   currentY = motionTouchEventY
}

الخطوة 3: تنفيذ touchMove()

  1. على مستوى الصف، أضِف المتغيّر touchTolerance واضبطه على ViewConfiguration.get(context).scaledTouchSlop.
private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop

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

  • إذا لم يتحرّك الإصبع إلا قليلاً، لن تحتاج إلى الرسم.
  • إذا تحرّك الإصبع لمسافة أقل من touchTolerance، لا ترسم.
  • تعرض scaledTouchSlop المسافة بالبكسل التي يمكن أن يتحركها اللمس قبل أن يعتقد النظام أنّ المستخدم يتصفّح.
  1. حدِّد طريقة touchMove(). احسب المسافة المقطوعة (dx، dy)، وأنشئ منحنى بين النقطتين وخزِّنه في path، وعدِّل إجمالي currentX وcurrentY، وارسم path. بعد ذلك، اتّصِل بالرقم invalidate() لإعادة رسم الشاشة باستخدام path المعدَّل.
private fun touchMove() {
   val dx = Math.abs(motionTouchEventX - currentX)
   val dy = Math.abs(motionTouchEventY - currentY)
   if (dx >= touchTolerance || dy >= touchTolerance) {
       // QuadTo() adds a quadratic bezier from the last point,
       // approaching control point (x1,y1), and ending at (x2,y2).
       path.quadTo(currentX, currentY, (motionTouchEventX + currentX) / 2, (motionTouchEventY + currentY) / 2)
       currentX = motionTouchEventX
       currentY = motionTouchEventY
       // Draw the path in the extra bitmap to cache it.
       extraCanvas.drawPath(path, paint)
   }
   invalidate()
}

في ما يلي تفاصيل أكثر عن هذه الطريقة:

  1. احسب المسافة التي تمّت إزاحتها (dx, dy).
  2. إذا كانت الحركة أبعد من مدى التفاوت المسموح به للمس، أضِف جزءًا إلى المسار.
  3. اضبط نقطة البداية للجزء التالي على نقطة نهاية هذا الجزء.
  4. يؤدي استخدام quadTo() بدلاً من lineTo() إلى إنشاء خط مرسوم بسلاسة بدون زوايا. يُرجى الاطّلاع على منحنيات بيزير.
  5. استدعِ الدالة invalidate() (لاستدعاء الدالة onDraw() في النهاية) لإعادة رسم العرض.

الخطوة 4: تنفيذ touchUp()

عندما يرفع المستخدم إصبعه عن الشاشة، كل ما عليه فعله هو إعادة ضبط المسار حتى لا يتم رسمه مرة أخرى. لا يتم رسم أي شيء، لذا لا حاجة إلى إبطال صحة العرض.

  1. نفِّذ الطريقة touchUp().
private fun touchUp() {
   // Reset the path so it doesn't get drawn again.
   path.reset()
}
  1. نفِّذ الرمز البرمجي واستخدِم إصبعك للرسم على الشاشة. يُرجى العِلم أنّه في حال تدوير الجهاز، سيتم محو الشاشة لأنّه لا يتم حفظ حالة الرسم. في هذا التطبيق النموذجي، تم تصميم هذه الميزة لمنح المستخدم طريقة بسيطة لمحو الشاشة.

الخطوة 5: رسم إطار حول الرسم التخطيطي

أثناء الرسم على الشاشة، ينشئ تطبيقك المسار ويحفظه في الصورة النقطية extraBitmap. تعرض طريقة onDraw() الصورة النقطية الإضافية في لوحة الرسم الخاصة بطريقة العرض. يمكنك الرسم أكثر في onDraw(). على سبيل المثال، يمكنك رسم أشكال بعد رسم الصورة النقطية.

في هذه الخطوة، ارسم إطارًا حول حافة الصورة.

  1. في MyCanvasView، أضِف متغيّرًا باسم frame يحتوي على عنصر Rect.
private lateinit var frame: Rect
  1. في نهاية onSizeChanged()، حدِّد مساحة داخلية، وأضِف رمزًا لإنشاء Rect سيتم استخدامه للإطار، باستخدام الأبعاد الجديدة والمساحة الداخلية.
// Calculate a rectangular frame around the picture.
val inset = 40
frame = Rect(inset, inset, width - inset, height - inset)
  1. في onDraw()، بعد رسم الصورة النقطية، ارسم مستطيلاً.
// Draw a frame around the canvas.
canvas.drawRect(frame, paint)
  1. شغِّل تطبيقك ولاحظ الإطار.

المهمة (اختيارية): تخزين البيانات في مسار

في التطبيق الحالي، يتم تخزين معلومات الرسم في صورة نقطية. على الرغم من أنّ هذا الحلّ جيد، إلا أنّه ليس الطريقة الوحيدة الممكنة لتخزين معلومات الرسومات. تعتمد طريقة تخزين سجلّ الرسومات على التطبيق ومتطلباتك المختلفة. على سبيل المثال، إذا كنت ترسم أشكالاً، يمكنك حفظ قائمة بالأشكال مع مواقعها الجغرافية وأبعادها. بالنسبة إلى تطبيق MiniPaint، يمكنك حفظ المسار بتنسيق Path. في ما يلي الخطوط العريضة العامة حول كيفية إجراء ذلك، إذا أردت تجربته.

  1. في MyCanvasView، أزِل كل الرموز الخاصة بـ extraCanvas وextraBitmap.
  2. أضِف متغيرات للمسار حتى الآن والمسار الذي يتم رسمه حاليًا.
// Path representing the drawing so far
private val drawing = Path()

// Path representing what's currently being drawn
private val curPath = Path()
  1. في onDraw()، بدلاً من رسم الصورة النقطية، ارسم المسارات المخزّنة والحالية.
// Draw the drawing so far
canvas.drawPath(drawing, paint)
// Draw any current squiggle
canvas.drawPath(curPath, paint)
// Draw a frame around the canvas
canvas.drawRect(frame, paint)
  1. في touchUp()، أضِف المسار الحالي إلى المسار السابق وأعِد ضبط المسار الحالي.
// Add the current path to the drawing so far
drawing.addPath(curPath)
// Rewind the current path for the next touch
curPath.reset()
  1. شغِّل تطبيقك، ولن يكون هناك أي اختلاف على الإطلاق.

نزِّل الرمز البرمجي للدرس التطبيقي حول الترميز المكتمل.

$  git clone https://github.com/googlecodelabs/android-kotlin-drawing-canvas


يمكنك بدلاً من ذلك تنزيل المستودع كملف Zip وفك ضغطه وفتحه في Android Studio.

تنزيل ملف Zip

  • Canvas هو سطح رسم ثنائي الأبعاد يوفّر طرقًا للرسم.
  • يمكن ربط Canvas بمثيل View يعرضه.
  • يحتوي العنصر Paint على معلومات حول النمط واللون وكيفية رسم الأشكال الهندسية (مثل الخط والمستطيل والشكل البيضاوي والمسارات) والنص.
  • من الأنماط الشائعة للعمل باستخدام لوحة الرسم إنشاء طريقة عرض مخصّصة وتجاوز الطريقتَين onDraw() وonSizeChanged().
  • يمكنك إلغاء طريقة onTouchEvent() لتسجيل لمسات المستخدمين والرد عليها من خلال رسم العناصر.
  • يمكنك استخدام صورة نقطية إضافية لتخزين المعلومات مؤقتًا للرسومات التي تتغير بمرور الوقت. بدلاً من ذلك، يمكنك تخزين الأشكال أو المسار.

دورة Udacity التدريبية:

مستندات مطوّري تطبيقات Android:

يسرد هذا القسم مهامًا منزلية محتملة للطلاب الذين يعملون على هذا الدرس التطبيقي العملي كجزء من دورة تدريبية يقودها مدرّب. على المعلّم تنفيذ ما يلي:

  • حدِّد واجبًا منزليًا إذا لزم الأمر.
  • توضيح كيفية إرسال الواجبات المنزلية للطلاب
  • وضع درجات للواجبات المنزلية

يمكن للمدرّبين استخدام هذه الاقتراحات بالقدر الذي يريدونه، ويجب ألا يترددوا في تكليف الطلاب بأي واجبات منزلية أخرى يرونها مناسبة.

إذا كنت تعمل على هذا الدرس العملي بنفسك، يمكنك استخدام مهام الواجب المنزلي هذه لاختبار معلوماتك.

الإجابة عن هذه الأسئلة

السؤال 1

أيّ من المكوّنات التالية مطلوبة للعمل باستخدام Canvas؟ يُرجى اختيار جميع الإجابات المناسبة.

Bitmap

Paint

Path

View

السؤال 2

ما هو الإجراء الذي يتم تنفيذه عند الاتصال بالرقم invalidate() (بشكل عام)؟

▢ يبطل تطبيقك ويعيد تشغيله.

▢ يمحو الرسم من الصورة النقطية.

يشير الرمز ▢ إلى أنّه يجب عدم تنفيذ الرمز السابق.

▢ يخبر النظام بأنّه عليه إعادة رسم الشاشة.

السؤال 3

ما هي وظيفة العناصر Canvas وBitmap وPaint؟

‫▢ سطح رسم ثنائي الأبعاد، صورة نقطية معروضة على الشاشة، معلومات حول تصميم الرسم

‫▢ سطح رسم ثلاثي الأبعاد، صورة نقطية لتخزين المسار مؤقتًا، معلومات التنسيق للرسم

‫▢ سطح رسم ثنائي الأبعاد، صورة نقطية معروضة على الشاشة، تصميم للعرض

‫▢ ذاكرة التخزين المؤقت لمعلومات الرسم، والصورة النقطية للرسم عليها، ومعلومات التنسيق الخاصة بالرسم

للحصول على روابط تؤدي إلى دروس برمجية أخرى في هذه الدورة التدريبية، يمكنك الانتقال إلى الصفحة المقصودة للدروس البرمجية المتقدّمة حول Android بلغة Kotlin.