شرح الكلاس TableView في javafx

شرح الكلاس  TableView

الكلاس TableView يستخدم لإضافة جدول ( Table ) في واجهة المستخدم.

هذا الجدول يوفر لك ميزات مهمة جداً مثل:

  • يضيف Scroll Bar أفقي و عامودي بشكل تلقائي للجدول عند الحاجة.

  • إمكانية تعديل القيم مباشرةً على الجدول مع ضمان أن يكون نوع البيانات المدخل صحيح.

  • توفير أكثر من طريقة لتعديل القيم بشكل مباشر على الجدول.

  • إمكانية وضع الأعمدة بداخل بعضها بكل سهولة.

  • إمكانية تحديد حجم الأعمدة الأولي و إمكانية التلاعب بأحجامها و تبديل أماكنها.

  • في حال كان الجدول فارغ, تظهر رسالة تفيد بأن الجدول فارغ و يمكنك تغيير النص الذي يظهر.

  • في حال كان الجدول فارغ, يمكنك جعل أي شيء يظهر في الجدول. فمثلاً يمكنك إظهار Button يسمح عند النقر عليه بتعبئة الجدول من قاعدة بيانات.

  • كل عامود تضيفه في الجدول, يضاف له خاصية الترتيب ( Sorting ) أبجدياً إذا كان يمثل قيم نصية أو حسب القيم إذا كان يمثل قيم عددية.
        أي بمجرد أن تنقر على إسم العامود ستجد أنه قد تم ترتيب كل أسطر الجدول.



بناء الكلاس TableView

@DefaultProperty(value="items")
public class TableView<S>
extends Control 
	


طريقة التعامل مع ال TableView

عند استخدام TableView لعرض البيانات, فإن JavaFX تتوقع منك أن تقوم بتعريف كلاس خاص تضع فيه عدة خصائص تمثل أسماء و أنواع البيانات التي سيتم تخزينها في كل عامود فيه. كما أنها تجبرك على تعريف دوال Setter و Getter مع إختيار التسميات الصحيحة المشتقة من أسماء الخصائص لأنها ستقوم باستدعائهم بشكل تلقائي عند ربط الجدول و أعمدته بهذا الكلاس.

ملاحظة: لتعرض البيانات بداخل TableView يجب أن يحتوي الجدول على عامود واحد على الأقل. و كل عامود تضيفه عبارة عن كائن من الكلاس TableColumn.


معلومة تقنية TableView

كل كائن TableView تنشئه يتم ربطه بكائن من كلاس إسمه ObservableList.
كائن الـ ObservableList هو الذي تتخزن فيه الخيارات التي تظهر في الـ TableView.

لهذا السبب, سيكون عليك إنشاء كائن من الكلاس ObservableList و تمرر له العناصر التي تريدها أن تظهر في كائن الـ TableView.
بعدها تقوم بربط كائن الـ ObservableList بكائن الـ TableView بواسطة الدالة setItems().

لا تقلق ستفهم كل شيء من الأمثلة.

كونستركتورات الكلاس TableView

الجدول التالي يحتوي على كونستركتورات الكلاس TableView.

الكونستركتور مع تعريفه
public TableView() ينشئ كائن من الكلاس TableView يمثل List فارغة عامودية, أي لا تحتوي أي عنصر.
public TableView(ObservableList<T> items) ينشئ كائن من الكلاس TableView يمثل List عامودية فيها خيارات جاهزة.
مكان الباراميتر items نمرر كائن من الكلاس ObservableList من أي نوع مثل String, Integer إلخ..
عناصر هذا الكائن هي التي ستظهر كخيارات في القائمة.

دوال الكلاس TableView

الجدول التالي يحتوي على دوال الكلاس TableView الأكثر إستخداماً.

الدالة مع تعريفها
public final void setTranslateX(double value) تستخدم لتحديد مكان كائن الـ TableView الذي قام باستدعائها أفقياً.
مكان الباراميتر value نضع رقم يمثل كم Pixel سيتم إزاحته من اليسار إلى اليمين.
public final void setTranslateY(double value) تستخدم لتحديد مكان الـ TableView الذي قام باستدعائها عامودياً.
مكان الباراميتر value نضع رقم يمثل كم Pixel سيتم إزاحته من الأعلى إلى الأسفل.
public void setPrefSize(double prefWidth, double prefHeight) تستخدم لتحديد حجم كائن الـ TableView الذي قام باستدعائها.

  • مكان الباراميتر prefWidth نضع رقم يمثل عرض الـ TableView بالـ Pixel.

  • مكان الباراميتر prefHeight نضع رقم يمثل طول الـ TableView بالـ Pixel.

public final void setDisable(boolean value) تستخدم لجعل كائن الـ TableView الذي قام باستدعائها يبدو غير مفعّل, أي يصبح لونه باهتاً و غير قابل للنقر عليه.
مكان الباراميتر value نضع القيمة true لجعله غير مفعّل.
public final MultipleSelectionModel<T> getSelectionModel() ترجع كائن من الكلاس MultipleSelectionModel يمثل الخيار أو مجموعة الخيارات التي تم النقر عليها بداخل كائن الـ TableView.
من خلال الكائن الذي ترجعه هذه الدالة يمكنك معرفة قيمة و index كل خيار تم إختياره في الـ TableView, معرفة عدد الخيارات التي تم تحديدها, تحديد ما إذا كان يمكن للمستخدم تحديد أكثر من سطر أو عامود في الجدول بنفس الوقت الخ..
public final void setItems(ObservableList<T> value) تستخدم لوضع مجموعة من البيانات في كائن الـ TableView الذي قام باستدعائها.
مكان الباراميتر value نمرر مصفوفة من القيم نوعها مطابق لنوع القيم التي يمكن أن يحتويها كائن الـ TableView.
public final ObservableList<T> getItems() ترجع كائن الـ ObservableList المرتبط بكائن الـ TableView الذي قام باستدعائها.
من خلال الكائن الذي ترجعه هذه الدالة يمكنك معرفة عدد الخيارات الموجودة في الـ TableView, إضافة خيار أو أكثر فيه, حذف خيار أو أكثر منه, البحث فيه, فلترة الخيارات التي نريدها أن تظهر إلخ..
public final void setEditable(boolean value) تستخدم لجعل المستخدم قادر على تحديث قيم كائن الـ TableView الذي قام باستدعائها.
مكان الباراميتر value نضع القيمة true لجعله المستخدم قادر على تحديث القيم بداخله.

ملاحظة: تحتاج أيضاً إستدعاء الدوال setCellValueFactory() و setCellFactory() و setCellFactory() على كل TableColumn تضيفه بالجدول حتى تحدد كيف يستطيع المستخدم تحديث القيم. لا تقلق, شرحنا هذا بتفصيل في الأمثلة.
public final ObservableList<<S, ?>> getColumns() ترجع كائن الـ ObservableList المرتبط بكائن الـ TableView الذي قام باستدعائها.
من خلال الكائن الذي ترجعه هذه الدالة يمكنك التحكم بأعمدة الجدول كيفما شئت حيث يمكنك إضافة أعمدة جديدة على الجدول, حذف أعمدة موجودة في الجدول إلخ..
public void scrollTo(int index) تستخدم للنزول بشكل عامودي ( Vertical Scrolling ) و لمسافة محددة في كائن الـ TableView الذي قام باستدعائها.
فمثلاً, في حال كان الجدول يحتوي على أسطر كثيرة يمكنك إنشاء Button عند النقر عليه ينزل شريط التمرير في الجدول مسافة 100 بيكسل عند كل نقرة.
مكان الباراميتر index نضع رقم يمثل كم Pixel سيتم النزول في الجدول.
public void scrollTo(Object o) تستخدم للنزول بشكل عامودي ( Vertical Scrolling ) في كائن الـ TableView الذي قام باستدعائها للوقوف مباشرةً عند الكائن الذي تم تمريره لها مكان الباراميتر o.
مكان الباراميتر o نضع الكائن الذي نريد أن يتم النزول إليه في الجدول حتى يراه المستخدم.
إذاً هذه الدالة مفيدة في حال أردت إضافة Button خاص للبحث عن كائن موجود في الجدول.
public final void setStyle(String value) تستخدم لتعديل تصميم كائن الـ TableView الذي قام بإستدعائها.
مكان الباراميتر value يمكنك تمرير إسم و قيمة أي خاصية تريد تعديلها في كائن الـ TableView بأسلوب لغة CSS لإظهاره بالشكل الذي تريده.

أمثلة شاملة على الكلاس TableView

في جميع الأمثلة سنتعامل مع القائمة كالتالي:

  • سنقوم بتعريف كائن من الكلاس TableView و الذي بدوره يمثل الذي سيظهر في واجهة المستخدم.

  • سنقوم بتعريف كائن من الكلاس TableColumn يمثل كل عامود نضيفه في الجدول.

  • سنقوم بتعريف كائن من الكلاس ObservableList و الذي سنستخدمه لتخزين العناصر التي ستظهر في القائمة.

  • سنقوم بربط كائن الـ TableView بكائن الـ ObservableList بواسطة الدالة setItems().


لجعل القيم الموضوعة في الجدول قابلة للتعديل يجب أن نفعل التالي و بالترتيب:

  1. إنشاء كلاس خاص للجدول يمثل أسماء و أنواع البيانات التي سيتم عرضها فيه مع تجهيز دوال الـ Setters و الـ Getters بشكل صحيح.

  2. إستدعاء الدالة setEditable() من كائن الـ TableView و تمرير القيمة true لها.

  3. إستدعاء الدالة setCellValueFactory() من كائن الـ TableColumn و تمرير إسم الخاصية التي تمثل البيانات التي نريدها أن تظهر تحت هذا العمود.

  4. إستدعاء الدالة setValueFactory() من كائن الـ TableColumn و تمرير كلاس معين بداخلها مهمته تحديد كيف يمكن للمستخدم أن يقوم بتغيير القيمة المدخلة و من هذا الكلاس نقوم باستدعاء استدعاء الدالة forNameColumn().

  5. إستدعاء الدالة setOnEditCommit() من كائن الـ TableColumn و بداخلها نفعل implements بأسلوب الـ Lambda Expression للإنترفيس EventHandler. في الأخير نستدعي دالة الـ Set الخاصة بالعامود الذي يجب أن يتم تبديل القيمة فيه.



 طريقة إنشاء كائن من الكلاس TableView

المثال التالي يعلمك طريقة إنشاء كائن من الكلاس TableView يمثل جدول فارغ و إضافته في النافذة.

مثال

Main.java
      import javafx.application.Application;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableView;
      import javafx.stage.Stage;

      public class Main extends Application {

      @Override
      public void start(Stage stage) {

      // يمثل الجدول الذي نريد إضافته في النافذة TableView هنا قمنا بإنشاء كائن من الكلاس
      TableView table = new TableView();

      // table تمثل الأعمدة التي سنضيفها في الكائن TableColumn هنا قمنا بإنشاء ثلاث كائنات
      TableColumn columnFirstName = new TableColumn("First Name");
      TableColumn columnLastName  = new TableColumn("Last Name");
      TableColumn columnEmail     = new TableColumn("Email");

      // table هنا وضعنا الأعمدة الثلاثة في الكائن
      table.getColumns().addAll(columnFirstName, columnLastName, columnEmail);

      // في النافذة table هنا قمنا بتحديد حجم الكائن
      table.setPrefSize(440, 300);

      // حتى نستغل كل الحجم الذي حجزناه له table هنا قمنا بتحديد عرض كل عامود بداخل الكائن
      columnFirstName.setPrefWidth(120);
      columnLastName.setPrefWidth(120);
      columnEmail.setPrefWidth(200);

      // في النافذة table هنا قمنا بتحديد مكان ظهور الكائن
      table.setTranslateX(10);
      table.setTranslateY(10);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن table هنا قمنا بإضافة الكائن
      root.getChildren().add(table);

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 460, 320);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX TableView");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

ملاحظة: يمكنك التلاعب بحجم الأعمدة و تبديل أماكنهم بواسطة الفأرة.


 طريقة وضع TableColumn بداخل TableColumn في الـ TableView

المثال التالي يعلمك طريقة وضع TableColumn بداخل TableColumn في الـ TableView.
ستتعلم أيضاً كيف تغير النص الذي يظهر إذا كان الـ TableView فارغاً.

مثال

Main.java
      import javafx.application.Application;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableView;
      import javafx.stage.Stage;

      public class Main extends Application {

      @Override
      public void start(Stage stage) {

      // يمثل الجدول الذي نريد إضافته في النافذة TableView هنا قمنا بإنشاء كائن من الكلاس
      TableView table = new TableView();

      // table تمثل الأعمدة الأساسية التي سنضيفها في الكائن TableColumn هنا قمنا بإنشاء ثلاث كائنات
      TableColumn columnFirstName = new TableColumn("First Name");
      TableColumn columnLastName = new TableColumn("Last Name");
      TableColumn columnEmail = new TableColumn("Email");

      // columnEmail الأعمدة الداخلية التي سنضيفها في الكائن TableColumn هنا قمنا بإنشاء كائنين
      TableColumn columnEmailPrimary = new TableColumn("Primary");
      TableColumn columnEmailSecondary = new TableColumn("Secondary");

      // table هنا وضعنا الأعمدة الثلاثة الأساسية في الكائن
      table.getColumns().addAll(columnFirstName, columnLastName, columnEmail);

      // columnEmail بداخل العامود الأساسي columnEmailSecondary و columnEmailPrimary هنا وضعنا العامودين
      columnEmail.getColumns().addAll(columnEmailPrimary, columnEmailSecondary);

      // إذا كان فارغاً table يحتوي على النص الجديد الذي سيظهر بداخل كائن الـ Label هنا قمنا يوضع
      table.setPlaceholder( new Label("Table is empty") );

      // في النافذة table هنا قمنا بتحديد حجم الكائن
      table.setPrefSize(440, 300);

      // حتى نستغل كل الحجم الذي حجزناه له table هنا قمنا بتحديد عرض كل عامود بداخل الكائن
      columnFirstName.setPrefWidth(100);
      columnLastName.setPrefWidth(100);
      columnEmailPrimary.setPrefWidth(120);
      columnEmailSecondary.setPrefWidth(120);

      // في النافذة table هنا قمنا بتحديد مكان ظهور الكائن
      table.setTranslateX(10);
      table.setTranslateY(10);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن table هنا قمنا بإضافة الكائن
      root.getChildren().add(table);

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 460, 320);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX TableView");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

ملاحظة: يمكنك التلاعب بحجم الأعمدة و تبديل أماكنهم بواسطة الفأرة.


 طريقة عرض البيانات في TableView

المثال التالي يعلمك طريقة عرض البيانات في TableView.

تذكر

عند استخدام TableView لعرض البيانات, فإن JavaFX تتوقع منك أن تقوم بتعريف كلاس خاص تضع فيه عدة خصائص تمثل أسماء و أنواع البيانات التي سيتم تخزينها في كل عامود فيه. كما أنها تجربك على تعريف دوال Setters و Getters مع إختيار التسميات الصحيحة المشتقة من أسماء الخصائص لأنها ستقوم باستدعائهم بشكل تلقائي عند ربط الجدول و أعمدته بهذا الكلاس.

الآن, بما أننا ننوي بناء جدول خاص لعرض المنتجات, سنقوم بتجهيز كلاس إسمه Product يمثل بيانات أي منتج سيتم عرضه في الجدول.
إذاً الكلاس Product سيحتوي على ثلاث خصائص و هي الإسم (name), السعر (price) و الكمية (quantity) مع دوال الـ Setters و الـ Getters التي تتيح الوصول لهذه الخصائص.

إنتبه: إن لم تقم بتعريف دوال الـ Setters و الـ Getters في الكلاس Product تماماً كما فعلنا فإنه لن يظهر لك أي بيانات في الجدول عند تشغيل البرنامج.


مثال

Product.java
      public class Product {

      String name;
      double price;
      int quantity;

      public Product() {
      this.name = "";
      this.price = 0.0;
      this.quantity = 0;
      }

      public Product(String name, double price, int quantity) {
      this.name = name;
      this.price = price;
      this.quantity = quantity;
      }

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }

      public double getPrice() {
      return price;
      }

      public void setPrice(double price) {
      this.price = price;
      }

      public int getQuantity() {
      return quantity;
      }

      public void setQuantity(int quantity) {
      this.quantity = quantity;
      }

      }
    

Main.java
      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableView;
      import javafx.scene.control.cell.PropertyValueFactory;
      import javafx.stage.Stage;

      public class Main extends Application {

      @Override
      public void start(Stage stage) {

      // يمثل الجدول الذي نريد إضافته في النافذة TableView هنا قمنا بإنشاء كائن من الكلاس
      TableView table = new TableView();

      // يمثل مجموعة البيانات التي ستظهر في الجدول ObservableList هنا قمنا بإنشاء كائن من الكلاس
      // Product كل سطر في هذا الجدول يمثل كائن من الكلاس
      ObservableList<Product> data = FXCollections.observableArrayList(
      new Product("Laptop", 800.00, 3),
      new Product("Camera", 199.99, 12),
      new Product("Hard Disk", 79.99, 8),
      new Product("Projector", 240.55, 7),
      new Product("PC Screen", 120.00, 6),
      new Product("Speakers", 33.00, 10),
      new Product("Headphones", 12.50, 4),
      new Product("Microphone", 24.75, 5)
      );

      // table تمثل الأعمدة التي سنضيفها في الكائن TableColumn هنا قمنا بإنشاء ثلاث كائنات
      TableColumn columnName = new TableColumn("Name");
      TableColumn columnPrice = new TableColumn("Price ($)");
      TableColumn columnQuantity = new TableColumn("Quantity");

      // table هنا وضعنا الأعمدة الثلاثة في الكائن
      table.getColumns().addAll(columnName, columnPrice, columnQuantity);

      // table في الكائن data هنا قمنا بوضع بيانات الكائن
      table.setItems(data);

      // تم إضافته في الجدول Product الموجودة في كل كائن name سيُعرض فيه قيم الـ columnName هنا قلنا أن العامود
      columnName.setCellValueFactory( new PropertyValueFactory<>("name") );

      // تم إضافته في الجدول Product الموجودة في كل كائن price سيُعرض فيه قيم الـ columnPrice هنا قلنا أن العامود
      columnPrice.setCellValueFactory( new PropertyValueFactory<>("price") );

      // تم إضافته في الجدول Product الموجودة في كل كائن quantity سيُعرض فيه قيم الـ columnQuantity هنا قلنا أن العامود
      columnQuantity.setCellValueFactory( new PropertyValueFactory<>("quantity") );

      // في النافذة table هنا قمنا بتحديد حجم الكائن
      table.setPrefSize(440, 300);

      // حتى نستغل كل الحجم الذي حجزناه له table هنا قمنا بتحديد عرض كل عامود بداخل الكائن
      columnName.setPrefWidth(144);
      columnPrice.setPrefWidth(144);
      columnQuantity.setPrefWidth(150);

      // في النافذة table هنا قمنا بتحديد مكان ظهور الكائن
      table.setTranslateX(10);
      table.setTranslateY(10);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن table هنا قمنا بإضافة الكائن
      root.getChildren().add(table);

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 460, 320);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX TableView");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

كما تلاحظ, يمكنك التلاعب بحجم الأعمدة و تبديل أماكنهم و النقر على عنوان أي عامود حتى يتم عرض البيانات بالترتيب الأبجدي أو حسب قيمهم.


 طريقة     تغيير القيم الموجودة في الـ TableView 

المثال التالي يعلمك طريقة جعل المستخدم قادر على تغيير القيم الموجودة في الـ TableView عند النقر عليها.

ملاحظة: هنا كل عامود في الجدول يخزن نوع محدد من البيانات كالتالي:

  • عامود الأسماء (Name) يقبل بيانات من النوع String.

  • عامود الأسعار (Price) يقبل بيانات من النوع Double.

  • عامود الكميات (Quantity) يقبل بيانات من النوع Integer.

الآن, بما أننا ننوي بناء جدول خاص لعرض المنتجات, سنقوم بتجهيز كلاس إسمه Product يمثل بيانات أي منتج سيتم عرضه في الجدول.
إذاً الكلاس Product سيحتوي على ثلاث خصائص و هي الإسم (name), السعر (price) و الكمية (quantity) مع دوال الـ Setters و الـ Getters التي تتيح الوصول لهذه الخصائص.

إنتبه: إن لم تقم بتعريف دوال الـ Setters و الـ Getters في الكلاس Product تماماً كما فعلنا فإنه لن يظهر لك أي بيانات في الجدول عند تشغيل البرنامج.


تذكر

عند استخدام TableView لعرض البيانات, فإن JavaFX تتوقع منك أن تقوم بتعريف كلاس خاص تضع فيه عدة خصائص تمثل أسماء و أنواع البيانات التي سيتم تخزينها في كل عامود فيه. كما أنها تجربك على تعريف دوال Setters و Getters مع إختيار التسميات الصحيحة المشتقة من أسماء الخصائص لأنها ستقوم باستدعائهم بشكل تلقائي عند ربط الجدول و أعمدته بهذا الكلاس.


مثال

Product.java
      import javafx.beans.property.SimpleStringProperty;
      import javafx.beans.property.SimpleDoubleProperty;
      import javafx.beans.property.SimpleIntegerProperty;

      public class Product {

      SimpleStringProperty name;
      SimpleDoubleProperty price;
      SimpleIntegerProperty quantity;

      public Product() {
      this.name = new SimpleStringProperty("");
      this.price = new SimpleDoubleProperty(0.0);
      this.quantity = new SimpleIntegerProperty(0);
      }

      public Product(String name, double price, int quantity) {
      this.name = new SimpleStringProperty(name);
      this.price = new SimpleDoubleProperty(price);
      this.quantity = new SimpleIntegerProperty(quantity);
      }

      public String getName() {
      return name.getValue();
      }

      public void setName(String name) {
      this.name = new SimpleStringProperty(name);
      }

      public double getPrice() {
      return price.getValue();
      }

      public void setPrice(double price) {
      this.price = new SimpleDoubleProperty(price);
      }

      public int getQuantity() {
      return quantity.getValue();
      }

      public void setQuantity(int quantity) {
      this.quantity = new SimpleIntegerProperty(quantity);
      }

      }
    

Main.java
      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableColumn.CellEditEvent;
      import javafx.scene.control.TableView;
      import javafx.scene.control.cell.PropertyValueFactory;
      import javafx.scene.control.cell.TextFieldTableCell;
      import javafx.stage.Stage;
      import javafx.util.converter.DoubleStringConverter;
      import javafx.util.converter.IntegerStringConverter;

      public class Main extends Application {

      @Override
      public void start(Stage stage) {

      // يمثل الجدول الذي نريد إضافته في النافذة TableView هنا قمنا بإنشاء كائن من الكلاس
      TableView table = new TableView();

      // يمثل مجموعة البيانات التي ستظهر في الجدول ObservableList هنا قمنا بإنشاء كائن من الكلاس
      // Product كل سطر في هذا الجدول يمثل كائن من الكلاس
      ObservableList<Product> data = FXCollections.observableArrayList(
      new Product("Laptop", 800.00, 3),
      new Product("Camera", 199.99, 12),
      new Product("Hard Disk", 79.99, 8),
      new Product("Projector", 240.55, 7),
      new Product("PC Screen", 120.00, 6),
      new Product("Speakers", 33.00, 10),
      new Product("Headphones", 12.50, 4),
      new Product("Microphone", 24.75, 5)
      );


      // فقط String هنا قمنا بإنشاء عامود الأسماء و حددنا أنه يقبل قيم من النوع
      TableColumn<Product, String> columnName = new TableColumn<>("Name");
      // تم إضافته في الجدول Product الموجودة في كل كائن name سيُعرض فيه قيم الـ columnName هنا قلنا أن العامود
      columnName.setCellValueFactory( new PropertyValueFactory<>("name") );
      // String يتيح لك إدخال القيمة الجديدة مع ضمان أن لا يتم قيولها إذا لم تكن من النوع TextField سيتم إظهار columnName عند النقر على أي قيمة موجودة في العامود
      columnName.setCellFactory(TextFieldTableCell.forTableColumn());
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnName.setOnEditCommit((CellEditEvent<Product, String> t) -> {
      ((Product) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setName(t.getNewValue());
      });


      // فقط Double هنا قمنا بإنشاء عامود الأسعار و حددنا أنه يقبل قيم من النوع
      TableColumn<Product, Double> columnPrice = new TableColumn<>("Price ($)");
      // تم إضافته في الجدول Product الموجودة في كل كائن price سيُعرض فيه قيم الـ columnPrice هنا قلنا أن العامود
      columnPrice.setCellValueFactory( new PropertyValueFactory<>("price") );
      // Double يتيح لك إدخال القيمة الجديدة مع ضمان أن لا يتم قيولها إذا لم تكن من النوع TextField سيتم إظهار columnPrice عند النقر على أي قيمة موجودة في العامود
      columnPrice.setCellFactory(TextFieldTableCell.forTableColumn(new DoubleStringConverter()));
      // Double سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnPrice.setOnEditCommit((CellEditEvent<Product, Double> t) -> {
      ((Product) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setPrice(t.getNewValue());
      });


      // فقط Integer هنا قمنا بإنشاء عامود الكميات و حددنا أنه يقبل قيم من النوع
      TableColumn<Product, Integer> columnQuantity = new TableColumn<>("Quantity");
      // تم إضافته في الجدول Product الموجودة في كل كائن quantity سيُعرض فيه قيم الـ columnQuantity هنا قلنا أن العامود
      columnQuantity.setCellValueFactory( new PropertyValueFactory<>("quantity") );
      // Integer يتيح لك إدخال القيمة الجديدة مع ضمان أن لا يتم قيولها إذا لم تكن من النوع TextField سيتم إظهار columnQuantity عند النقر على أي قيمة موجودة في العامود
      columnQuantity.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
      // Integer سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnQuantity.setOnEditCommit((CellEditEvent<Product, Integer> t) -> {
      ((Product) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setQuantity(t.getNewValue());
      });


      // قابلة للتعديل table هنا جعلنا بيانات الجدول
      table.setEditable(true);
      // table هنا وضعنا الأعمدة الثلاثة في الكائن
      table.getColumns().addAll(columnName, columnPrice, columnQuantity);
      // table في الكائن data هنا قمنا بوضع بيانات الكائن
      table.setItems(data);
      // في النافذة table هنا قمنا بتحديد حجم الكائن
      table.setPrefSize(440, 300);

      // حتى نستغل كل الحجم الذي حجزناه له table هنا قمنا بتحديد عرض كل عامود بداخل الكائن
      columnName.setPrefWidth(144);
      columnPrice.setPrefWidth(144);
      columnQuantity.setPrefWidth(150);

      // في النافذة table هنا قمنا بتحديد مكان ظهور الكائن
      table.setTranslateX(10);
      table.setTranslateY(10);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن table هنا قمنا بإضافة الكائن
      root.getChildren().add(table);

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 460, 320);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX TableView");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

لتغيير أي قيمة في الجدول عليك أن تضع الفأرة على الجدول ثم تنقر مرة واحدة على الزر Enter.

بعد أن تدخل القيمة الجديدة و تنقر على الزر Enter, سيتم التشييك عليها لمعرفة ما إذا كان يمكن وضعها في الجدول أو لا لأنه لن يتم قبولها إلا إذا كان نوعها مطابق لنوع العامود.


 طريقة   إضافة و حذف و تعديل بيانات الـ TableView

المثال التالي يعلمك طريقة جعل المستخدم قادر على إضافة و حذف و تعديل بيانات الـ TableView.

مثال

User.java
      import javafx.beans.property.SimpleStringProperty;

      public class User {

      SimpleStringProperty name;
      SimpleStringProperty role;
      SimpleStringProperty email;

      public User() {
      this.name = new SimpleStringProperty("");
      this.role = new SimpleStringProperty("");
      this.email = new SimpleStringProperty("");
      }

      public User(String name, String role, String email) {
      this.name = new SimpleStringProperty(name);
      this.role = new SimpleStringProperty(role);
      this.email = new SimpleStringProperty(email);
      }

      public String getName() {
      return name.getValue();
      }

      public void setName(String name) {
      this.name = new SimpleStringProperty(name);
      }

      public String getRole() {
      return role.getValue();
      }

      public void setRole(String role) {
      this.role = new SimpleStringProperty(role);
      }

      public String getEmail() {
      return email.getValue();
      }

      public void setEmail(String email) {
      this.email = new SimpleStringProperty(email);
      }

      }
    

Main.java
      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.Alert;
      import javafx.scene.control.Alert.AlertType;
      import javafx.scene.control.Button;
      import javafx.scene.control.Label;
      import javafx.scene.control.SelectionMode;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableColumn.CellEditEvent;
      import javafx.scene.control.TableView;
      import javafx.scene.control.TextField;
      import javafx.scene.control.cell.PropertyValueFactory;
      import javafx.scene.control.cell.TextFieldTableCell;
      import javafx.stage.Stage;

      public class Main extends Application {

      @Override
      public void start(Stage stage) {

      // الذين سنضيفهم في النافذة Labels هنا قمنا بإنشاء جميع الـ
      Label labelName = new Label("Name");
      Label labelRole = new Label("Role");
      Label labelEmail = new Label("Email");
      Label labelTotalUsers = new Label("To Users: 0");

      // الذين سنضيفهم في النافذة Buttons هنا قمنا بإنشاء جميع الـ
      Button buttonAdd = new Button("Add Record");
      Button buttonDelete = new Button("Delete Selected");
      Button buttonDeleteAll = new Button("Delete All");

      // الذين سنضيفهم في النافذة TextFields هنا قمنا بإنشاء جميع الـ
      TextField fieldName = new TextField();
      TextField fieldRole = new TextField();
      TextField fieldEmail = new TextField();

      // يمثل الجدول الذي نريد إضافته في النافذة TableView هنا قمنا بإنشاء كائن من الكلاس
      TableView<User> table = new TableView();

      // يمثل مجموعة البيانات التي ستظهر في الجدول ObservableList هنا قمنا بإنشاء كائن من الكلاس
      // User كل سطر في هذا الجدول يمثل كائن من الكلاس
      ObservableList<User> data = FXCollections.observableArrayList();


      // فقط String هنا قمنا بإنشاء العامود الذي سيظهر أسماء المستخدمين و حددنا أنه يقبل قيم من النوع
      TableColumn<User, String> columnName = new TableColumn<>("Name");
      // تم إضافته في الجدول User الموجودة في كل كائن name سيُعرض فيه قيم الـ columnName هنا قلنا أن العامود
      columnName.setCellValueFactory( new PropertyValueFactory<>("name") );
      // يتيح لك إدخال قيمة جديدة TextField سيتم إظهار columnName عند النقر على أي قيمة موجودة في العامود
      columnName.setCellFactory(TextFieldTableCell.forTableColumn());
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnName.setOnEditCommit((CellEditEvent<User, String> t) -> {
      ((User) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setName(t.getNewValue());
      });


      // فقط String هنا قمنا بإنشاء العامود الذي سيظهر أدوار المستخدمين و حددنا أنه يقبل قيم من النوع
      TableColumn<User, String> columnRole = new TableColumn<>("Role");
      // تم إضافته في الجدول User الموجودة في كل كائن role سيُعرض فيه قيم الـ columnRole هنا قلنا أن العامود
      columnRole.setCellValueFactory( new PropertyValueFactory<>("role") );
      // يتيح لك إدخال قيمة جديدة TextField سيتم إظهار columnRole عند النقر على أي قيمة موجودة في العامود
      columnRole.setCellFactory(TextFieldTableCell.forTableColumn());
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnRole.setOnEditCommit((CellEditEvent<User, String> t) -> {
      ((User) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setRole(t.getNewValue());
      });


      // فقط String هنا قمنا بإنشاء العامود الذي سيظهر البريد الإلكتروني للمستخدمين و حددنا أنه يقبل قيم من النوع
      TableColumn<User, String> columnEmail = new TableColumn<>("Email");
      // تم إضافته في الجدول User الموجودة في كل كائن role سيُعرض فيه قيم الـ columnEmail هنا قلنا أن العامود
      columnEmail.setCellValueFactory( new PropertyValueFactory<>("email") );
      // يتيح لك إدخال قيمة جديدة TextField سيتم إظهار columnEmail عند النقر على أي قيمة موجودة في العامود
      columnEmail.setCellFactory(TextFieldTableCell.forTableColumn());
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnEmail.setOnEditCommit((CellEditEvent<User, String> t) -> {
      ((User) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setEmail(t.getNewValue());
      });


      // قابلة للتعديل table هنا جعلنا بيانات الجدول
      table.setEditable(true);
      // table هنا وضعنا الأعمدة الثلاثة في الكائن
      table.getColumns().addAll(columnName, columnRole, columnEmail);
      // table في الكائن data هنا قمنا بوضع بيانات الكائن
      table.setItems(data);
      // table هنا جعلنا المستخدم قادر على تحديد أكثر من سطر بداخل الـ
      table.getSelectionModel().setSelectionMode( SelectionMode.MULTIPLE );

      // في النافذة table هنا قمنا بتحديد حجم الكائن
      table.setPrefSize(500, 200);
      columnName.setPrefWidth(120);
      columnRole.setPrefWidth(180);
      columnEmail.setPrefWidth(200);
      labelName.setPrefSize(50, 30);
      labelRole.setPrefSize(50, 30);
      labelEmail.setPrefSize(50, 30);
      fieldName.setPrefSize(180, 30);
      fieldRole.setPrefSize(180, 30);
      fieldEmail.setPrefSize(180, 30);
      buttonAdd.setPrefSize(230, 30);
      labelTotalUsers.setPrefSize(230, 30);
      buttonDelete.setPrefSize(180, 30);
      buttonDeleteAll.setPrefSize(180, 30);

      // في النافذة table هنا قمنا بتحديد مكان ظهور الكائن
      table.setTranslateX(280);
      table.setTranslateY(25);
      labelName.setTranslateX(20);
      labelName.setTranslateY(45);
      labelRole.setTranslateX(20);
      labelRole.setTranslateY(85);
      labelEmail.setTranslateX(20);
      labelEmail.setTranslateY(125);
      fieldName.setTranslateX(70);
      fieldName.setTranslateY(45);
      fieldRole.setTranslateX(70);
      fieldRole.setTranslateY(85);
      fieldEmail.setTranslateX(70);
      fieldEmail.setTranslateY(125);
      buttonAdd.setTranslateX(20);
      buttonAdd.setTranslateY(175);
      labelTotalUsers.setTranslateX(20);
      labelTotalUsers.setTranslateY(250);
      buttonDelete.setTranslateX(330);
      buttonDelete.setTranslateY(250);
      buttonDeleteAll.setTranslateX(540);
      buttonDeleteAll.setTranslateY(250);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن table هنا قمنا بإضافة الكائن
      root.getChildren().add(table);
      root.getChildren().add(labelName);
      root.getChildren().add(labelRole);
      root.getChildren().add(labelEmail);
      root.getChildren().add(fieldName);
      root.getChildren().add(fieldRole);
      root.getChildren().add(fieldEmail);
      root.getChildren().add(buttonAdd);
      root.getChildren().add(labelTotalUsers);
      root.getChildren().add(buttonDelete);
      root.getChildren().add(buttonDeleteAll);

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 800, 300);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX TableView");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      // buttonAdd هنا قمنا بتحديد ماذا سيحدث عند النقر على الزر
      buttonAdd.setOnAction(Action -> {
      // email و role و name هنا قمنا بتخزين النصوص المدخلة في الحقول في المتغيرات
      String name = fieldName.getText();
      String role = fieldRole.getText();
      String email = fieldEmail.getText();
      // غير فارغة - أي فيها نص email و role و name هنا قلنا إذا كانت المتغيرات
      if (!name.equals("") && !role.equals("") && !email.equals("") ) {
      // سيتم إضافتها على سطر جديد في الجدول
      // labelTotalUsers ثم تحديث عدد المستخدمين الموجودين في الجدول و الذي يتم عرضه على الكائن
      // بعدها سيتم مسح النص المدخل في الحقول
      data.add(new User(name, role, email));
      labelTotalUsers.setText("Total Users: " + table.getItems().size());
      fieldName.setText("");
      fieldRole.setText("");
      fieldEmail.setText("");
      }
      else {
      // فارغاً سيتم إظهار رسالة تنبيه أمام المستخدم email و role و name إذا كان واحد من المتغيرات
      new Alert(AlertType.WARNING, "Name, Role and Email fields cannot be empty!").show();
      }
      });

      // buttonDelete هنا قمنا بتحديد ماذا سيحدث عند النقر على الزر
      buttonDelete.setOnAction(Action -> {
      // سيتم حذف أي سطر تم تحديده
      table.getItems().removeAll( table.getSelectionModel().getSelectedItems() );
      // labelTotalUsers ثم تحديث عدد المستخدمين الموجودين في الجدول و الذي يتم عرضه على الكائن
      labelTotalUsers.setText("Total Users: " + table.getItems().size());
      });

      // buttonDeleteAll هنا قمنا بتحديد ماذا سيحدث عند النقر على الزر
      buttonDeleteAll.setOnAction(Action -> {
      // سيتم حذف جميع الأسطر في الجدول
      table.getItems().clear();
      // labelTotalUsers ثم تحديث عدد المستخدمين الموجودين في الجدول و الذي يتم عرضه على الكائن
      labelTotalUsers.setText("Total Users: " + table.getItems().size());
      });

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

إذا أردت مسح أكثر من سطر, عليك إبقاء إصبعك على الزر ctrl حتى تستطيع تحديد أكثر من سطر في الجدول.


 طريقة إضافة ChoiceBox في الـ TableView 

المثال التالي يعلمك طريقة إضافة ChoiceBox في الـ TableView لجعل المستخدم قادر على اختيار قيم محددة.

لاحظ أنه عند محاولة تغيير قيمة موجودة في الحقل Name أو Email يظهر TextField عادي.
أما عند محاولة تغيير قيمة الحقل Role فإنه يظهر ChoiceBox.

مثال

User.java
      import javafx.beans.property.SimpleStringProperty;
      import javafx.scene.control.TableCell;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.cell.ChoiceBoxTableCell;
      import javafx.util.Callback;

      public class User {

      SimpleStringProperty name;
      SimpleStringProperty role;
      SimpleStringProperty email;

      public User() {
      this.name = new SimpleStringProperty("");
      this.role = new SimpleStringProperty("");
      this.email = new SimpleStringProperty("");
      }

      public User(String name, String role, String email) {
      this.name = new SimpleStringProperty(name);
      this.role = new SimpleStringProperty(role);
      this.email = new SimpleStringProperty(email);
      }

      public String getName() {
      return name.getValue();
      }

      public void setName(String name) {
      this.name = new SimpleStringProperty(name);
      }

      public String getRole() {
      return role.getValue();
      }

      public void setRole(String role) {
      this.role = new SimpleStringProperty(role);
      }

      public String getEmail() {
      return email.getValue();
      }

      public void setEmail(String email) {
      this.email = new SimpleStringProperty(email);
      }

      // TableView يمكن إضافته في كائن من الكلاس ChoiceBoxTableCell هنا قمنا بتعريف دالة تمرر لها مصفوفة فترجع كائن
      public static <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> forTableColumn(T... items) {
      return new Callback<TableColumn<S,T>, TableCell<S,T>>() {

      private Callback<TableColumn<S,T>, TableCell<S,T>> callback = ChoiceBoxTableCell.forTableColumn(items);

      @Override
      public TableCell<S,T> call(TableColumn<S,T> param) {
      ChoiceBoxTableCell<S,T> cell = (ChoiceBoxTableCell<S,T>) this.callback.call(param);
      cell.setEditable(true);
      return cell;
      }

      };
      }

      }
    

Main.java
      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.Alert;
      import javafx.scene.control.Alert.AlertType;
      import javafx.scene.control.Button;
      import javafx.scene.control.ChoiceBox;
      import javafx.scene.control.Label;
      import javafx.scene.control.SelectionMode;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableColumn.CellEditEvent;
      import javafx.scene.control.TableView;
      import javafx.scene.control.TextField;
      import javafx.scene.control.cell.PropertyValueFactory;
      import javafx.scene.control.cell.TextFieldTableCell;
      import javafx.stage.Stage;

      public class Main extends Application {

      @Override
      public void start(Stage stage) {

      // سيظهر في الجدول أو النافذة ChoiceBox هنا قمنا بتعريف مصفوفة الخيارات التي ستظهر في كل
      String[] usersRoles = { "Admin", "Editor" , "Author", "Contributor", "Subscriber"};

      // الذين سنضيفهم في النافذة Labels هنا قمنا بإنشاء جميع الـ
      Label labelName = new Label("Name");
      Label labelRole = new Label("Role");
      Label labelEmail = new Label("Email");
      Label labelTotalUsers = new Label("To Users: 0");

      // الذين سنضيفهم في النافذة Buttons هنا قمنا بإنشاء جميع الـ
      Button buttonAdd = new Button("Add Record");
      Button buttonDelete = new Button("Delete Selected");
      Button buttonDeleteAll = new Button("Delete All");

      // الذين سنضيفهم في النافذة TextFields هنا قمنا بإنشاء جميع الـ
      TextField fieldName = new TextField();
      TextField fieldEmail = new TextField();

      // usersRoles الذين سنضيفه في النافذة و مررنا له العناصر الموجودة في المصفوفة ChoiceBox هنا قمنا بإنشاء الـ
      ChoiceBox choiceBoxRole = new ChoiceBox(FXCollections.observableArrayList(usersRoles));

      // يمثل الجدول الذي نريد إضافته في النافذة TableView هنا قمنا بإنشاء كائن من الكلاس
      TableView<User> table = new TableView();

      // يمثل مجموعة البيانات التي ستظهر في الجدول ObservableList هنا قمنا بإنشاء كائن من الكلاس
      // User كل سطر في هذا الجدول يمثل كائن من الكلاس
      ObservableList<User> data = FXCollections.observableArrayList();


      // فقط String هنا قمنا بإنشاء العامود الذي سيظهر أسماء المستخدمين و حددنا أنه يقبل قيم من النوع
      TableColumn<User, String> columnName = new TableColumn<>("Name");
      // تم إضافته في الجدول User الموجودة في كل كائن name سيُعرض فيه قيم الـ columnName هنا قلنا أن العامود
      columnName.setCellValueFactory( new PropertyValueFactory<>("name") );
      // يتيح لك إدخال قيمة جديدة TextField سيتم إظهار columnName عند النقر على أي قيمة موجودة في العامود
      columnName.setCellFactory(TextFieldTableCell.forTableColumn());
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnName.setOnEditCommit((CellEditEvent<User, String> t) -> {
      ((User) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setName(t.getNewValue());
      });


      // فقط String هنا قمنا بإنشاء العامود الذي سيظهر أدوار المستخدمين و حددنا أنه يقبل قيم من النوع
      TableColumn<User, String> columnRole = new TableColumn<>("Role");
      // تم إضافته في الجدول User الموجودة في كل كائن role سيُعرض فيه قيم الـ columnRole هنا قلنا أن العامود
      columnRole.setCellValueFactory( new PropertyValueFactory<>("role") );
      // يتيح لك إختيار قيمة جديدة ChoiceBox سيتم إظهار columnRole عند النقر على أي قيمة موجودة في العامود
      columnRole.setCellFactory(User.forTableColumn(usersRoles));
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnRole.setOnEditCommit((CellEditEvent<User, String> t) -> {
      ((User) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setRole(t.getNewValue());
      });


      // فقط String هنا قمنا بإنشاء العامود الذي سيظهر البريد الإلكتروني للمستخدمين و حددنا أنه يقبل قيم من النوع
      TableColumn<User, String> columnEmail = new TableColumn<>("Email");
      // تم إضافته في الجدول User الموجودة في كل كائن role سيُعرض فيه قيم الـ columnEmail هنا قلنا أن العامود
      columnEmail.setCellValueFactory( new PropertyValueFactory<>("email") );
      // يتيح لك إدخال قيمة جديدة TextField سيتم إظهار columnEmail عند النقر على أي قيمة موجودة في العامود
      columnEmail.setCellFactory(TextFieldTableCell.forTableColumn());
      // String سيتم حفظ القيمة الجديدة في حال كان نوعها Enter عند النقر على الزر
      columnEmail.setOnEditCommit((CellEditEvent<User, String> t) -> {
      ((User) t.getTableView()
      .getItems()
      .get(t.getTablePosition().getRow()))
      .setEmail(t.getNewValue());
      });


      // قابلة للتعديل table هنا جعلنا بيانات الجدول
      table.setEditable(true);
      // table هنا وضعنا الأعمدة الثلاثة في الكائن
      table.getColumns().addAll(columnName, columnRole, columnEmail);
      // table في الكائن data هنا قمنا بوضع بيانات الكائن
      table.setItems(data);
      // table هنا جعلنا المستخدم قادر على تحديد أكثر من سطر بداخل الـ
      table.getSelectionModel().setSelectionMode( SelectionMode.MULTIPLE );

      // في النافذة table هنا قمنا بتحديد حجم الكائن
      table.setPrefSize(500, 200);
      columnName.setPrefWidth(120);
      columnRole.setPrefWidth(180);
      columnEmail.setPrefWidth(200);
      labelName.setPrefSize(50, 30);
      labelRole.setPrefSize(50, 30);
      labelEmail.setPrefSize(50, 30);
      fieldName.setPrefSize(180, 30);
      choiceBoxRole.setPrefSize(180, 30);
      fieldEmail.setPrefSize(180, 30);
      buttonAdd.setPrefSize(230, 30);
      labelTotalUsers.setPrefSize(230, 30);
      buttonDelete.setPrefSize(180, 30);
      buttonDeleteAll.setPrefSize(180, 30);

      // في النافذة table هنا قمنا بتحديد مكان ظهور الكائن
      table.setTranslateX(280);
      table.setTranslateY(25);
      labelName.setTranslateX(20);
      labelName.setTranslateY(45);
      labelRole.setTranslateX(20);
      labelRole.setTranslateY(85);
      labelEmail.setTranslateX(20);
      labelEmail.setTranslateY(125);
      fieldName.setTranslateX(70);
      fieldName.setTranslateY(45);
      choiceBoxRole.setTranslateX(70);
      choiceBoxRole.setTranslateY(85);
      fieldEmail.setTranslateX(70);
      fieldEmail.setTranslateY(125);
      buttonAdd.setTranslateX(20);
      buttonAdd.setTranslateY(175);
      labelTotalUsers.setTranslateX(20);
      labelTotalUsers.setTranslateY(250);
      buttonDelete.setTranslateX(330);
      buttonDelete.setTranslateY(250);
      buttonDeleteAll.setTranslateX(540);
      buttonDeleteAll.setTranslateY(250);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن table هنا قمنا بإضافة الكائن
      root.getChildren().add(table);
      root.getChildren().add(labelName);
      root.getChildren().add(labelRole);
      root.getChildren().add(labelEmail);
      root.getChildren().add(fieldName);
      root.getChildren().add(choiceBoxRole);
      root.getChildren().add(fieldEmail);
      root.getChildren().add(buttonAdd);
      root.getChildren().add(labelTotalUsers);
      root.getChildren().add(buttonDelete);
      root.getChildren().add(buttonDeleteAll);

      // مختاراً بشكل افتراضي choiceBoxRole هنا قمنا بجعل العنصر الأول في الـ
      choiceBoxRole.getSelectionModel().selectFirst();

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 800, 300);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX TableView");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      // buttonAdd هنا قمنا بتحديد ماذا سيحدث عند النقر على الزر
      buttonAdd.setOnAction(Action -> {
      // email و role و name في المتغيرات fieldEmail و الـ ChoiceBox و الـ FieldName هنا قمنا بتخزين نص الـ
      String name = fieldName.getText();
      String role = choiceBoxRole.getSelectionModel().getSelectedItem().toString();
      String email = fieldEmail.getText();
      // غير فارغين - أي فيهما نص email و name هنا قلنا إذا كان المتغيران
      if (!name.equals("") && !email.equals("") ) {
      // على سطر جديد في الجدول email و role و name سيتم إضافة قيم المتغيرات
      // labelTotalUsers ثم تحديث عدد المستخدمين الموجودين في الجدول و الذي يتم عرضه على الكائن
      // ChoiceBox و اختيار العنصر الأول في الـ fieldEmail و fieldName بعدها سيتم مسح النص المدخل في الحقلين
      data.add(new User(name, role, email));
      labelTotalUsers.setText("Total Users: " + table.getItems().size());
      fieldName.setText("");
      choiceBoxRole.getSelectionModel().selectFirst();
      fieldEmail.setText("");
      }
      else {
      // فارغاً سيتم إظهار رسالة تنبيه أمام المستخدم email و name إذا كان واحد من المتغيرات
      new Alert(AlertType.WARNING, "Name and Email fields cannot be empty!").show();
      }
      });

      // buttonDelete هنا قمنا بتحديد ماذا سيحدث عند النقر على الزر
      buttonDelete.setOnAction(Action -> {
      // سيتم حذف أي سطر تم تحديده
      table.getItems().removeAll( table.getSelectionModel().getSelectedItems() );
      // labelTotalUsers ثم تحديث عدد المستخدمين الموجودين في الجدول و الذي يتم عرضه على الكائن
      labelTotalUsers.setText("Total Users: " + table.getItems().size());
      });

      // buttonDeleteAll هنا قمنا بتحديد ماذا سيحدث عند النقر على الزر
      buttonDeleteAll.setOnAction(Action -> {
      // سيتم حذف جميع الأسطر في الجدول
      table.getItems().clear();
      // labelTotalUsers ثم تحديث عدد المستخدمين الموجودين في الجدول و الذي يتم عرضه على الكائن
      labelTotalUsers.setText("Total Users: " + table.getItems().size());
      });

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

إذا أردت مسح أكثر من سطر, عليك إبقاء إصبعك على الزر ctrl حتى تستطيع تحديد أكثر من سطر في الجدول.

طريقة إضافة ChoiceBox في الـ TableView

أفضل كورس تعلم javaFX