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

أجزاء التخطيط: BorderPane و GridPane

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

أجزاء التخطيط: BorderPane و GridPane

يُمثّل كلٌّ من BorderPane و GridPane من أقوى حاويات التخطيط في JavaFX، وهما النمطان اللذان يلجأ إليهما مطوّرو الواجهات ذوو الخبرة أوّلًا: BorderPane للهيكل الكلاسيكي المؤلّف من إطار وناحية محتوى رئيسية، وGridPane للتموضع الدقيق في صفوف وأعمدة. أتقن هذين الاثنين وستتمكّن من التعبير عن الغالبية العظمى من تخطيطات التطبيقات الحقيقية.

BorderPane — التخطيط القائم على المناطق

يقسم BorderPane مساحته إلى خمس مناطق مُسمّاة: top (أعلى)، وbottom (أسفل)، وleft (يسار)، وright (يمين)، وcenter (وسط). تُسنَد أي عقدة Node إلى أي منطقة؛ والمناطق غير المستخدمة تنكمش إلى الصفر. تتمدّد المنطقة الوسطى لتملأ كلّ المساحة المتبقية، مما يجعلها المكان الطبيعي لناحية المحتوى الرئيسية.

لماذا يُهمّ BorderPane: كل هيكل تطبيق سطح مكتب يتّبع القالب ذاته — شريط أدوات في الأعلى، وشريط حالة في الأسفل، ولوحة تنقّل على اليسار، ولوحة خصائص على اليمين، ولوحة أو قائمة في المركز. يُرمّز BorderPane هذا القالب مباشرةً — فأنت تصف النية لا الحساب الدقيق للبكسلات.

فيما يلي هيكل كامل يشبه بيئة تطوير متكاملة (IDE) أو مدير ملفات:

import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.stage.Stage; public class BorderPaneDemo extends Application { @Override public void start(Stage stage) { BorderPane root = new BorderPane(); // ── TOP: شريط أدوات بسيط ───────────────────────────────────── ToolBar toolbar = new ToolBar( new Button("New"), new Button("Open"), new Separator(), new Button("Save") ); root.setTop(toolbar); // ── BOTTOM: شريط الحالة ─────────────────────────────────────── Label status = new Label("Ready"); status.setPadding(new Insets(4, 8, 4, 8)); root.setBottom(status); // ── LEFT: شجرة التنقّل ──────────────────────────────────────── TreeView<String> nav = new TreeView<>( new TreeItem<>("Project") {{ getChildren().addAll( new TreeItem<>("src"), new TreeItem<>("resources"), new TreeItem<>("tests") ); setExpanded(true); }} ); nav.setPrefWidth(180); root.setLeft(nav); // ── RIGHT: لوحة الخصائص ─────────────────────────────────────── VBox props = new VBox(8, new Label("Properties"), new CheckBox("Read-only"), new CheckBox("Executable") ); props.setPadding(new Insets(8)); props.setPrefWidth(160); root.setRight(props); // ── CENTER: المحتوى الرئيسي (يملأ المساحة المتبقية) ────────── TextArea editor = new TextArea("// start coding here…"); root.setCenter(editor); stage.setScene(new Scene(root, 800, 500)); stage.setTitle("BorderPane Demo"); stage.show(); } }

المحاذاة داخل مناطق BorderPane

تستوعب كل منطقة عقدة واحدة. تتحكّم في كيفية جلوس تلك العقدة داخل المنطقة باستخدام الأساليب الثابتة المُعرَّفة على BorderPane:

// محاذاة تسمية الحالة إلى الحافة اليمنى لمنطقة الأسفل BorderPane.setAlignment(status, javafx.geometry.Pos.CENTER_RIGHT); // إضافة هامش بين العقدة وحافة المنطقة BorderPane.setMargin(nav, new Insets(4));
استخدم التداخل عند الحاجة إلى أبناء متعددين في منطقة واحدة. تستوعب المنطقة عقدة واحدة فحسب، لكن تلك العقدة يمكن أن تكون HBox أو VBox أو أي حاوية أخرى تحتوي على أي عدد من الأبناء. هذا هو النمط الطبيعي — لا تُعاكس العقد الواحدة أبدًا.

GridPane — التخطيط بصفوف وأعمدة

يُرتّب GridPane الأبناء في شبكة من الخلايا. على خلاف جداول HTML، لا تمتلك الخلايا حدودًا افتراضية ويمكن أن تكون بأي حجم — كلّ صف وعمود يُحدَّد حجمه باستقلالية وفق أكبر محتوياته (أو وفق قيود صريحة تُحدّدها). يمكن للأبناء امتداد عبر صفوف وأعمدة متعددة.

تضع كلّ ابن باستدعاء add(node, columnIndex, rowIndex) أو التحميل الزائد للامتداد add(node, col, row, colSpan, rowSpan). تبدأ الفهارس من الصفر.

import javafx.application.Application; import javafx.geometry.*; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.stage.Stage; public class GridPaneDemo extends Application { @Override public void start(Stage stage) { GridPane grid = new GridPane(); grid.setHgap(10); // المسافة الأفقية بين الأعمدة grid.setVgap(8); // المسافة الرأسية بين الصفوف grid.setPadding(new Insets(16)); // الصف 0 — الاسم الأول grid.add(new Label("First name:"), 0, 0); grid.add(new TextField(), 1, 0); // الصف 1 — اسم العائلة grid.add(new Label("Last name:"), 0, 1); grid.add(new TextField(), 1, 1); // الصف 2 — البريد الإلكتروني grid.add(new Label("Email:"), 0, 2); grid.add(new TextField(), 1, 2); // الصف 3 — حقل الملاحظات متعدد الأسطر يمتدّ عبر عمودين grid.add(new Label("Notes:"), 0, 3); TextArea notes = new TextArea(); notes.setPrefRowCount(4); grid.add(notes, 1, 3, 1, 2); // يمتدّ عمودًا واحدًا وصفَّين // الصف 5 — زر الإرسال يمتدّ عبر العرض الكامل Button submit = new Button("Submit"); GridPane.setColumnSpan(submit, 2); GridPane.setHalignment(submit, HPos.RIGHT); grid.add(submit, 0, 5); stage.setScene(new Scene(grid, 400, 320)); stage.setTitle("GridPane Demo"); stage.show(); } }

قيود الأعمدة والصفوف

تنكمش الأعمدة بشكل افتراضي إلى أدنى عرض لمحتوياتها. في النماذج عادةً ما تريد عمود التسميات أن يبقى ضيّقًا وعمود الحقول أن يتمدّد مع النافذة. استخدم ColumnConstraints:

ColumnConstraints labelCol = new ColumnConstraints(); labelCol.setMinWidth(90); labelCol.setHgrow(Priority.NEVER); ColumnConstraints fieldCol = new ColumnConstraints(); fieldCol.setHgrow(Priority.ALWAYS); // يتمدّد لملء العرض المتاح fieldCol.setFillWidth(true); grid.getColumnConstraints().addAll(labelCol, fieldCol);

الأمر المكافئ للصفوف هو RowConstraints — يمكنك تثبيت ارتفاع صف بـ setMinHeight / setPrefHeight أو السماح له بالتمدّد باستخدام setVgrow(Priority.ALWAYS).

محاذاة المحتوى داخل الخلية

أساليب ثابتة تتحكّم في موضع عقدة داخل خليتها:

  • GridPane.setHalignment(node, HPos.CENTER) — المحاذاة الأفقية: LEFT أو CENTER أو RIGHT.
  • GridPane.setValignment(node, VPos.TOP) — المحاذاة الرأسية: TOP أو CENTER أو BOTTOM أو BASELINE.
  • GridPane.setMargin(node, new Insets(...)) — مسافة حول عقدة محدّدة.
  • GridPane.setFillWidth(node, true) — تمديد العقدة لملء عرض الخلية.
لا تضع عقدتَين في الخلية ذاتها. لا يرمي JavaFX خطأً — بل يتكدّسهما بصمت. إن بدا أن تسمية اختفت، تحقّق أولًا من التداخل غير المقصود في نفس الخلية قبل أي شيء آخر.

الجمع بين BorderPane و GridPane

في الممارسة الفعلية نادرًا ما تختار أحدهما دون الآخر — بل تُداخلهما. يستخدم شاشة تسجيل نموذجية BorderPane كجذر على المستوى الأعلى (شريط أدوات وشريط حالة ومركز محشو بهوامش) مع GridPane كابن مركزي يحتوي على حقول النموذج. يتولّى BorderPane هيكل التطبيق الكامل، ويتولّى GridPane محاذاة النموذج الداخلي. كلّ حاوية تؤدّي ما صُمِّمت له، ولا تتعارض مع الأخرى.

الخلاصة

يمنحك BorderPane نمط الهيكل ذا الخمس مناطق: الأعلى والأسفل واليسار واليمين والمركز المتمدّد — مثالي لإطار التطبيق. يمنحك GridPane الدقة بصفوف وأعمدة مع امتداد وتحكّم في الفجوات وسياسات نموّ لكل عمود — مثالي لنماذج إدخال البيانات. داخلهما بحرية: الـ BorderPane الخارجي يُعرّف هيكل التطبيق؛ وداخله GridPane أو HBox/VBox تبني لوحات التفاصيل. يغطّي هذان اللوحان معًا غالبية تخطيطات تطبيقات سطح المكتب الاحترافية.