طرق التنفيذ

نقدّم لك Cahier: نموذج جديد لتطبيق Android على GitHub يتيح الإنتاجية والإبداع على الشاشات الكبيرة

يستغرق الاطِّلاع على المقال 11 دقيقة
الاطّلاع على الملف الشخصي لـ "كريس أسغبي"
Chris Assigbe مهندسة علاقات المطوّرين

أصبحت Ink API الآن في مرحلة الإصدار التجريبي وهي جاهزة للدمج في تطبيقك. وقد تحقّق هذا الإنجاز بفضل ملاحظات المطوّرين القيّمة، ما أدّى إلى تحسينات مستمرة في أداء واجهة برمجة التطبيقات واستقرارها وجودتها المرئية.

تستخدم تطبيقات Google، مثل مستندات Google وPixel Studio وصور Google وملف PDF في Chrome وأداة إنشاء التأثيرات في YouTube والميزات الفريدة على Android، مثل دائرة البحث، أحدث واجهات برمجة التطبيقات. 

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

ما هو Cahier؟

Cahier (تعني "دفتر" بالفرنسية) هو نموذج تطبيق مصمَّم لتوضيح كيفية إنشاء تطبيق يتيح للمستخدمين تسجيل أفكارهم وتنظيمها من خلال الجمع بين النصوص والرسومات والصور. 

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

تشمل الميزات الرئيسية المعروضة في النموذج ما يلي:

  • إنشاء ملاحظات متعددة الاستخدامات: يوضّح هذا القسم كيفية تنفيذ نظام مرن لإنشاء المحتوى يتيح استخدام تنسيقات متعددة ضمن ملاحظة واحدة، بما في ذلك النصوص والرسومات الحرة والمرفقات المصوّرة.
  • أدوات الكتابة الإبداعية: تنفّذ تجربة رسم عالية الأداء ومنخفضة وقت الاستجابة باستخدام Ink API. يقدّم النموذج مثالاً عمليًا على دمج فُرش مختلفة وأداة اختيار الألوان ووظيفة التراجع/الإعادة وأداة الممحاة.
  • دمج المحتوى المرن باستخدام ميزة السحب والإفلات: يوضّح هذا المثال كيفية التعامل مع المحتوى الوارد والصادر باستخدام ميزة السحب والإفلات. ويشمل ذلك قبول الصور التي يتم إسقاطها من تطبيقات أخرى والسماح للمستخدمين بسحب المحتوى من تطبيقك لمشاركته بسلاسة.
  • تنظيم الملاحظات: يمكنك وضع علامة "مفضّلة" على الملاحظات للوصول إليها بسرعة. فلتر العرض للحفاظ على التنظيم
  • بنية تعمل بلا إنترنت أولاً:  تم تصميم التطبيق ببنية تعمل بلا إنترنت أولاً باستخدام Room، ما يضمن حفظ جميع البيانات محليًا واستمرار عمل التطبيق بكامل وظائفه بدون اتصال بالإنترنت.
  • إمكانات قوية لدعم النوافذ المتعددة وفتح نسختين من التطبيق: توضّح هذه الميزة كيفية دعم فتح نسختين من التطبيق، ما يسمح بتشغيل تطبيقك في نوافذ متعددة ليتمكّن المستخدمون من العمل على ملاحظات مختلفة جنبًا إلى جنب، ما يعزّز الإنتاجية والإبداع على الشاشات الكبيرة.
  • واجهة مستخدم متكيّفة مع جميع الشاشات: تتكيّف واجهة المستخدم بسلاسة مع أحجام الشاشات واتجاهاتها المختلفة باستخدام ListDetailPaneScaffold و NavigationSuiteScaffold لتوفير تجربة مستخدم محسّنة على الهواتف والأجهزة اللوحية والأجهزة القابلة للطي.
  • التكامل العميق مع النظام: يقدّم هذا المستند دليلاً حول كيفية جعل تطبيقك هو تطبيق تدوين الملاحظات التلقائي على الإصدار 14 من نظام التشغيل Android والإصدارات الأحدث من خلال الاستجابة لطلبات "الملاحظات" على مستوى النظام، ما يتيح إمكانية تسجيل المحتوى بسرعة من نقاط دخول مختلفة في النظام.

مصمَّم لزيادة الإنتاجية والإبداع على الشاشات الكبيرة

في الإصدار الأوّلي، سنركّز الإعلان على بعض الميزات الأساسية التي تجعل Cahier مصدرًا رئيسيًا للتعلم في حالات استخدام الإنتاجية والإبداع.

أساس التكيّف

تم تصميم Cahier ليكون قابلاً للتكيّف من البداية. يستخدم النموذج مكتبة material3-adaptive، وتحديدًا ListDetailPaneScaffold وNavigationSuiteScaffold، لتكييف تخطيط التطبيق بسلاسة مع أحجام الشاشات واتجاهاتها المختلفة. هذا عنصر أساسي لتطبيق Android حديث، وتقدّم Cahier مثالاً واضحًا على كيفية تنفيذه بفعالية.

Cahier adaptive UI built with Material 3 Adaptive library..gif

واجهة مستخدم Cahier التكيُّفية المستندة إلى مكتبة Material 3 Adaptive

عرض واجهات برمجة التطبيقات وعمليات الدمج الرئيسية

يركّز النموذج على عرض واجهات برمجة تطبيقات إنتاجية فعّالة يمكنك الاستفادة منها في تطبيقاتك، بما في ذلك:

نظرة عن كثب على واجهات برمجة التطبيقات الرئيسية

لنتعرّف بالتفصيل على اثنتين من واجهات برمجة التطبيقات الأساسية التي يدمجها تطبيق Cahier لتقديم تجربة تدوين ملاحظات من الدرجة الأولى.

إنشاء تجارب كتابة طبيعية باستخدام Ink API

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

توفّر Ink API بنية معيارية، ما يتيح لك تخصيصها لتناسب حزمة تطبيقك واحتياجاته المحدّدة. تتضمّن وحدات واجهة برمجة التطبيقات ما يلي:

  • وحدات الإنشاء (إنشاءطرق العرض): تعالج هذه الوحدات إدخال الحبر في الوقت الفعلي لإنشاء ضربات سلسة بأقل وقت استجابة يمكن أن يوفّره الجهاز.
    • في DrawingSurface، يستخدم تطبيق Cahier العنصر القابل للإنشاء InProgressStrokes الذي تم تقديمه حديثًا للتعامل مع إدخال القلم أو اللمس في الوقت الفعلي. هذه الوحدة مسؤولة عن تسجيل أحداث المؤشر وعرض ضربات الحبر الرطب بأقل وقت استجابة ممكن.
  • وحدة الضربات: تمثّل إدخال الحبر وتمثيله المرئي. عندما ينتهي المستخدم من رسم خط، تقدّم الدالة onStrokesFinished المتصلة عنصر Stroke نهائيًا/جافًا إلى التطبيق. تتم بعد ذلك إدارة هذا العنصر غير القابل للتغيير، الذي يمثّل ضربة الحبر المكتملة، في DrawingCanvasViewModel.
  • وحدة العرض: تعرض ضربات الحبر بكفاءة، ما يتيح دمجها مع Compose من Jetpack أو طرق العرض في Android.
    • لعرض كل من الضربات الحالية والضربات التي تم تجفيفها حديثًا، يستخدم Cahier CanvasStrokeRenderer  في DrawingSurface للرسم النشط وفي DrawingDetailPanePreview لعرض معاينة ثابتة للملاحظة. تعمل هذه الوحدة بكفاءة على رسم عناصر Stroke على Canvas.
  • وحدات Brush (إنشاءطرق العرض): توفّر طريقة تعريفية لتحديد النمط المرئي للضربات. تشمل التحديثات الأخيرة (منذ إصدار alpha03) فرشاة خط متقطّع جديدة، وهي مفيدة بشكل خاص لميزات مثل التحديد باستخدام أداة التحديد الحر. يحتوي DrawingCanvasViewModel على حالة currentBrush. يتيح صندوق الأدوات في DrawingCanvas للمستخدمين اختيار مجموعات مختلفة من الفرش (مثل StockBrushes.pressurePen() أو StockBrushes.highlighter()) وتغيير الألوان. تعدِّل ViewModel العنصر Brush الذي يستخدمه بعد ذلك العنصر InProgressStrokes القابل للإنشاء من أجل ضربات الفرشاة الجديدة.
  • وحدات الأشكال الهندسية (Composeطرق العرض): تتيح هذه الوحدات معالجة وتحليل ضربات القلم لاستخدامها في ميزات مثل المحو والاختيار.
    • تعتمد أداة الممحاة ضمن صندوق الأدوات والوظائف في DrawingCanvasViewModel على وحدة الأشكال الهندسية. عندما تكون الممحاة نشطة، تنشئ MutableParallelogram حول مسار إيماءة المستخدم. بعد ذلك، تتحقّق الممحاة من التقاطعات بين الشكل والمربّعات المحيطة بالضربات الحالية لتحديد الضربات التي يجب محوها، ما يجعل الممحاة سهلة الاستخدام ودقيقة.
  • وحدة التخزين: توفّر إمكانات فعّالة لتسلسل بيانات الحبر وإلغاء تسلسلها، ما يؤدي إلى توفير مساحة كبيرة على القرص وفي الشبكة. لحفظ الرسومات، يحتفظ تطبيق Cahier بكائنات Stroke في قاعدة بيانات Room. في المحوّلات، يستخدم المثال الدالة encode الخاصة بوحدة التخزين لتسلسل StrokeInputBatch (بيانات النقاط الأولية) إلى ByteArray. يتم حفظ مصفوفة البايت، بالإضافة إلى خصائص الفرشاة، كسلسلة JSON. يتم استخدام الدالة decode لإعادة إنشاء ضربات القلم عند تحميل ملاحظة.
orion.png

بالإضافة إلى هذه الوحدات الأساسية، وسّعت التحديثات الأخيرة إمكانات Ink API:

  • تتيح واجهات برمجة التطبيقات التجريبية الجديدة لعناصر BrushFamily المخصّصة للمطوّرين إنشاء أنواع فرش إبداعية وفريدة، ما يوفّر إمكانات لأدوات مثل فرشاة القلم ومؤشر الليزر.

تستفيد Cahier من الفُرَش المخصّصة، بما في ذلك فرشاة الموسيقى الفريدة المعروضة أدناه، لتوضيح الإمكانات الإبداعية المتقدّمة.

Rainbow laser created with Ink API's custom brushes..gif

مؤشر ليزر بألوان قوس قزح تم إنشاؤه باستخدام فُرش مخصّصة في Ink API

notes.png

فرشاة الموسيقى التي تم إنشاؤها باستخدام الفرش المخصّصة في Ink API

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

توفّر Ink API العديد من المزايا التي تجعلها الخيار المثالي لتطبيقات الإنتاجية والإبداع بدلاً من التنفيذ المخصّص:

  • سهولة الاستخدام: تجرِّد Ink API تعقيدات الرسومات والأشكال الهندسية، ما يتيح لك التركيز على الميزات الأساسية في Cahier.
  • الأداء: يضمن توفُّر ميزة الاستجابة السريعة المُدمَجة وعملية العرض المحسَّنة تجربة سلسة وسريعة الاستجابة عند الكتابة بالحبر.
  • المرونة: يتيح لك التصميم المعياري اختيار المكوّنات المطلوبة، ما يتيح دمج Ink API بسلاسة في بنية Cahier.

تمّ اعتماد Ink API في العديد من تطبيقات Google، بما في ذلك إضافة التعليقات التوضيحية في "مستندات Google" واستخدام ميزة "دائرة البحث"، بالإضافة إلى تطبيقات الشركاء مثل Orion Notes وPDF Scanner.

"كانت Ink API خيارنا الأول لميزة "دائرة البحث" (CtS). بفضل مستنداتهم الشاملة، كانت عملية دمج Ink API سهلة للغاية، ما سمح لنا بالوصول إلى أول نموذج أولي يعمل في غضون أسبوع واحد فقط. وقد أتاحت لنا ميزة "الحبر" إمكانية تكرار تصميم ضربات الفرشاة بسرعة بفضل دعمها لنسيج الفرشاة المخصّص والرسوم المتحركة". - "جوردان كومودا"، مهندس برمجيات في Google

أن يصبح تطبيق تدوين الملاحظات التلقائي مع دور الملاحظات

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

التنفيذ في Cahier

يتضمّن تنفيذ دور الملاحظات بعض الخطوات الرئيسية، وكلّها موضّحة في النموذج:

  1. بيان ملف البيان: يجب أولاً أن يوضّح التطبيق قدرته على التعامل مع أغراض تدوين الملاحظات. في ملف AndroidManifest.xml، يتضمّن تطبيق Cahier <intent-filter> للإجراء android.intent.action.CREATE_NOTE. تشير هذه العلامة إلى النظام بأنّ التطبيق هو مرشّح محتمل لدور تدوين الملاحظات.
  2. التحقّق من حالة الدور: يستخدم SettingsViewModel RoleManager في Android لتحديد الحالة الحالية. يتحقّق SettingsViewModel مما إذا كان دور الملاحظات متاحًا على الجهاز (isRoleAvailable) وما إذا كان تطبيق Cahier يؤدي هذا الدور حاليًا (isRoleHeld). يتم عرض هذه الحالة في واجهة المستخدم باستخدام تدفقات Kotlin.
  3. طلب الدور: في ملف Settings.kt، يتم عرض زر للمستخدم إذا كان الدور متاحًا ولكن لم يتم الحصول عليه. عند النقر على الزر، يستدعي الدالة requestNotesRole في ViewModel. تنشئ الدالة غرضًا لفتح شاشة إعدادات التطبيق التلقائي حيث يمكن للمستخدم اختيار Cahier. تتم إدارة العملية باستخدام واجهة برمجة التطبيقات rememberLauncherForActivityResult التي تتولّى إطلاق الغرض واستلام النتيجة.
  4. تعديل واجهة المستخدم: بعد أن يعود المستخدم من شاشة الإعدادات، يؤدي ردّ الاتصال ActivityResultLauncher إلى تشغيل دالة في ViewModel لتعديل حالة الدور، ما يضمن أنّ واجهة المستخدم تعكس بدقة ما إذا كان التطبيق هو التطبيق التلقائي الآن.

تعرَّف على كيفية دمج دور "الملاحظات" في تطبيقك من خلال دليل إنشاء تطبيق لتدوين الملاحظات.

helloworld.png

تم إطلاق تطبيق Cahier في نافذة عائمة ليكون تطبيق تدوين الملاحظات التلقائي على أجهزة Lenovo اللوحية

خطوة كبيرة إلى الأمام: تتيح Lenovo دور "الملاحظات"

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

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

فتح نسختين من التطبيق، واستخدام نوافذ متعددة، والعرض في نافذة

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

  • النوافذ المتعدّدة: هي الإمكانية الأساسية لتشغيل تطبيق إلى جانب تطبيق آخر في وضع تقسيم الشاشة أو الوضع الحرّ. هذا أمر ضروري لتنفيذ مهام مثل الرجوع إلى صفحة ويب أثناء تدوين الملاحظات في Cahier.
  • فتح نسختين من التطبيق: هنا تظهر مزايا تنفيذ مهام متعددة. يتيح تطبيق Cahier للمستخدمين فتح نوافذ متعددة ومستقلة للتطبيق في الوقت نفسه. تخيَّل مقارنة ملاحظتَين مختلفتَين جنبًا إلى جنب أو الرجوع إلى ملاحظة نصية في إحدى النوافذ أثناء العمل على رسم في نافذة أخرى. يوضّح Cahier كيفية إدارة هذه المثيلات المنفصلة، ولكل منها حالته الخاصة، ما يحوّل تطبيقك إلى أداة قوية ومتعددة الأوجه.
  • العرض في نافذة على سطح المكتب: عند توصيل جهاز لوحي أو جهاز قابل للطي بشاشة عرض خارجية، يحوّل "وضع الكمبيوتر المكتبي" على Android الجهاز إلى محطة عمل. بما أنّ تطبيق Cahier مصمّم بواجهة مستخدم قابلة للتكيّف ويتيح فتح نسختين من التطبيق، يعمل التطبيق بشكل ممتاز في هذه البيئة. يمكن للمستخدمين فتح نوافذ Cahier متعددة وتغيير أحجامها وموضعها تمامًا كما هو الحال على الكمبيوتر المكتبي التقليدي، ما يتيح إمكانية تنفيذ مهام معقّدة لم تكن متاحة من قبل على الأجهزة الجوّالة.
cahier-desktop-windowing.webp

تطبيق Cahier يعمل في وضع النافذة على سطح المكتب على جهاز Pixel Tablet

في ما يلي كيفية تنفيذ هذه الميزات في Cahier:

لتفعيل ميزة تشغيل عدة مثيلات، كان علينا أولاً إعلام النظام بأنّ التطبيق يتيح تشغيله عدة مرات من خلال إضافة السمة PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI إلى تعريف MainActivity في AndroidManifest:

<activity

    android:name="com.example.cahier.MainActivity"

    android:exported="true"

    android:label="@string/app_name"

    android:theme="@style/Theme.MyApplication"

    android:showWhenLocked="true"

    android:turnScreenOn="true"

    android:resizeableActivity="true"

    android:launchMode="singleInstancePerTask">


    <property

        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"

        android:value="true"/>

    ...

</activity>

بعد ذلك، نفّذنا منطقًا لإطلاق مثيل جديد من التطبيق. في CahierHomeScreen.kt، عندما يختار المستخدم فتح ملاحظة في نافذة جديدة، ننشئ Intent جديدًا يتضمّن علامات محدّدة توجّه النظام بشأن كيفية التعامل مع إطلاق النشاط الجديد. يضمن الجمع بين FLAG_ACTIVITY_NEW_TASK وFLAG_ACTIVITY_MULTIPLE_TASK وFLAG_ACTIVITY_LAUNCH_ADJACENT فتح الملاحظة في نافذة جديدة منفصلة بجانب النافذة الحالية.

fun openNewWindow(activity: Activity?, note: Note) {

    val intent = Intent(activity, MainActivity::class.java)

    intent.putExtra(AppArgs.NOTE_TYPE_KEY, note.type)

    intent.putExtra(AppArgs.NOTE_ID_KEY, note.id)

    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or

        Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT


    activity?.startActivity(intent)

}

لإتاحة وضع النوافذ المتعددة، كان علينا إعلام النظام بأنّ التطبيق يتيح تغيير الحجم من خلال ضبط العنصر <activity> أو <application> في ملف البيان.

<activity

    android:name="com.example.cahier.MainActivity"

    android:resizeableActivity="true"

    ...>

</activity>

إنّ واجهة المستخدم نفسها التي تم إنشاؤها باستخدام مكتبة Material 3 التكيُّفية تتيح لها التكيّف بسلاسة في سيناريوهات النوافذ المتعددة، مثل وضع تقسيم الشاشة في Android. 

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

السحب والإفلات

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

  • استيراد سهل: يمكن للمستخدمين سحب الصور من تطبيقات أخرى، مثل متصفّح الويب أو معرض الصور أو مدير الملفات، وإفلاتها مباشرةً على لوحة ملاحظات. لإجراء ذلك، تستخدم Cahier أداة التعديل dragAndDropTarget لتحديد منطقة الإفلات، والتحقّق من المحتوى المتوافق (مثل image/*)، ومعالجة معرّف الموارد الموحّد الوارد.
  • المشاركة البسيطة: يمكن مشاركة المحتوى داخل Cahier بسهولة كما هو الحال مع المحتوى من التطبيقات الأخرى. يمكن للمستخدمين الضغط مع الاستمرار على صورة داخل ملاحظة نصية، أو الضغط مع الاستمرار على لوحة الرسم بأكملها لملاحظة رسم وتركيب صورة، ثم سحبها إلى تطبيق آخر.

نظرة معمّقة على التفاصيل الفنية: السحب من لوحة الرسم

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

بشكلٍ تلقائي، تم تصميم نظام إدخال المؤشر في Jetpack Compose بحيث تتلقّى دالة مركّبة شقيقة واحدة فقط —الأولى في ترتيب التعريف التي تتداخل مع موضع اللمس—الحدث. في حالة Cahier، نريد أن تتاح الفرصة لمنطق معالجة الإدخال بالسحب والإفلات ليتم تنفيذه واستهلاك المدخلات المحتملة قبل أن تستخدم الدالة المركّبة InProgressStrokes جميع المدخلات غير المستهلكة للرسم ثم تستهلك هذه المدخلات. إذا لم نرتب العناصر بالترتيب الصحيح، لن يرصد Box الإيماءة بالضغط مع الاستمرار لبدء السحب، ولن يتلقّى InProgressStrokes الإدخال للرسم.

لحلّ هذه المشكلة، أنشأنا معدِّلاً مخصّصًا pointerInputWithSiblingFallthrough، ووضعنا Box باستخدام هذا المعدِّل قبل InProgressStrokes في الرمز البرمجي القابل للإنشاء. هذه الأداة المساعدة هي برنامج تضمين بسيط لنظام pointerInput العادي، ولكن مع تغيير مهم واحد: فهي تتجاوز الدالة sharePointerInputWithSiblings() لعرض true. يطلب ذلك من إطار عمل Compose السماح بتمرير أحداث المؤشر إلى العناصر القابلة للإنشاء المتجاورة، حتى بعد استخدامها.

internal fun Modifier.pointerInputWithSiblingFallthrough(

    pointerInputEventHandler: PointerInputEventHandler

) = this then PointerInputSiblingFallthroughElement(pointerInputEventHandler)


private class PointerInputSiblingFallthroughModifierNode(

    pointerInputEventHandler: PointerInputEventHandler

) : PointerInputModifierNode, DelegatingNode() {


    var pointerInputEventHandler: PointerInputEventHandler

        get() = delegateNode.pointerInputEventHandler

        set(value) {

            delegateNode.pointerInputEventHandler = value

        }


    val delegateNode = delegate(

        SuspendingPointerInputModifierNode(pointerInputEventHandler)

    )


    override fun onPointerEvent(

        pointerEvent: PointerEvent,

        pass: PointerEventPass,

        bounds: IntSize

    ) {

        delegateNode.onPointerEvent(pointerEvent, pass, bounds)

    }


    override fun onCancelPointerInput() {

        delegateNode.onCancelPointerInput()

    }


    override fun sharePointerInputWithSiblings() = true

}


private data class PointerInputSiblingFallthroughElement(

    val pointerInputEventHandler: PointerInputEventHandler

) : ModifierNodeElement<PointerInputSiblingFallthroughModifierNode>() {


    override fun create() = PointerInputSiblingFallthroughModifierNode(pointerInputEventHandler)


    override fun update(node: PointerInputSiblingFallthroughModifierNode) {

        node.pointerInputEventHandler = pointerInputEventHandler

    }


    override fun InspectorInfo.inspectableProperties() {

        name = "pointerInputWithSiblingFallthrough"

        properties["pointerInputEventHandler"] = pointerInputEventHandler

    }

}

في ما يلي كيفية استخدامها في DrawingSurface:

Box(

    modifier = Modifier

        .fillMaxSize()

        // Our custom modifier enables this gesture to coexist with the drawing input.

        .pointerInputWithSiblingFallthrough {

            detectDragGesturesAfterLongPress(

                onDragStart = { onStartDrag() },

                onDrag = { _, _ -> /* consume drag events */ },

                onDragEnd = { /* No action needed */ }

            )

        }

) 

// The Ink API's composable for live drawing sits here as a sibling.

InProgressStrokes(...)

وبعد تنفيذ ذلك، يرصد النظام بشكل صحيح كلاً من ضربات الرسم وإيماءة السحب بالضغط مع الاستمرار في الوقت نفسه. بعد بدء عملية السحب، ننشئ معرّف موارد منتظمًا content:// قابلاً للمشاركة باستخدام FileProvider ونمرّر معرّف الموارد المنتظم إلى إطار عمل السحب والإفلات في النظام باستخدام view.startDragAndDrop(). يضمن هذا الحل تجربة المستخدم قوية وسهلة الاستخدام، ويوضّح كيفية التغلّب على تعارضات الإيماءات المعقّدة في واجهات المستخدم ذات الطبقات.

مبنى ذو تصميم معماري حديث

بالإضافة إلى واجهات برمجة التطبيقات المحدّدة، يوضّح Cahier أنماطًا معمارية مهمة لإنشاء تطبيقات عالية الجودة وقابلة للتكيّف.

طبقة العرض: Jetpack Compose والتكيّف

تم إنشاء طبقة العرض بالكامل باستخدام Jetpack Compose. كما ذكرنا، تستخدم Cahier مكتبة material3-adaptive لتكييف واجهة المستخدم. تتّبع إدارة الحالة نمط "تدفّق البيانات في اتجاه واحد" (UDF) الصارم، مع استخدام مثيلات ViewModel كحاويات بيانات تحتوي على معلومات الملاحظات وحالة واجهة المستخدم.

طبقة البيانات: المستودعات وRoom

بالنسبة إلى طبقة البيانات، تستخدم Cahier واجهة NoteRepository لتجريد جميع عمليات البيانات. يتيح خيار التصميم هذا للتطبيق التبديل بين مصدر بيانات محلي (Room) وخادم خلفي بعيد محتمل في المستقبل. إنّ تدفق البيانات لإجراء مثل تعديل ملاحظة بسيط:

  1. تؤدي واجهة مستخدم Jetpack Compose إلى تشغيل طريقة في ViewModel.
  2. يجلب ViewModel الملاحظة من NoteRepository ويتعامل مع منطقها ويعيد الملاحظة المعدَّلة إلى المستودع.
  3. يحفظ NoteRepository التعديل في قاعدة بيانات Room.

التوافق الشامل مع أجهزة الإدخال

لكي يكون التطبيق أداة إنتاجية حقيقية، يجب أن يتعامل مع مجموعة متنوعة من طرق الإدخال بسلاسة. تم تصميم تطبيق Cahier ليتوافق مع إرشادات الإدخال على الشاشات الكبيرة ويتيح ما يلي:

  • قلم الشاشة: يتضمّن هذا القسم معلومات عن الدمج مع Ink API، وميزة رفض لمس راحة اليد، والتسجيل في دور "الملاحظات"، وإدخال البيانات باستخدام قلم الشاشة في الحقول النصية، والوضع الغامر.
  • لوحة المفاتيح: تتوافق مع معظم اختصارات لوحة المفاتيح الشائعة ومجموعاتها (مثل ctrl+click وmeta+click) وتوفّر إشارة واضحة إلى موضع التركيز على لوحة المفاتيح.
  • الماوس ولوحة اللمس: إتاحة النقر بزر الماوس الأيمن وحالات التمرير.

يُعد توفير إمكانية التفاعل المتقدّم مع لوحة المفاتيح والماوس ولوحة اللمس من أهم الجوانب التي نركّز عليها لإجراء المزيد من التحسينات. 

البدء الآن

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

هل أنت مستعد لخوض التجربة؟

  • استكشاف الرمز: انتقِل إلى مستودع GitHub لاستكشاف قاعدة الرموز البرمجية لـ Cahier والاطّلاع على مبادئ التصميم أثناء العمل.
  • إنشاء تطبيقك الخاص: استخدِم Cahier كأساس لتطبيقك الخاص لتدوين الملاحظات أو ترميز المستندات أو التطبيقات الإبداعية.
  • المساهمة: نرحّب بمساهماتك. ساعِدنا في تحسين Cahier ليكون مرجعًا أفضل لمجتمع مطوّري تطبيقات Android.

يمكنك الاطّلاع على أدلة المطوّرين الرسمية والبدء في إنشاء الجيل التالي من تطبيقات الإنتاجية والإبداع اليوم. نحن متحمّسون لرؤية أعمالكم الإبداعية.

المؤلف:
متابعة القراءة