مشروع: تطبيق JavaFX متكامل
مشروع: تطبيق JavaFX متكامل
يجمع هذا الدرس الأخير من البرنامج التعليمي كل مفهوم تناولناه حتى الآن — دورة حياة Application، ورسم المشهد (scene graph)، والتخطيطات، وعناصر التحكم، والأشكال، والألوان، والخطوط، ومعالجة الأحداث، وسلامة الخيوط — في تطبيق سطح مكتب واحد يعمل بالكامل. بدلًا من مقتطفات معزولة، ستبني محوّل وحدات يحوّل الكيلومترات إلى أميال والسيلزيوس إلى فهرنهايت، مع واجهة مستخدم مكوّنة من قسمَين، واختصارات لوحة مفاتيح، وتسمية نتيجة مباشرة تتحدّث أثناء كتابة المستخدم.
بحلول نهاية هذا الدرس سيكون لديك برنامج قابل للتشغيل يُظهر الأنماط التي ستستخدمها في كل مشروع JavaFX حقيقي.
ما يفعله التطبيق
- لوحتا تحويل جنبًا إلى جنب داخل
HBox. - كل لوحة تحتوي على
TextFieldمُوسَم للإدخال وتسميةLabelللنتيجة بنمط مميز. - تتحدّث النتائج فورًا أثناء الكتابة، بالاعتماد على
ChangeListenerعلى خاصية النص. - يعرض إدخال غير صالح أو فارغ شرطة "—" بدلًا من التعطّل.
- زر Reset يمسح الحقلين؛ والضغط على Enter في أي حقل ينفّذ نفس الإجراء.
- تمتلك
Stageالرئيسية حدًّا أدنى للحجم وعنوانًا مخصّصًا.
هيكل المشروع
كل شيء موجود في فئة واحدة للتركيز على JavaFX. في مشروع حقيقي ستفصل كود بناء الواجهة إلى متحكّم (controller) أو تستخدم FXML، لكن التطبيق ذو الملف الواحد هو نقطة البداية المثالية لفهم التوصيلات الكاملة.
أضف وحدات JavaFX SDK إلى ملف Maven pom.xml الخاص بك (أو مسار الوحدة إذا كنت تشغّل من سطر الأوامر):
كود المصدر الكامل
شرح القرارات المحورية
دالة مساعدة لبناء اللوحات
تقبل الدالة buildPanel() وسيطًا من نوع Runnable حتى يؤدي الضغط على Enter في أي حقل إلى استدعاء دالة التحويل المقابلة. هذا يتجنّب تكرار الكود مع إبقاء منطق كل لوحة منفصلًا. Runnable هي دالة لامدا: () -> convertKmToMiles(). لاحظ أن الحقل وتسمية Label للنتيجة تُمرَّران كمعاملات حتى يحتفظ الكود المُستدعي (في start()) بمراجع لهما للتحديثات المباشرة ولعملية الإعادة.
التحديثات المباشرة عبر ChangeListener
إرفاق ChangeListener بـ field.textProperty() يعني إعادة الحساب عند كل ضغطة مفتاح. يستقبل المستمع ثلاثة وسيطات — الكائن القابل للمراقبة، القيمة القديمة، والقيمة الجديدة — لكن هذا الكود يهتم فقط بالأثر الجانبي (استدعاء التحويل)، لذا يمكن تجاهل الثلاثة.
setOnAction للتحديثات المباشرة؟ setOnAction على TextField يُطلَق فقط عند ضغط المستخدم على Enter. أما ChangeListener على خاصية النص فيُطلَق عند كل تغيير في الأحرف — وهذا ما يمنح التجربة "المباشرة". كلاهما مُوصَّل هنا: Enter ينفّذ نفس التحويل للمستخدمين الذين يعتمدون سير عمل لوحة المفاتيح.
معالجة الأخطاء بأسلوب لطيف
كتلة catch (NumberFormatException ex) لا تعرض حوار خطأ ولا تطبع تتبع المكدس. بدلًا من ذلك تُعيد تسمية النتيجة إلى "—" وتغيّر لونها إلى الرمادي. هذا هو تجربة المستخدم الصحيحة لحقل يتحدّث فورًا — فالمستخدم في منتصف الكتابة ولم يرتكب خطأً بعد؛ رقمه غير مكتمل فحسب.
سلامة الخيوط
كل الكود الموجود في start() ومعالجات الأحداث يعمل على خيط تطبيق JavaFX. لا توجد عمليات خلفية هنا، لذا لا حاجة لـ Platform.runLater(). إذا وسّعت هذا التطبيق لاحقًا لجلب سعر صرف من API ويب، ستُجري طلب الشبكة على خيط خلفي وتحدّث التسمية داخل Platform.runLater() — تمامًا كما تناولنا في درس الخيوط.
CSS المُضمَّن مقابل ورقة الأنماط
التنسيق يُنجَز باستدعاءات setStyle() بدلًا من ملف CSS خارجي. هذا مقبول لمشروع صغير مستقل ويجعل الكود ذاتي الاحتواء. لأي شيء أكبر، يُفضَّل ملف styles.css يُحمَّل بـ scene.getStylesheets().add(...) — فهو يفصل الاهتمامات ويُتيح الثيمات.
setMinWidth / setMinHeight على Stage لمنع المستخدم من تصغير النافذة إلى حد تتداخل فيه عناصر التحكم. سيحترم JavaFX الحجم الأدنى أثناء تغيير الحجم التفاعلي بينما يظل قادرًا على ضبط الحجم الأولي بحرية.
توسيع المشروع
إليك الخطوات التالية الطبيعية التي يقدّم كل منها مفهومًا جديدًا في JavaFX:
- إضافة
ComboBoxلاختيار الوحدة — يُعرّفك على ربط القيم القابلة للمراقبة وعناصر تحكمChoiceBox/ComboBox. - تحميل ورقة أنماط خارجية — ينقل قواعد التنسيق إلى
converter.cssويُعلّمك نظام CSS في JavaFX. - إضافة سجل تحويل بـ
ListView— يُعرّفك علىObservableListوFXCollections. - جلب سعر عملة مباشر — يُجبرك على استخدام
ThreadأوTaskخلفي وPlatform.runLater().
الخلاصة
هذا المشروع صغير عمدًا — كل سطر فيه يخدم غرضًا تعليميًا. لقد رأيت كيف تتناسب نقطة الدخول Application، ورسم المشهد، وحاويات التخطيط، وعناصر التحكم، والخصائص، والمستمعون، ومعالجات الأحداث معًا في كل متماسك. الأنماط التي استخدمتها هنا — بناء المشهد في start()، وربط المستمعين بالخصائص القابلة للمراقبة، وفصل منطق التحويل عن توصيلات الواجهة، ومعالجة الإدخال الخاطئ بأسلوب لطيف — هي نفسها التي ستطبّقها في تطبيقات بأي حجم. من هنا، كل موضوع جديد في JavaFX (FXML، والربطات، والحركات، وعناصر التحكم المخصّصة) هو امتداد لهذا الأساس.