أساسيات JavaFX ورسم المشهد

نافذتك الأولى في JavaFX

18 دقيقة الدرس 5 من 12

نافذتك الأولى في JavaFX

قدّمت الدروس السابقة فئة Application ودورة الحياة وشجرة العقد التي تُشكّل المشهد. الآن تجمع كل ذلك في نافذة حقيقية تعمل ويستطيع المستخدم التفاعل معها. بنهاية هذا الدرس سيكون لديك تطبيق JavaFX يعرض زرًا ويستجيب حين ينقر عليه المستخدم — أصغر برنامج واجهة رسومية ذي معنى يمكنك كتابته.

ما يحتاجه وقت التشغيل منك

تشترط JavaFX امتداد فئة واحدة تحديدًا من javafx.application.Application وتجاوز الدالة start(Stage primaryStage). هذه الدالة هي نقطة دخولك إلى عالم الواجهات الرسومية. كل ما يلمس شجرة المشهد يجب أن يحدث هنا أو على خيط تطبيق JavaFX (يُغطّى بالكامل في الدرس التالي). أما دالة main فتستدعي launch(args) فقط لتسليم التحكم إلى الأداة.

على Java 11 وما بعدها يمكنك حذف دالة main كليًا عند تهيئة نظام الوحدات — لكن إبقاءها أكثر أمانًا وتوافقًا عبر أدوات البناء المختلفة.

بناء المشهد خطوة بخطوة

تُبنى نافذة JavaFX من الأسفل إلى الأعلى: تُنشئ عناصر التحكم أولًا، ثم تجمعها في حاوية تخطيط، ثم تلفّها في Scene، وأخيرًا تضع المشهد على Stage. إليك البرنامج الكامل:

import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class HelloWindow extends Application { @Override public void start(Stage primaryStage) { // 1. إنشاء عناصر التحكم Label label = new Label("Click the button below"); Button button = new Button("Say Hello"); // 2. ربط معالج الحدث (lambda — يُغطّى في الدرس 8) button.setOnAction(event -> label.setText("Hello, JavaFX!")); // 3. ترتيب العناصر في حاوية تخطيط عمودية VBox root = new VBox(12); // 12 بكسل مسافة بين العناصر root.setStyle("-fx-padding: 24; -fx-alignment: center;"); root.getChildren().addAll(label, button); // 4. تغليف التخطيط في مشهد (عرض=320، ارتفاع=200) Scene scene = new Scene(root, 320, 200); // 5. ضبط الخشبة وعرضها primaryStage.setTitle("Hello JavaFX"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }

لنتجوّل عبر كل قرار في هذا البرنامج المكوّن من 30 سطرًا.

الخطوة 1 — عناصر التحكم مجرد كائنات

كلٌّ من Label وButton فئات فرعية من Node. تُنشئها بـnew، وتضبط خصائصها بالمُعيِّنات (setters)، وتضيفها إلى حاوية أب. لا يوجد XML ولا ملف وصفي ولا معالج IDE مطلوب — هو بناء كائنات Java خالص.

الخطوة 2 — الاستجابة لحدث باستخدام Lambda

السطر button.setOnAction(event -> label.setText("Hello, JavaFX!")) هو جوهر برمجة الواجهات الرسومية: ربط فعل المستخدم بقطعة كود. تقبل setOnAction أي EventHandler<ActionEvent>. ولأن هذه الواجهة وظيفية (دالة مجردة واحدة)، تعمل lambda بشكل مثالي. عند النقر على الزر يستدعي الإطار هذه lambda على خيط تطبيق JavaFX، وهو أيضًا الخيط المالك لشجرة المشهد — لذا تحديث label مباشرةً آمن هنا.

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

الخطوة 3 — حاويات التخطيط تضع العناصر في أماكنها

يرصّ VBox عناصره الأبناء عموديًا مع مسافة قابلة للضبط. الوسيطة الأولى لمنشئه هي المسافة بالبكسل بين العناصر. يمكنك أيضًا ضبط التبطين والمحاذاة عبر CSS المضمّنة (أسماء الخصائص -fx-*) أو عبر واجهة Java البرمجية. من الحاويات الشائعة الأخرى التي ستلتقيها قريبًا: HBox (أفقي)، وBorderPane (شمال/جنوب/شرق/غرب/مركز)، وGridPane (صفوف وأعمدة).

تُضيف الأبناء بـgetChildren().addAll(...). تُعيد دالة getChildren() قائمة ObservableList<Node> — إضافة العناصر أو إزالتها منها تُفعّل تلقائيًا دورة تخطيط وإعادة رسم.

الخطوة 4 — المشهد يمتلك عقدة جذر وحجمًا

ينشئ new Scene(root, 320, 200) مشهدًا جذره هو VBox وحجمه الأولي 320×200 بكسل. كل عنصر تحكم داخل root هو الآن جزء من شجرة عقد هذا المشهد. كائن Scene هو أيضًا المكان الذي تُرفق فيه أوراق الأنماط لاحقًا:

scene.getStylesheets().add( getClass().getResource("/styles/app.css").toExternalForm() );

الخطوة 5 — الخشبة هي نافذة نظام التشغيل

يوفّر الإطار primaryStage حين يستدعي start. تضبط عنوانًا وتُرفق المشهد وتستدعي show(). هذا كل ما يلزم لعرض نافذة على الشاشة. الخشبة هي أيضًا المكان الذي تتحكم فيه في قابلية تغيير الحجم والوضع ملء الشاشة والأيقونات والأبعاد الدنيا/القصوى:

primaryStage.setResizable(false); primaryStage.setMinWidth(200); primaryStage.getIcons().add( new Image(getClass().getResourceAsStream("/icon.png")) );

الرسم البياني الكامل للكائنات

بعد show() يبدو الرسم البياني الحي للكائنات هكذا:

Stage (نافذة نظام التشغيل) └── Scene (320 × 200) └── VBox [العقدة الجذر] ├── Label "Click the button below" └── Button "Say Hello"

هذه شجرة المشهد في العمل. حين تنقر الزر تتغير خاصية نص التسمية؛ تلاحظ محرك الرسم في JavaFX التغيير (لأن Text قابل للمراقبة) ويُعيد رسم المنطقة المتأثرة فقط في النبضة التالية — دون استدعاءات إعادة رسم يدوية.

سلوك الإغلاق

بشكل افتراضي، إغلاق الخشبة الرئيسية يُنهي التطبيق عبر Platform.exit(). إذا كان لديك خشبات متعددة أو تحتاج تنفيذ كود تنظيف، يمكنك تجاوز stop() في فئتك الفرعية من Application:

@Override public void stop() throws Exception { // تحرير الموارد وإغلاق اتصالات قاعدة البيانات وما إلى ذلك System.out.println("Application shutting down"); }
لا تستدعِ System.exit() أبدًا لإغلاق تطبيق JavaFX. تتجاوز هذه الدالة stop() وتتخطى أي تنظيف سجّلته. استخدم Platform.exit() بدلًا من ذلك، أو أغلق النافذة ببساطة ودع السلوك الافتراضي يعمل.

تشغيل التطبيق

مع توفّر JavaFX SDK في مسار الوحدات (مثلًا عبر تبعية Maven/Gradle org.openjfx:javafx-controls)، صرّف التطبيق وشغّله كفئة Java عادية. يدعم كل من IntelliJ IDEA وVS Code مشاريع JavaFX من خلال تكاملهما مع Maven/Gradle — لا تحتاج إضافة منفصلة لهذا الإعداد الأساسي.

الخلاصة

تحتاج نافذة JavaFX العاملة إلى خمسة أشياء: عناصر تحكم مُنشأة ككائنات، ومعالج حدث يربط أفعال المستخدم بالكود، وحاوية تخطيط تجمع العناصر، ومشهد Scene يمنح الشجرة حجمًا، وخشبة Stage تعرضها على الشاشة. يستدعي وقت التشغيل start() نيابةً عنك — مهمتك هي بناء الشجرة واستدعاء show(). هذا النمط يتوسّع دون تغيير من هذا البرنامج المكوّن من 30 سطرًا إلى تطبيق مؤسسي متعدد الشاشات.