ترجمة
البرنامج التعليمي JavaFX: مقال FXML و SceneBuilder بواسطة Vojtech
Ruzicka .
كيفية إنشاء واجهة المستخدم الرسومية مع JavaFX باستخدام ترميز FXML و SceneBuilder.
جميع المشاركات في سلسلة JavaFX:
- JavaFX البرنامج التعليمي: الشروع في العمل
- JavaFX البرنامج التعليمي: مرحبا العالم!
- JavaFX البرنامج التعليمي: FXML و SceneBuilder
- JavaFX البرنامج التعليمي: تخطيطات الأساسية
- JavaFX تعليمي: تخطيطات متقدمة
- JavaFX البرنامج التعليمي: CSS التصميم
- JavaFX Weaver: دمج تطبيقات JavaFX و Spring التمهيد
الطريقة التقليدية
في المقالة السابقة ،
أنشأنا تطبيق Hello World بسيط .
مجرد تذكير - رمز يشبه هذا:
@Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello world Application"); primaryStage.setWidth(300); primaryStage.setHeight(200); InputStream iconStream = getClass().getResourceAsStream("/icon.png"); Image image = new Image(iconStream); primaryStage.getIcons().add(image); Label helloWorldLabel = new Label("Hello world!"); helloWorldLabel.setAlignment(Pos.CENTER); Scene primaryScene = new Scene(helloWorldLabel); primaryStage.setScene(primaryScene); primaryStage.show(); }
كما ترون ، يتم إنشاء واجهة المستخدم بالكامل في تعليمات Java البرمجية.
هذا مثال بسيط للغاية ، ولكن عندما يصبح تطبيقك أكثر تعقيدًا ، عندما تضطر إلى إدخال عدة مستويات من التنسيقات المتداخلة والعديد من المكونات ، يمكن أن يصبح فهم الكود الناتج أمرًا صعبًا للغاية. ومع ذلك ، هذا ليس كل شيء - في نفس الفئة يوجد كود مسؤول عن البنية والتأثيرات المرئية والسلوك في نفس الوقت.
من الواضح أن الفصل لا يتحمل مسؤولية واحدة. قارن هذا ، على سبيل المثال ، بواجهة الويب ، حيث تفصل كل صفحة المهام بوضوح:
- HTML هو الهيكل
- المغلق هو المؤثرات البصرية
- جافا سكريبت هو السلوك
تقديم FXML
من الواضح أن امتلاك كل الكود في مكان واحد ليس فكرة جيدة. تحتاج إلى تنظيمها بطريقة أو بأخرى بحيث يكون من الأسهل فهمها وجعلها أكثر قابلية للإدارة.
في الواقع ، هناك العديد من أنماط التصميم لهذا الغرض. عادةً ، ينتهي بك الأمر إلى خيار "عرض نموذج - مهما كان" - إنه يشبه "تحكم عرض النموذج" أو "مقدم عرض النموذج" أو "عرض نموذج ViewModel".
يمكنك قضاء ساعات في مناقشة إيجابيات وسلبيات الخيارات المختلفة - فلنقم بذلك هنا. الأهم من ذلك ، مع JavaFx يمكنك استخدام أي منها.
هذا ممكن لأنه بالإضافة إلى التصميم الإجرائي لواجهة المستخدم الخاصة بك ، يمكنك استخدام ترميز XML التعريفي.
اتضح أن الهيكل الهرمي لـ XML هو وسيلة رائعة لوصف التسلسل الهرمي للمكونات في واجهة المستخدم. HTML يعمل بشكل جيد ، أليس كذلك؟
يسمى تنسيق XML الخاص بـ JavaFX FXML. في ذلك يمكنك تحديد جميع مكونات التطبيق وخصائصها ، وكذلك ربطها بوحدة التحكم ، المسؤولة عن إدارة التفاعلات.
قم بتنزيل ملفات FXML
لذا ، كيف يمكننا تغيير طريقة الإطلاق الخاصة بنا للعمل مع FXML؟
FXMLLoader loader = new FXMLLoader(); URL xmlUrl = getClass().getResource("/mainScene.fxml"); loader.setLocation(xmlUrl); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show();
يمثل
الجذر هنا المكون الرئيسي لواجهة المستخدم الخاصة بك ، والمكونات الأخرى متداخلة فيه.
تحتوي طريقة
التحميل على قيمة إرجاع عامة ، بحيث يمكنك تحديد نوع معين ، وليس
الأصل . بعد ذلك ، يمكنك الوصول إلى الأساليب الموجهة للمكونات. ومع ذلك ، هذا يجعل التعليمات البرمجية الخاصة بك أكثر هشاشة. إذا قمت بتغيير نوع المكون الجذر في FXML ، فقد يتوقف التطبيق عن العمل في وقت التشغيل ، ولكن لن تحدث أية أخطاء أثناء الترجمة. هذا لأنه يوجد الآن عدم تطابق من النوع المعلن في FXML وفي أداة تحميل Java FXML.
إنشاء ملف FXML
نحن الآن نعرف كيفية تحميل ملف FXML ، لكن ما زلنا بحاجة إلى إنشائه. يجب أن يكون الملف بالامتداد .fxml. في مشروع Maven ، يمكنك وضع هذا الملف في مجلد الموارد أو يمكن لـ FXMLLoader تنزيله من عنوان URL خارجي.
بعد إنشاء الملف ، أدخل إعلان XML في السطر الأول:
<?xml version="1.0" encoding="UTF-8"?>
واردات
قبل إضافة مكونات فردية إلى ملف ، يجب التأكد من التعرف عليها بشكل صحيح. للقيام بذلك ، أضف عبارات الاستيراد. هذا مشابه جدًا للاستيراد في فئات Java. يمكنك استيراد فئات فردية أو استخدام أحرف البدل كالمعتاد. دعونا نلقي نظرة على مثال لقسم الاستيراد:
<?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
والخبر السار هو أنه بدلاً من إضافة جميع عبارات الاستيراد يدويًا ، ينبغي أن يساعدك IDE في إضافة الاستيراد مثل إضافتها إلى فئات Java.
مضيفا مكونات
الآن حان الوقت لإضافة بعض المكونات. في
مقال سابق ، علمنا أن كل مشهد يمكن أن يحتوي على مكون فرعي واحد فقط. للبدء ، دعونا نضيف تسمية بسيطة:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <Label>Hello World!</Label>
بالطبع ، لا يعد وضع العلامات كمكون جذري مثالًا واقعيًا للغاية. من الأفضل عادة استخدام نوع من أنواع التخطيط (تخطيط) ، وهو حاوية للعديد من المكونات وينظم ترتيبها. سنغطي التخطيطات لاحقًا في هذه السلسلة ، ولكن الآن ، دعونا نستخدم VBox البسيط الذي يضع أطفاله رأسيًا فوق بعضهم البعض.
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.Button?> <VBox> <Label text="Hello world!"/> <Label text="This is a simple demo application."/> <Button text="Click me!"/> </VBox>
مساحة الاسم FX
هناك بضعة عناصر وسمات FXML غير متوفرة بشكل افتراضي. تحتاج إلى إضافة Namespace FXML لإتاحتها. يجب إضافته إلى مكون الجذر:
<?xml version="1.0" encoding="UTF-8"?> ... <VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"> ... </VBox>
يمكنك الآن استخدام العناصر الجديدة من مساحة اسم الفوركس. دعونا نحاول إضافة معرّفات فريدة لمكوناتنا:
<Label fx:id="mainTitle" text="Hello world!"/>
تعد سمة
fx: id معرفًا فريدًا لأحد المكونات التي يمكن استخدامها للإشارة إلى مكون من أجزاء أخرى من FXML لدينا وحتى من وحدة التحكم الخاصة بنا.
مخطوطات
طلبنا لا يزال ثابتا. هناك العديد من التسميات وزر ، لكن التطبيق لا يفعل أي شيء ديناميكي.
دعنا نرد على نقرة زرنا ونغير العنوان من "Click me!" إلى "Click me again!".
أول شيء فعله هو إضافة معالج حدث onAction لزرنا.
<Button fx:id="mainButton" text="Click me!" onAction="buttonClicked()"/>
انتبه إلى fx: id ، هذا هو المعرف الذي سيتم استخدامه لاحقًا للإشارة إلى الزر.
تحتاج الآن إلى توفير وظيفة سيتم استدعاؤها للتعامل مع الحدث. يمكن تعريفه داخل علامة fx: script. الشيء المهم هو أنه يمكنك استخدام لغات مختلفة لكتابة برنامج نصي أو JavaScript أو Groovy أو Clojure. دعونا نلقي نظرة على مثال في JavaScript:

لاحظ أننا نشير إلى مكون الزر الخاص بنا باستخدام معرف الزر الرئيسي ، والذي تم إعلانه على النحو التالي:
fx:id = "mainButton"
يجب أيضًا الإشارة إلى لغة البرمجة النصية التي تستخدمها في ملف FXML:
<?language javascript?>
لنلقي نظرة على النص الكامل للمثال:

هل يجب علي استخدام هذا؟
يوضح المثال أعلاه كيفية الرجوع إلى المكونات باستخدام
fx: id وكيفية إضافة سلوك بسيط باستخدام برنامج نصي JavaScript. هل هذا حقا ما يجب أن تفعله؟
الجواب في معظم الحالات لا. هناك العديد من المشاكل مع هذا النهج. سبب تقديم FXML هو فصل المصالح - لفصل بنية وسلوك واجهة المستخدم. في هذا البرنامج النصي ، عاد السلوك المدمج مع بنية واجهة المستخدم مرة أخرى. علاوة على ذلك ، نظرًا لأننا لم نعد نعمل مع Java code ، ولكن مع XML ، فقد فقدت جميع عمليات التحقق من الشفرة في وقت الترجمة وسلامة النوع. الآن سيتم الكشف عن جميع المشاكل في التطبيق في وقت التشغيل ، وليس في وقت الترجمة. أصبح التطبيق هشا للغاية وعرضة للخطأ.
إضافة وحدة تحكم
إذن ما الذي يمكن فعله للحصول على فصل واضح للمصالح؟ يمكنك ربط وحدة التحكم بملف FXML الخاص بنا. وحدة التحكم هي فئة Java مسؤولة عن معالجة السلوك وتفاعل المستخدم في أحد التطبيقات. وبهذه الطريقة يمكنك إرجاع سلامة النوع والتحقق من وقت الترجمة.
جهاز التحكم هو POJO ، ولا ينبغي أن يمتد أو ينفذ أي شيء ، ولا ينبغي أن يحتوي على أي تعليقات توضيحية خاصة.
كيف يمكنني ربط فئة تحكم مع FXML لدينا؟ هناك أساسا خياران.
في جافا
يمكنك إنشاء مثيل لوحدة التحكم بنفسك أو استخدام أي طرق أخرى لإنشاء مثيل ، مثل حقن التبعية. ثم فقط قم بتنزيل
FXMLLoader الخاص بك.
FXMLLoader loader = new FXMLLoader(); loader.setController(new MainSceneController());
في FXML
يمكنك تحديد فئة وحدة التحكم الخاصة بك
كسمة fx: controller ، والتي يجب أن تكون موجودة في مكون الجذر.
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> ... </VBox>
إذا أعلنت فئة وحدة التحكم في FXML ، فسيتم إنشاؤها تلقائيًا من أجلك. يحتوي هذا النهج على قيود واحدة - في وحدة التحكم ، تحتاج إلى إنشاء مُنشئ بدون وسيطات لتسهيل إنشاء مثيل جديد من فئة وحدة التحكم.
للوصول إلى مثيل وحدة تحكم تم إنشاؤها تلقائيًا ، يمكنك استخدام أداة تحميل FXML:
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/mainScene.fxml")); MainSceneController controller = loader.getController();
استدعاء أساليب تحكم
الآن بعد أن أصبح لديك جهاز تحكم ، يمكنك حذف البرنامج النصي وتنفيذ منطق الضغط على الأزرار مباشرةً في وحدة التحكم:
public class MainSceneController { public void buttonClicked() { System.out.println("Button clicked!"); } }
الخطوة التالية هي تسجيل مكالمة هذه الطريقة
كمعالج أحداث
onAction لزرنا . للإشارة إلى طرق من وحدة التحكم الخاصة بنا ، نحتاج إلى استخدام علامة
# أمام اسم الطريقة:
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> <Label fx:id="mainTitle" text="Hello world!"/> <Label fx:id="subTitle" text="This is a simple demo application."/> <Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/> </VBox>
عند النقر فوق زر ، فإنه يستدعي الأسلوب
MainSceneController.buttonClicked () . ضع في اعتبارك أن هذا يعمل فقط إذا تم الإعلان عن الطريقة العامة. إذا كان معدل الوصول أكثر صرامة ، فيجب عليك إضافة تعليق توضيحي للطريقة باستخدام التعليق التوضيحي
FXML .
@FXML private void buttonClicked() { System.out.println("Button clicked!"); }
تضمين المكونات في وحدة تحكم
حتى الآن ، نحن فقط طباعة إلى وحدة التحكم. ماذا لو أردنا تغيير نص الزر الخاص بنا إلى "
انقر لي مرة أخرى "
مرة أخرى ؟ كيف يمكننا الحصول على روابط لمكونات في وحدة التحكم لدينا؟
لحسن الحظ ، هذا سهل. تذكر هذه
العملات الأجنبية: سمات
الهوية ؟
<Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/>
تحاول JavaFX مطابقة المكونات تلقائيًا مع
fx: id مع الحقول المعرفة في وحدة التحكم الخاصة بك بنفس الاسم.
لنفترض أن لدينا زرًا موضحًا أعلاه
fx:id="mainButton"
يحاول JavaFX حقن كائن الزر في وحدة التحكم الخاصة بك في حقل يسمى
mainButton :
public class MainSceneController {
كما هو الحال في الطرق السابقة ، يجب أن تكون
حقولك علنية أو مشروحة
FXML .
الآن وقد أصبح لدينا رابط إلى زرنا ، يمكننا بسهولة تغيير نصه:
public class MainSceneController { @FXML private Button mainButton; @FXML private void buttonClicked() { mainButton.setText("Click me again!"); } }
باني المشهد
قد تكون كتابة بنية واجهة المستخدم الرسومية في XML أكثر طبيعية من لغة Java (خاصة إذا كنت معتادًا على HTML). ومع ذلك ، فإنه لا يزال غير مريحة للغاية. والخبر السار هو أن هناك أداة رسمية تسمى Scene Builder ستساعدك على إنشاء واجهة المستخدم. باختصار ، هذا محرر رسومي لواجهة المستخدم الرسومية الخاصة بك.

المحرر لديه ثلاثة مجالات رئيسية هي:
- يعرض الجزء الأيسر المكونات المتاحة التي يمكن سحبها إلى الجزء الأوسط. كما أنه يحتوي على تسلسل هرمي لجميع المكونات في واجهة المستخدم الخاصة بك ، حتى تتمكن من التنقل بسهولة.
- الجزء الأوسط هو عرض التطبيق الخاص بك على أساس ملف FXML الخاص بك.
- على اليمين هو المفتش المكون الحالي. هنا يمكنك تحرير خصائص مختلفة للمكون الحالي المحدد. يتم عرض أي مكون يتم تحديده في منتصف التسلسل الهرمي في المفتش.
مستقل
يمكن
تنزيل Scene Builder كتطبيق مستقل يمكن استخدامه لتحرير ملفات FXML.
التكامل مع IntelliJ IDEA
بدلاً من ذلك ، يقدم Scene Builder التكامل مع IDE.
في IntelliJ IDEA ، يمكنك النقر بزر الماوس الأيمن فوق أي ملف FXML ثم تحديد خيار القائمة فتح في SceneBuilder.
بدلاً من ذلك ، يدمج IntelliJ IDEA SceneBuilder مباشرة في IDE. إذا قمت بفتح ملف FXML في IDEA ، فستظهر علامتا تبويب في أسفل الشاشة
لكل ملف FXML ، يمكنك التبديل بسهولة بين تحرير ملف FXML مباشرة أو من خلال SceneBuilder.

في IntelliJ IDEA ، يمكنك تكوين موقع SceneBuilder القابل للتنفيذ:
Settings → Languages & Frameworks → JavaFX → Path to SceneBuilder
ما التالي
في المنشور التالي في سلسلتنا ، سنناقش بعض التخطيطات الأساسية التي يمكن استخدامها لتنظيم مكونات تطبيق واجهة المستخدم الرسومية في JavaFX.