مقدمة إلى FXML
مقدمة إلى FXML
حتى الآن في هذا البرنامج التعليمي، كنتَ تبني كل واجهة مستخدم بلغة Java الخالصة: تنشئ العقد، تضبط الخصائص، تضيف العناصر الفرعية، وتربط المستمعين — كل ذلك داخل نفس الفئة. هذا النهج يصلح للأمثلة الصغيرة، لكنه يصبح سريعًا غير قابل للإدارة. دالة start() التي تمتد 200 سطر وتخلط منطق التخطيط مع منطق الأعمال يصعب قراءتها، ولا يمكن تسليمها لمصمم، وتكاد تكون كابوسًا في الاختبار.
FXML هو حل JavaFX لهذه المشكلة. إنه لغة ترميز قائمة على XML تتيح لك الإعلان عن واجهة المستخدم في ملف منفصل تمامًا مستقل عن كود Java الذي يديرها. فكّر فيه على أنه مكافئ JavaFX لـHTML في صفحات الويب: الترميز يصف البنية، وفئة Java تتولى السلوك.
لماذا FXML موجود
ثمة ثلاث فوائد ملموسة تدفع كل مشروع JavaFX احترافي إلى استخدام FXML:
- فصل الاهتمامات — ملف التخطيط (
.fxml) هيكلي بحت. يمكن لمصمم أو أداة مساعدة تعديله دون لمس أي فئة Java. - دعم الأدوات — برنامج Scene Builder، مصمم واجهة المستخدم بالسحب والإفلات، يقرأ FXML ويكتبه بشكل أصيل. تسحب زرًا
Buttonعلى اللوحة ويكتب Scene Builder XML المقابل نيابةً عنك. - سهولة القراءة — تخطيط متداخل يستغرق 80 سطرًا من Java يتحول إلى 30 سطرًا من XML منسّق، والتسلسل الهرمي واضح بصريًا.
FXMLLoader الذي يُحلّل XML، ويُنشئ العقد، ويضبط قيم الخصائص، ويحقنها في فئة التحكم التي تُزوّده بها. هذا يعني أنك تستطيع تحديث التخطيط دون إعادة تصريف كود Java — مفيد جدًا خلال مرحلة تكرار تصميم واجهة المستخدم.
تشريح ملف FXML
ملف FXML هو مستند XML قياسي. العنصر الجذري هو دائمًا تقريبًا جزء تخطيط أو عقدة حاوية. الخصائص (attributes) تُعيَّن مباشرة إلى خصائص عقدة JavaFX. إليك أبسط مثال ممكن — VBox يحتوي على Label وButton:
لاحظ عدة أشياء:
<?import ...?>تعليمات معالجة تعمل تمامًا مثل عبارات الاستيراد في Java. كل فئة تُشير إليها في الترميز يجب استيرادها بهذه الطريقة.xmlns:fx="http://javafx.com/fxml"يُعلن عن مساحة الاسمfx:التي تتيح خصائص FXML الخاصة مثلfx:idوfx:controller.fx:controllerيُخبرFXMLLoaderبفئة Java التي هي وحدة تحكم هذا الملف. يُنشئها المحمّل تلقائيًا.onAction="#handleClick"— البادئة#تعني "ابحث عن دالة اسمهاhandleClickفي وحدة التحكم".
تحميل FXML باستخدام FXMLLoader
الجسر بين ملف FXML وتطبيقك المُشغَّل هو javafx.fxml.FXMLLoader. تستدعيه مرة واحدة في Application.start():
تُعيد loader.load() العقدة الجذرية من رسم بياني المشهد المُعلَن في ملف FXML — في هذه الحالة VBox. النوع المُعاد يُلقى عادةً إلى Parent أو النوع الملموس حسب حاجتك.
src/main/resources (بنية Maven/Gradle)، في حزمة تعكس شجرة مصدر Java. مثلًا إذا كانت وحدة التحكم هي com.example.HelloController ضع FXML في src/main/resources/fxml/hello.fxml وحمّله بـgetClass().getResource("/fxml/hello.fxml"). وضعها على classpath — لا على نظام الملفات مباشرةً — يضمن عمل المسار بشكل متطابق في بيئة التطوير وفي JAR مجمّع وفي صورة أصيلة (native image).
ضبط الخصائص في FXML
يمكن ضبط أي خاصية JavaFX لها setter عام كخاصية XML. الأنواع البدائية تُحوَّل تلقائيًا؛ الألوان والتعدادات تُستخرج من تمثيلاتها النصية. الكائنات المعقدة تُعبَّر عنها كعناصر فرعية متداخلة:
الخاصية fx:id خاصة: تُخبر FXMLLoader بحقن هذه العقدة في حقل مُعلَّق بـ@FXML في وحدة التحكم. هذا الربط موضوع الدرس التالي؛ في الوقت الحالي اعرف فقط اتفاقية التسمية: يجب أن يطابق fx:id اسم الحقل في Java تمامًا.
مساحة اسم FXML والخصائص الخاصة
توفر مساحة الاسم fx: مجموعة صغيرة من الخصائص القوية:
fx:id— يُسمّي عقدة لتتمكن وحدة التحكم من الإشارة إليها.fx:controller— يُحدد فئة وحدة التحكم (يُضبط مرة واحدة على العنصر الجذري).fx:include— يُضمّن ملف FXML آخر مما يُتيح تخطيطات معيارية.fx:define— يُعلن كائنات خارج رسم بياني المشهد (مفيد للموارد المشتركة).fx:root— يُتيح نمط المكون المخصص حيث تكون الفئة هي نفسها عقدة الجذر ووحدة التحكم.
صيغة الخاصية المباشرة مقابل صيغة العنصر
يدعم JavaFX FXML صيغتين لضبط القيم. صيغة الخاصية (attribute) موجزة وتصلح للسلاسل النصية والأنواع البسيطة:
صيغة عنصر الخاصية تُستخدم للقيم التي هي كائنات بحد ذاتها كـInsets وFont وObservableList:
ستجد الصيغتين معًا في ملفات FXML الحقيقية. افضّل صيغة الخاصية حين تكون قابلة للقراءة بوضوح؛ وارجع إلى صيغة العنصر لرسوم بيانية الكائنات المعقدة.
getResource(). إذا بدأ المسار بـ/ فهو مطلق من جذر classpath. بدون الشرطة المائلة الأولى يكون نسبيًا إلى حزمة الفئة المُستدعِية. مسار خاطئ يجعل FXMLLoader.load() يرمي NullPointerException في وقت التشغيل — ليس خطأ تصريف — لذا تحقق دائمًا من أن الملف موجود على classpath وأن سلسلة المسار صحيحة.
بنية مشروع عملي بسيط
مشروع Maven نموذجي يستخدم FXML يبدو هكذا:
بناء Maven ينسخ كل شيء تحت resources/ إلى جذر classpath، لذا يُحلَّل getClass().getResource("/fxml/hello.fxml") بشكل صحيح في وقت التشغيل.
الخلاصة
يفصل FXML إعلان واجهة المستخدم عن منطق التطبيق بالتعبير عن رسم بياني المشهد بلغة XML. تستورد الفئات بـ<?import?>، وتربط وحدة تحكم بـfx:controller، وتُسمّي العقد بـfx:id، وتربط معالجات الأحداث بـ#methodName. يربط FXMLLoader الترميز وبيئة Java: يُحلّل الملف، يبني شجرة العقد، ويُسلّم العقدة الجذرية جاهزةً للوضع في Scene. هذا الفصل النظيف هو أساس كل تطبيق JavaFX احترافي — وبرنامج Scene Builder، المحرر المرئي بالسحب والإفلات، يعمل بالكامل فوقه.