عناصر JavaFX والتخطيطات وFXML

أجزاء التخطيط: HBox و VBox

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

أجزاء التخطيط: HBox و VBox

كل تطبيق JavaFX يتجاوز نافذةً بسيطة تحتوي على زر واحد يحتاج إلى ترتيب عقد متعددة على الشاشة بطريقة منتظمة. يحل JavaFX هذه المشكلة من خلال أجزاء التخطيط (Layout Panes) — حاويات تقيس أبناءها وتضعهم تلقائيًا وفق خوارزمية ثابتة. أبسط جزئين وأكثرهما استخدامًا هما HBox الذي يرتّب العقد أفقيًا، وVBox الذي يكدّسها رأسيًا. إتقان هاذين الجزءين سيُمكّنك من تنفيذ الغالبية العظمى من تخطيطات النماذج وأشرطة الأدوات والأجزاء الجانبية في التطبيقات الحقيقية.

لماذا لا تستخدم المواضع المطلقة؟

يوفّر JavaFX فعلًا AnchorPane وPane الخام حيث يمكنك ضبط layoutX/layoutY يدويًا. تجنّب هذا الأسلوب في العمل العام على واجهات المستخدم. الإحداثيات المُدمجة في الكود تنكسر فور قيام المستخدم بتغيير حجم النافذة، أو تغيير حجم خط النظام، أو تبديل اللغة (النصوص العربية أعرض). أجزاء التخطيط تُعيد حساب المواضع عند كل تغيير في الحجم تلقائيًا — هذا هو هدفها الأساسي.

HBox: تخطيط الصف الأفقي

يضع HBox أبناءه واحدًا تلو الآخر من اليسار إلى اليمين. يحصل كل ابن على عرضه المفضّل؛ ويكبر الـHBox ليناسبهم. يمكنك التحكم في الفجوة بين الأبناء وكيفية محاذاتهم داخل الصف.

import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class HBoxDemo extends Application { @Override public void start(Stage stage) { Label nameLabel = new Label("Name:"); TextField nameField = new TextField(); Button saveBtn = new Button("Save"); // spacing = الفجوة بالبكسل بين كل ابن HBox row = new HBox(10, nameLabel, nameField, saveBtn); // محاذاة جميع الأبناء عموديًا في وسط الصف row.setAlignment(Pos.CENTER_LEFT); // إضافة مساحة حول الـHBox نفسه row.setPadding(new Insets(12)); stage.setScene(new Scene(row, 400, 60)); stage.setTitle("HBox Demo"); stage.show(); } public static void main(String[] args) { launch(args); } }

ثلاثة أمور ملحوظة هنا:

  • المُنشئ الذي يقبل عناصر متغيرة new HBox(spacing, child1, child2, ...) هو الأسلوب الأكثر إيجازًا لبناء صف — دون الحاجة إلى استدعاء getChildren().addAll(...) منفصل.
  • setAlignment(Pos.CENTER_LEFT) يتحكّم في موضع صف الأبناء داخل الـHBox عندما يتوفر ارتفاع زائد.
  • setPadding(new Insets(top, right, bottom, left)) يُضيف مساحة داخلية. new Insets(12) يطبّق نفس القيمة على الجوانب الأربعة.

جعل ابن يتوسع ليملأ المساحة المتبقية

حقل TextField في المثال أعلاه يستخدم عرضه المفضل فقط. في شريط بحث حقيقي أو صف نموذج تريد منه أن يتوسع ليملأ المساحة المتبقية بعد أن تأخذ التسمية والزر حصتيهما. استخدم المساعد الثابت HBox.setHgrow(node, Priority.ALWAYS):

HBox.setHgrow(nameField, Priority.ALWAYS);

هذا يُخبر محرك التخطيط: "امنح أي مساحة أفقية إضافية لـnameField." إذا طلب أبناء متعددون Priority.ALWAYS فإنهم يتقاسمون المساحة الزائدة بالتساوي.

قيم تعداد Priority: ALWAYS — احصل على المساحة الزائدة عند توفرها دائمًا. SOMETIMES — استخدم المساحة الزائدة فقط إذا لم يكن ثمة ابن ALWAYS. NEVER (الافتراضي) — ابقَ بالحجم المفضّل.

VBox: تخطيط المكدّس الرأسي

VBox هو النظير الرأسي لـHBox. يُكدّس الأبناء من أعلى إلى أسفل. الواجهة البرمجية متطابقة — التباعد والمحاذاة والمسافة الداخلية وأولويات التوسع — إلا أن المحور رأسي وقيد التوسع هو VBox.setVgrow(node, Priority).

import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.*; import javafx.scene.layout.*; // لوحة تسجيل دخول بسيطة مبنية بـVBox VBox loginPanel = new VBox(14); // فجوة 14 بكسل بين الأبناء loginPanel.setAlignment(Pos.CENTER); loginPanel.setPadding(new Insets(24, 40, 24, 40)); Label title = new Label("Sign In"); title.setStyle("-fx-font-size: 22px; -fx-font-weight: bold;"); TextField emailField = new TextField(); emailField.setPromptText("Email"); PasswordField passwordField = new PasswordField(); passwordField.setPromptText("Password"); Button loginBtn = new Button("Log In"); loginBtn.setMaxWidth(Double.MAX_VALUE); // تمديد الزر ليملأ العرض الكامل loginPanel.getChildren().addAll(title, emailField, passwordField, loginBtn);

ضبط loginBtn.setMaxWidth(Double.MAX_VALUE) أسلوب شائع: يُزيل قيد الحد الأقصى لعرض الزر حتى يتمكن الـVBox من تمديده ليطابق عرضه الخاص، مما يُعطي مظهر "الزر بعرض كامل" المعتاد في شاشات تسجيل الدخول.

تداخل HBox داخل VBox (والعكس)

التخطيطات الحقيقية نادرًا ما تكون صفًا أو عمودًا واحدًا. النمط الذي ستستخدمه باستمرار هو تركيب الأجزاء: VBox كحاوية خارجية يحتوي أبناؤها على صفوف HBox واحد أو أكثر.

// نموذج يحتوي على حقول مُعنونة وصف إجراءات في الأسفل VBox form = new VBox(10); form.setPadding(new Insets(16)); // الصف 1: تسمية + حقل "الاسم الأول" HBox row1 = buildFormRow("First Name:", new TextField()); // الصف 2: تسمية + حقل "اسم العائلة" HBox row2 = buildFormRow("Last Name:", new TextField()); // الصف 3: أزرار محاذاة يمينًا Button cancel = new Button("Cancel"); Button submit = new Button("Submit"); HBox actions = new HBox(8, cancel, submit); actions.setAlignment(Pos.CENTER_RIGHT); form.getChildren().addAll(row1, row2, actions); // -- الدالة المساعدة -- private HBox buildFormRow(String labelText, TextField field) { Label label = new Label(labelText); label.setMinWidth(100); // عرض ثابت يحافظ على محاذاة الأعمدة HBox.setHgrow(field, Priority.ALWAYS); return new HBox(8, label, field); }
امنح التسميات minWidth ثابتًا. عندما تستخدم صفوف نموذج متعددة نفس عرض التسمية، تتوافق حقول الإدخال في عمود أنيق، تمامًا كما في النماذج الاحترافية. بدون ذلك، تتحوّل المحاذاة من صف لآخر بحسب طول نص كل تسمية.

التباعد مقابل المسافة الداخلية مقابل الهامش

ثلاث آليات تباعد مختلفة متاحة في HBox/VBox ومن المفيد معرفة ما تفعله بالضبط:

  • التباعد (Spacing) — الفجوة بين الأبناء، تُضبط على الجزء (setSpacing() أو معامل المُنشئ).
  • المسافة الداخلية (Padding) — المسافة بين حدود الجزء وأبنائه، تُضبط على الجزء (setPadding(new Insets(...))).
  • الهامش (Margin) — مساحة إضافية حول ابن معين، تُضبط لكل عقدة على حدة (HBox.setMargin(node, new Insets(...)) أو VBox.setMargin(node, new Insets(...))).

في معظم النماذج يكفي التباعد والمسافة الداخلية. استخدم الهوامش لكل عقدة عندما تحتاج ابنًا واحدًا إلى إبعاده عن إخوته دون التأثير على التباعد العام للصف.

تنسيق HBox و VBox بـCSS

يقبل الجزءان CSS عبر طريقة setStyle() أو عبر ورقة أنماط. أكثر الخصائص فائدة هي -fx-background-color و-fx-background-radius و-fx-border-color و-fx-padding (التي تعادل setPadding()). يُفضّل استخدام أوراق الأنماط الخارجية على استدعاءات setStyle() المُدمجة للحفاظ على الفصل بين منطق الواجهة والتنسيق.

HBox toolbar = new HBox(8); toolbar.getStyleClass().add("toolbar"); // في ورقة الأنماط (مثلاً styles.css المحمّلة على الـScene): // .toolbar { // -fx-background-color: #2c2c2c; // -fx-padding: 8 12 8 12; // }
لا تخلط متطلبات التخطيط ومتطلبات التنسيق في setStyle(). ضبط -fx-padding في ورقة الأنماط وفي setPadding() في الكود في آنٍ واحد يجعل أحدهما يُلغي الآخر بصمت. اختر أسلوبًا واحدًا والتزم به في كامل التطبيق.

الخلاصة

HBox وVBox هما جزءا التخطيط الأساسيان في JavaFX. استخدم HBox لبناء أشرطة الأدوات وصفوف الأزرار وحقول النموذج المُعنونة جنبًا إلى جنب. استخدم VBox لتكديس الأقسام واللوحات وصفوف النموذج من أعلى إلى أسفل. اجمعهما بتداخل أحدهما داخل الآخر، وتحكّم في المساحة البيضاء بالتباعد والمسافة الداخلية والهامش، وادفع سلوك التوسع بـHBox.setHgrow / VBox.setVgrow. في الدرس التالي ستتعرّف على BorderPane وGridPane — الأجزاء المناسبة لتخطيطات النوافذ متعددة المناطق والشبكات الجدولية الدقيقة.