القوائم والجداول وصناديق الاختيار
القوائم والجداول وصناديق الاختيار
ثلاثة عناصر تهيمن على عرض البيانات في كل تطبيق JavaFX حقيقي تقريبًا: ListView وTableView وComboBox. يتّبع كل منها نفس الفلسفة المعمارية — الفصل بين النموذج والعرض (Model-View) حيث يعرض العنصر أيَّ بيانات تضعها في ObservableList، ويحدّث نفسه تلقائيًا عند تغيّر تلك القائمة. إن فهمت هذا النمط مرة واحدة فهمتَ العناصر الثلاثة كلّها.
أساس ObservableList
قبل لمس أي عنصر، استوعب هذا جيّدًا: عناصر البيانات في JavaFX لا تحتفظ بنسخ من بياناتك. إنها تحتفظ بمرجع إلى ObservableList<T> وتراقبه بحثًا عن أي تغيير. أضف عنصرًا إلى القائمة فيُعيد العنصر رسم نفسه. احذف واحدًا فيختفي الصف. لن تحتاج أبدًا إلى استدعاء دالة "تحديث".
ListView
يعرض ListView<T> قائمة قابلة للتمرير من العناصر، واحد في كل صف. يمكن أن يكون النوع العام أي شيء — String أو فئة نموذج أو نوع مُعدَّد (enum).
getSelectionModel() كائن MultipleSelectionModel يكشف عن selectedItemProperty() (ربط مفرد) وgetSelectedItems() (مجموعة قابلة للمراقبة للاختيار المتعدد). اقرأ الاختيار دائمًا عبر هذه الواجهة البرمجية — لا تتجوّل في شجرة الخلايا بنفسك أبدًا.
تخصيص عرض الخلايا بمصانع الخلايا
بشكل افتراضي يستدعي ListView الدالة toString() على كل عنصر. للحصول على صفوف أغنى — أيقونات، أزرار، نصوص منسّقة — أمدّ بـمصنع خلايا (cell factory): وهو Callback تستدعيه القائمة مرة واحدة لكل خلية مرئية لإنشاء ListCell<T>. تقوم JavaFX بعدها بإعادة تدوير تلك الخلايا أثناء التمرير (نفس مبدأ RecyclerView في Android).
super.updateItem() أولًا دائمًا وتعامل مع حالة empty دائمًا. تجاهل أيٍّ منهما يُسبّب ظهور محتوى وهمي في الصفوف الفارغة — وهو خطأ شائع جدًا عند مبتدئي JavaFX. الخلايا تُعاد استخدامها؛ إذا لم تمسحها عندما تكون empty == true فستنزّ البيانات القديمة إلى الفتحات الفارغة.
TableView
يعرض TableView<T> مجموعة من الكائنات في شبكة من الأعمدة. كل TableColumn<T, CellValue> يعرف كيف يستخرج حقلًا واحدًا من كائن من النوع T.
تستخدم PropertyValueFactory الانعكاس (reflection) لاستدعاء دالة getter باسم يطابق السلسلة التي تمرّرها. البديل الأنظف والآمن للإعادة الهيكلية (refactoring) هو مصنع قيم خلايا lambda يقرأ خاصية JavaFX مباشرةً:
الخلايا القابلة للتحرير
جعل خلية الجدول قابلة للتحرير يتطلب خطوتين: إخبار الجدول بأنه قابل للتحرير، ثم إعطاء العمود مصنع خلايا قابلًا للتحرير ومعالج setOnEditCommit يكتب القيمة الجديدة مجددًا إلى النموذج.
TableView وListView بكفاءة أعلى بكائنات Property (SimpleStringProperty وSimpleIntegerProperty وغيرها). إذا تغيّرت الخاصية برمجيًا، تحدّث خلية الجدول تلقائيًا — لا حاجة لاستدعاءات تحديث يدوية.
ComboBox
يجمع ComboBox<T> بين زر يعرض الاختيار الحالي وقائمة منسدلة. كما يدعم حقل نص اختياريًا قابلًا للتحرير عند استدعاء setEditable(true).
ComboBox مع كائنات مخصصة
عندما يكون النوع العام ليس String، أمدّ بـStringConverter حتى يعرف العنصر كيف يعرض العناصر في وجه الزر وفي القائمة المنسدلة على حدٍّ سواء.
مقارنة العناصر الثلاثة
- ListView: قائمة بعمود واحد من العناصر. استخدمها للوحات التنقل وقوائم الخيارات أو أي مجموعة قابلة للتمرير حيث تكفي قطعة بيانات واحدة في كل صف.
- TableView: شبكة متعددة الأعمدة. استخدمه عندما يكون لكل عنصر عدة حقول تحتاج إلى عرضها جنبًا إلى جنب مع الفرز الاختياري والتحرير.
- ComboBox: عنصر اختيار مدمج. استخدمه داخل النماذج حيث المساحة محدودة والمستخدم يختار قيمة واحدة من مجموعة محددة.
الخلاصة
تشترك العناصر الثلاثة في نموذج بيانات ObservableList — غيّر القائمة فتتحدّث الواجهة تلقائيًا. يستخدم ListView وTableView مصانع الخلايا للعرض المخصص ويتّبعان مخطط إعادة تدوير الخلايا الذي يتعامل مع آلاف الصفوف بكفاءة. يربط TableView الأعمدة بخصائص النموذج إما عبر PropertyValueFactory أو مصانع lambda، ويدعم التحرير في الموضع عبر setOnEditCommit. يندمج ComboBox بسلاسة في النماذج ويكتسب تخصيص العرض من خلال StringConverter. في الدرس القادم ستُرتّب هذه العناصر على الشاشة باستخدام حاويات التخطيط HBox وVBox.