JavaFXطريقة جعل المستخدم قادر على إضافة و حذف و تعديل بيانات الـTreeView
المثال التالي يعلمك طريقة جعل المستخدم قادر على إضافة و حذف و تعديل بيانات الـ TreeView بطريقة إحترافية.
ملاحظة: عليك إنشاء مجلد و وضع الصور فيه كما في الصورة التالية.
الصورة توضح أننا أضفنا مجلد إسمه images و وضعنا فيه أربع صور.
مثال
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
// متطورة TreeItem و بالتالي أصبح إنشاء كائن منه يعني إنشاء TreeCell هنا قمنا بتعريف كلاس يرث من الكلاس
public class EditableItem extends TreeCell<String> {
// يمثل قسم في الشركة TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكل كائن
public ImageView getDepartmentIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/department-icon.png")));
}
// يمثل موظف في الشركة TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكل كائن
public ImageView getUserMaleIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/user-male-icon.png")));
}
// يمثل موظفة في الشركة TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكل كائن
public ImageView getUserFemaleIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/user-female-icon.png")));
}
// TreeItem في كل مرة يريد فيها المستخدم أن يقوم بتعديل نص TextField لأننا سنظهر textField هنا قمنا بتعريف كائن
private TextField textField;
// الذي يمثل الشركة Root Item هنا قمنا بتعريف القائمة التي سنظهرها عندما ينقر المستخدمم بزر الفأرة الأيمن على الـ
private final ContextMenu menuForRootItem = new ContextMenu();
// هنا قمنا بتعريف القائمة التي سنظهرها عندما ينقر المستخدمم بزر الفأرة الأيمن على أي قسم موجود في الشركة
private final ContextMenu menuForAnyDepartment = new ContextMenu();
// هنا قمنا بتعريف القائمة التي سنظهرها عندما ينقر المستخدمم بزر الفأرة الأيمن على أي مستخدم موجود بداخل قسم
private final ContextMenu menuForAnyUser = new ContextMenu();
// الكونستركتور
public EditableItem() {
// هنا قمنا بتعريف عناصر القائمة التي تظهر عند النقر بزر الفأرة الأيمن على الشركة
MenuItem addNewDepartment = new MenuItem("Add new department");
MenuItem deleteAllDepartments = new MenuItem("Delete all departments");
// هنا قمنا بتعريف عناصر القائمة التي تظهر عند النقر بزر الفأرة الأيمن على أي قسم
MenuItem addNewMale = new MenuItem("Add a male employee");
MenuItem addNewFemale = new MenuItem("Add a female employee");
MenuItem deleteAllEmployees = new MenuItem("Delete all employees");
MenuItem deleteDepartment = new MenuItem("Delete department");
// هنا قمنا بتعريف عنصر القائمة الذي يظهر عند النقر بزر الفأرة الأيمن على أي موظف أو موظفة
MenuItem deleteUser = new MenuItem("Delete User");
// فيها menuForRootItem هنا قمنا بإضافة عناصر على القائمة
menuForRootItem.getItems().addAll(addNewDepartment, deleteAllDepartments);
// فيها menuForRootDepartment هنا قمنا بإضافة العناصر عناصر القائمة
menuForAnyDepartment.getItems().addAll(addNewMale, addNewFemale, deleteAllEmployees, deleteDepartment);
// فيها menuForRootItem هنا قمنا بإضافة عنصر على القائمة
menuForAnyUser.getItems().addAll(deleteUser);
// addNewDepartment هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
addNewDepartment.setOnAction((ActionEvent t) -> {
TreeItem newDepartment = new TreeItem<>("New Department", getDepartmentIcon());
getTreeItem().getChildren().add(newDepartment);
newDepartment.setExpanded(true);
});
// deleteAllDepartments هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteAllDepartments.setOnAction((ActionEvent t) -> {
getTreeItem().getChildren().remove(0, getTreeItem().getChildren().size());
});
// addNewMale هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
addNewMale.setOnAction((ActionEvent t) -> {
TreeItem newDepartment = new TreeItem<>("New Employee", getUserMaleIcon());
getTreeItem().getChildren().add(newDepartment);
});
// addNewFemale هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
addNewFemale.setOnAction((ActionEvent t) -> {
TreeItem newDepartment = new TreeItem<>("New Employee", getUserFemaleIcon());
getTreeItem().getChildren().add(newDepartment);
});
// deleteAllEmployees هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteAllEmployees.setOnAction((ActionEvent t) -> {
getTreeItem().getChildren().remove(0, getTreeItem().getChildren().size());
});
// deleteDepartment هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteDepartment.setOnAction((ActionEvent t) -> {
getTreeItem().getParent().getChildren().remove(getTreeItem());
});
// deleteUser هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteUser.setOnAction((ActionEvent t) -> {
getTreeItem().getParent().getChildren().remove(getTreeItem());
});
}
// بداخله بعد TreeItem أو ترجع نص فارغ في حال كان لا يحتوي على EditableItem هنا قمنا بتعريف دالة ترجع نص الـ
// و هذا يعني أنه في حال كان قد تم إنشاؤه فإننا لن نفقد القيمة الموجودة فيه
private String getString() {
return getItem() == null ? "" : getItem();
}
// EditableItem عند النقر على الـ TextField هنا قمنا بتعريف الدالة التي تنشئ
private void createTextField() {
// getString() فيه و الذي ترجعه الدالة EditableItem و تمرير النص الذي كان ظاهراً على الـ TextField هنا سيتم إنشاء الـ
textField = new TextField(getString());
// EditableItem هنا قمنا بفحص كل حرف يدخله المستخدم عند تعديل نص الـ
textField.setOnKeyReleased((KeyEvent t) -> {
// سيتم حفظ التعديلات Enter إذا قام المستخدم بالنقر على الزر
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
}
// سيتم إلغاء التعديلات Esc إذا قام المستخدم بالنقر على الزر
else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
}
// TextField التي تنشئ createTextField() سيتم إستدعاء الدالة EditableItem هنا قلنا أنه عند تعديل نص الـ
@Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
// سيتم إعادة النص الذي كان موجوداً من قبل EditableItem هنا قلنا أنه عند إلغاء تعديل نص الـ
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(getTreeItem().getGraphic());
}
// بأي شكلٍ كان EditableItem هنا قمنا بتحديد ما سيحدث عند محاولة تعديل الـ
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
// TreeView في حال تم إلغاؤه قلنا أنه سيتم مسحه من الـ
if (empty) {
setText(null);
setGraphic(null);
}
// في حال تم التفاعل معه بأي طريقة
else {
// مكانه ثم تحديث النافذة لإظهار النص الجديد TextField في حال كان يتم تعديل نصه سيتم وضع النص الذي تم إدخاله في الـ
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
}
// في حال تم النقر عليه بواسطة زر الفأرة الأيمن
else {
// سيتم تحديث النافذة لضمان أن يتم إظهاره بشكل صحيح
setText(getString());
setGraphic(getTreeItem().getGraphic());
// TreeView ثم سيتم إظهار القائمة الخاصة بالشركة إذا تم النقر على أول شيء في الـ
if (getTreeItem().getParent() == null) {
setContextMenu(menuForRootItem);
}
// TreeView أو سيتم إظهار القائمة الخاصة بالأقسام إذا تم النقر على أي شيء موضوع تحت الشركة في الـ
else if( getTreeItem().getParent().getParent() == null) {
setContextMenu(menuForAnyDepartment);
}
// TreeView أو سيتم إظهار القائمة الخاصة بالموظفين و المظفات إذا تم النقر على أي شيء موضوع تحت أي قسم في الـ
else if( getTreeItem().getParent().getParent().getParent() == null) {
setContextMenu(menuForAnyUser);
}
}
}
}
}
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
// متطورة TreeItem و بالتالي أصبح إنشاء كائن منه يعني إنشاء TreeCell هنا قمنا بتعريف كلاس يرث من الكلاس
public class EditableItem extends TreeCell<String> {
// يمثل قسم في الشركة TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكل كائن
public ImageView getDepartmentIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/department-icon.png")));
}
// يمثل موظف في الشركة TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكل كائن
public ImageView getUserMaleIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/user-male-icon.png")));
}
// يمثل موظفة في الشركة TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكل كائن
public ImageView getUserFemaleIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/user-female-icon.png")));
}
// TreeItem في كل مرة يريد فيها المستخدم أن يقوم بتعديل نص TextField لأننا سنظهر textField هنا قمنا بتعريف كائن
private TextField textField;
// الذي يمثل الشركة Root Item هنا قمنا بتعريف القائمة التي سنظهرها عندما ينقر المستخدمم بزر الفأرة الأيمن على الـ
private final ContextMenu menuForRootItem = new ContextMenu();
// هنا قمنا بتعريف القائمة التي سنظهرها عندما ينقر المستخدمم بزر الفأرة الأيمن على أي قسم موجود في الشركة
private final ContextMenu menuForAnyDepartment = new ContextMenu();
// هنا قمنا بتعريف القائمة التي سنظهرها عندما ينقر المستخدمم بزر الفأرة الأيمن على أي مستخدم موجود بداخل قسم
private final ContextMenu menuForAnyUser = new ContextMenu();
// الكونستركتور
public EditableItem() {
// هنا قمنا بتعريف عناصر القائمة التي تظهر عند النقر بزر الفأرة الأيمن على الشركة
MenuItem addNewDepartment = new MenuItem("Add new department");
MenuItem deleteAllDepartments = new MenuItem("Delete all departments");
// هنا قمنا بتعريف عناصر القائمة التي تظهر عند النقر بزر الفأرة الأيمن على أي قسم
MenuItem addNewMale = new MenuItem("Add a male employee");
MenuItem addNewFemale = new MenuItem("Add a female employee");
MenuItem deleteAllEmployees = new MenuItem("Delete all employees");
MenuItem deleteDepartment = new MenuItem("Delete department");
// هنا قمنا بتعريف عنصر القائمة الذي يظهر عند النقر بزر الفأرة الأيمن على أي موظف أو موظفة
MenuItem deleteUser = new MenuItem("Delete User");
// فيها menuForRootItem هنا قمنا بإضافة عناصر على القائمة
menuForRootItem.getItems().addAll(addNewDepartment, deleteAllDepartments);
// فيها menuForRootDepartment هنا قمنا بإضافة العناصر عناصر القائمة
menuForAnyDepartment.getItems().addAll(addNewMale, addNewFemale, deleteAllEmployees, deleteDepartment);
// فيها menuForRootItem هنا قمنا بإضافة عنصر على القائمة
menuForAnyUser.getItems().addAll(deleteUser);
// addNewDepartment هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
addNewDepartment.setOnAction((ActionEvent t) -> {
TreeItem newDepartment = new TreeItem<>("New Department", getDepartmentIcon());
getTreeItem().getChildren().add(newDepartment);
newDepartment.setExpanded(true);
});
// deleteAllDepartments هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteAllDepartments.setOnAction((ActionEvent t) -> {
getTreeItem().getChildren().remove(0, getTreeItem().getChildren().size());
});
// addNewMale هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
addNewMale.setOnAction((ActionEvent t) -> {
TreeItem newDepartment = new TreeItem<>("New Employee", getUserMaleIcon());
getTreeItem().getChildren().add(newDepartment);
});
// addNewFemale هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
addNewFemale.setOnAction((ActionEvent t) -> {
TreeItem newDepartment = new TreeItem<>("New Employee", getUserFemaleIcon());
getTreeItem().getChildren().add(newDepartment);
});
// deleteAllEmployees هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteAllEmployees.setOnAction((ActionEvent t) -> {
getTreeItem().getChildren().remove(0, getTreeItem().getChildren().size());
});
// deleteDepartment هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteDepartment.setOnAction((ActionEvent t) -> {
getTreeItem().getParent().getChildren().remove(getTreeItem());
});
// deleteUser هنا قمنا بتحديد ما سيحدث عند النقر على العنصر
deleteUser.setOnAction((ActionEvent t) -> {
getTreeItem().getParent().getChildren().remove(getTreeItem());
});
}
// بداخله بعد TreeItem أو ترجع نص فارغ في حال كان لا يحتوي على EditableItem هنا قمنا بتعريف دالة ترجع نص الـ
// و هذا يعني أنه في حال كان قد تم إنشاؤه فإننا لن نفقد القيمة الموجودة فيه
private String getString() {
return getItem() == null ? "" : getItem();
}
// EditableItem عند النقر على الـ TextField هنا قمنا بتعريف الدالة التي تنشئ
private void createTextField() {
// getString() فيه و الذي ترجعه الدالة EditableItem و تمرير النص الذي كان ظاهراً على الـ TextField هنا سيتم إنشاء الـ
textField = new TextField(getString());
// EditableItem هنا قمنا بفحص كل حرف يدخله المستخدم عند تعديل نص الـ
textField.setOnKeyReleased((KeyEvent t) -> {
// سيتم حفظ التعديلات Enter إذا قام المستخدم بالنقر على الزر
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
}
// سيتم إلغاء التعديلات Esc إذا قام المستخدم بالنقر على الزر
else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
}
// TextField التي تنشئ createTextField() سيتم إستدعاء الدالة EditableItem هنا قلنا أنه عند تعديل نص الـ
@Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
// سيتم إعادة النص الذي كان موجوداً من قبل EditableItem هنا قلنا أنه عند إلغاء تعديل نص الـ
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(getTreeItem().getGraphic());
}
// بأي شكلٍ كان EditableItem هنا قمنا بتحديد ما سيحدث عند محاولة تعديل الـ
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
// TreeView في حال تم إلغاؤه قلنا أنه سيتم مسحه من الـ
if (empty) {
setText(null);
setGraphic(null);
}
// في حال تم التفاعل معه بأي طريقة
else {
// مكانه ثم تحديث النافذة لإظهار النص الجديد TextField في حال كان يتم تعديل نصه سيتم وضع النص الذي تم إدخاله في الـ
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
}
// في حال تم النقر عليه بواسطة زر الفأرة الأيمن
else {
// سيتم تحديث النافذة لضمان أن يتم إظهاره بشكل صحيح
setText(getString());
setGraphic(getTreeItem().getGraphic());
// TreeView ثم سيتم إظهار القائمة الخاصة بالشركة إذا تم النقر على أول شيء في الـ
if (getTreeItem().getParent() == null) {
setContextMenu(menuForRootItem);
}
// TreeView أو سيتم إظهار القائمة الخاصة بالأقسام إذا تم النقر على أي شيء موضوع تحت الشركة في الـ
else if( getTreeItem().getParent().getParent() == null) {
setContextMenu(menuForAnyDepartment);
}
// TreeView أو سيتم إظهار القائمة الخاصة بالموظفين و المظفات إذا تم النقر على أي شيء موضوع تحت أي قسم في الـ
else if( getTreeItem().getParent().getParent().getParent() == null) {
setContextMenu(menuForAnyUser);
}
}
}
}
}
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
public class Main extends Application {
// و التي سنقصد بها الشركة Root Item الذي يمثل الـ TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكائن الـ
public ImageView getRootIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/building-icon.png")));
}
@Override
public void start(Stage stage) {
// getRootIcon() و مررنا له أيقونة المبنى التي سترجعها الدالة TreeView في الـ Root Item و الذي سنضعه كـ TreeItem هنا قمنا بإنشاء كائن من الكلاس
TreeItem<String> rootItem = new TreeItem<>("Root Item", getRootIcon());
// يمثل القائمة الشجرية التي نريد إضافتها في النافذة TreeView هنا قمنا بإنشاء كائن من الكلاس
TreeView<String> treeView = new TreeView<>(rootItem);
// يظهر عند تشغيل البرنامج rootItem موضوع مباشرةً تحت الـ TreeItem هنا جعلنا كل كائن
rootItem.setExpanded(true);
// في النافذةView هنا قمنا بتحديد حجم الـ
treeView.setPrefSize(400, 250);
// في النافذةView هنا قمنا بتحديد مكان ظهور الكائن
treeView.setTranslateX(0);
treeView.setTranslateY(0);
// قابل للتعديل treeView هنا جعلنا محتوى الـ
treeView.setEditable(true);
// EditableItem سيتم تعديله سيتم معاملته على أنه كائن من الكلاس treeView هنا قلنا أن أي شيء بداخل الـ
treeView.setCellFactory(
(TreeView<String> p) -> new EditableItem()
);
// في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
Group root = new Group();
// root في الكائنView هنا قمنا بإضافة الكائن
root.getChildren().add(treeView);
// فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
Scene scene = new Scene(root, 400, 250);
// هنا وضعنا عنوان للنافذة
stage.setTitle("JavaFX TreeView");
// أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
stage.setScene(scene);
// هنا قمنا بإظهار النافذة
stage.show();
}
// هنا قمنا بتشغيل التطبيق
public static void main(String[] args) {
launch(args);
}
}
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
public class Main extends Application {
// و التي سنقصد بها الشركة Root Item الذي يمثل الـ TreeItem هنا قمنا بتعريف دالة ترجع الأيقونة التي سنضعها لكائن الـ
public ImageView getRootIcon() {
return new ImageView(new Image(getClass().getResourceAsStream("images/building-icon.png")));
}
@Override
public void start(Stage stage) {
// getRootIcon() و مررنا له أيقونة المبنى التي سترجعها الدالة TreeView في الـ Root Item و الذي سنضعه كـ TreeItem هنا قمنا بإنشاء كائن من الكلاس
TreeItem<String> rootItem = new TreeItem<>("Root Item", getRootIcon());
// يمثل القائمة الشجرية التي نريد إضافتها في النافذة TreeView هنا قمنا بإنشاء كائن من الكلاس
TreeView<String> treeView = new TreeView<>(rootItem);
// يظهر عند تشغيل البرنامج rootItem موضوع مباشرةً تحت الـ TreeItem هنا جعلنا كل كائن
rootItem.setExpanded(true);
// في النافذةView هنا قمنا بتحديد حجم الـ
treeView.setPrefSize(400, 250);
// في النافذةView هنا قمنا بتحديد مكان ظهور الكائن
treeView.setTranslateX(0);
treeView.setTranslateY(0);
// قابل للتعديل treeView هنا جعلنا محتوى الـ
treeView.setEditable(true);
// EditableItem سيتم تعديله سيتم معاملته على أنه كائن من الكلاس treeView هنا قلنا أن أي شيء بداخل الـ
treeView.setCellFactory(
(TreeView<String> p) -> new EditableItem()
);
// في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
Group root = new Group();
// root في الكائنView هنا قمنا بإضافة الكائن
root.getChildren().add(treeView);
// فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
Scene scene = new Scene(root, 400, 250);
// هنا وضعنا عنوان للنافذة
stage.setTitle("JavaFX TreeView");
// أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
stage.setScene(scene);
// هنا قمنا بإظهار النافذة
stage.show();
}
// هنا قمنا بتشغيل التطبيق
public static void main(String[] args) {
launch(args);
}
}
ستظهر لك النافذة التالية عند التشغيل.