JavaFXطريقة إنشاء برنامج لحساب الأعمار
في هذا الدرس ستتعلم طريقة إنشاء برنامجين لحساب الأعمار ( Age Calculator ) و باستخدام JavaFX.
مميزات كلا البرنامجين
- يمكن إستخدام أي برنامج منهما لحساب عمر الشخص الحي أو المدة التي عاشها الشخص المتوفي.
- لحساب عمر الشخص الحي, ضع فقط تاريخ ميلاده.
- لحساب المدة التي عاشها الشخص المتوفي, ضع تاريخ ميلاده و تاريخ وفاته.
البرنامج الأول
⇓ تحميل البرنامج ⇓ تحميل المشروع كاملاً
كود البرنامج
import java.time.LocalDate; import java.time.Period; import javafx.application.Application; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.paint.Color; import javafx.stage.Stage; public class Main extends Application { // هنا قمنا بتعريف الأشياء التي سيتم وضعها في النافذة Label birthDateLabel, resultLabel; CheckBox deathDateCheckBox; Button calculateAgeButton; ComboBox dayOfBirth, monthOfBirth, yearOfBirth, dayOfDeath, monthOfDeath, yearOfDeath; // سنستخدم هذه المتغيرات لتخزين التواريخ التي إختارها المستخدم من النافذة بشكل مؤقت int d1, d2, m1, m2, y1, y2; // لتخزين تاريخ وفاته endDate لتخزين تاريخ ميلاد الشخص, و الكائن startDate سنستخدم الكائن LocalDate startDate, endDate; // سنستخدم هذه المتغيرات لعرض الفارق النهائي بين تاريخ الميلاد و التاريخ الحالي أو تاريخ الوفاة long daysCounter, monthsCounter, yearsCounter; // مفعلاً أو غير مفعل (deathDatePicker) قمنا ببناء هذه الدالة لتحديد متى سيكون الكائن الذي يمثل تاريخ الوفاة public void toggleDeathDatePicker() { // deathDatePicker يوجد عليه علامة صح, سيتم تفعيل الكائن deathDateCheckBox إذا كان الكائن if (deathDateCheckBox.isSelected()) { dayOfDeath.setDisable(false); monthOfDeath.setDisable(false); yearOfDeath.setDisable(false); } // deathDatePicker لا يوجد عليه علامة صح, سيتم إلغاء تفعيل الكائن deathDateCheckBox إذا كان الكائن else { dayOfDeath.setDisable(true); monthOfDeath.setDisable(true); yearOfDeath.setDisable(true); } } // قمنا ببناء هذه الدالة لحساب و عرض العمر سواء كان المستخدم يريد حساب عمر شخص على قيد الحياة أو شخص ميت public void displayAge() { // سنستخدم هذا المتغير لتخزين نص النتيجة التي ستظهر String text = ""; // هنا قمنا بتخزين تاريخ الميلاد الذي إختاره المستخدم في المتغيرات التالية y1 = Integer.parseInt(yearOfBirth.getSelectionModel().getSelectedItem().toString()); m1 = monthOfBirth.getSelectionModel().getSelectedIndex() + 1; d1 = Integer.parseInt(dayOfBirth.getSelectionModel().getSelectedItem().toString()); // إذا كان خيار تحديد تاريخ الوفاة مفعل, سيتم تخزين التاريخ المحدد في المتغيرات التالية if (deathDateCheckBox.isSelected()) { y2 = Integer.parseInt(yearOfDeath.getSelectionModel().getSelectedItem().toString()); m2 = monthOfDeath.getSelectionModel().getSelectedIndex() + 1; d2 = Integer.parseInt(dayOfDeath.getSelectionModel().getSelectedItem().toString()); } // إذا كان خيار تحديد تاريخ الوفاة غير مفعل, سيتم تخزين التاريخ الحالي في المتغيرات التالية else { y2 = LocalDate.now().getYear(); m2 = LocalDate.now().getMonthValue(); d2 = LocalDate.now().getDayOfMonth(); } // endDate و تاريخ الوفاة في الكائن startDate هنا قمنا بوضع تاريخ الميلاد الذي حدده المستخدم في الكائن startDate = LocalDate.of(y1, m1, d1); endDate = LocalDate.of(y2, m2, d2); // هنا قمنا بتخزين الفارق بين التواريخ في المتغيرات التالية yearsCounter = Period.between(startDate, endDate).getYears(); monthsCounter = Period.between(startDate, endDate).getMonths(); daysCounter = Period.between(startDate, endDate).getDays(); // هنا قلنا إذا كان التاريخين متواسيين سيتم إظهار التنبيه التالي if (yearsCounter == 0 && monthsCounter == 0 && daysCounter == 0) { resultLabel.setTextFill(Color.RED); resultLabel.setText("Cannot compare same date!"); } // هنا قلنا أنه إذا كانت التواريخ المحددة هي منطقياً صحيحة سيتم إظهار العمر else if (!Period.between(startDate, endDate).isNegative()) { if (yearsCounter == 1) { text += yearsCounter + " year "; } else if (yearsCounter > 1) { text += yearsCounter + " years "; } if (monthsCounter == 1) { if (yearsCounter > 0 && daysCounter > 0) { text += ", " + monthsCounter + " month "; } else if (yearsCounter > 0 && daysCounter == 0) { text += "and " + monthsCounter + " month "; } else { text += monthsCounter + " month "; } } if (monthsCounter > 1) { if (yearsCounter > 0 && daysCounter > 0) { text += ", " + monthsCounter + " months "; } else if (yearsCounter > 0 && daysCounter == 0) { text += "and " + monthsCounter + " months "; } else { text += monthsCounter + " months "; } } if (daysCounter == 1) { if (yearsCounter == 0 && monthsCounter == 0) { text += daysCounter + " day"; } else { text += "and " + daysCounter + " day"; } } if (daysCounter > 1) { if (yearsCounter == 0 && monthsCounter == 0) { text += daysCounter + " days"; } else { text += "and " + daysCounter + " days"; } } resultLabel.setTextFill(Color.BLACK); resultLabel.setText(text); } // هنا قلنا أنه إذا كانت التواريخ المحددة هي منطقياً غير مقبولة سيتم إظهار التنبيه التالي else { resultLabel.setTextFill(Color.RED); resultLabel.setText("Logic order of Dates is wrong!"); } } public void start(Stage stage) { // هنا وضعنا أسماء الأشهر التي سنظهرها في قائمة الأشهر String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; // هنا وضعنا أرقام الأيام التي سنظهرها في قائمة الأيام Integer[] days = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; // currentYear هنا قمنا بتخزين رقم السنة الحالية في المتغير int currentYear = LocalDate.now().getYear(); // هنا قمنا بإنشاء مصفوفة, عدد عناصرها يساوي رقم السنة الحالية Integer[] years = new Integer[currentYear]; // هنا قمنا بتخزين أرقام السنين من 1 إلى رقم السنة الحالية بشكل عكسي for (int i = 0; i < currentYear; i++) years[currentYear-i-1] = i + 1; // هنا قمنا بإنشاء جميع الأشياء التي سنضيفها في النافذة birthDateLabel = new Label("Birth Date"); deathDateCheckBox = new CheckBox("Death Date"); resultLabel = new Label(); calculateAgeButton = new Button("Calculate Age"); dayOfBirth = new ComboBox(FXCollections.observableArrayList(days)); monthOfBirth = new ComboBox(FXCollections.observableArrayList(months)); yearOfBirth = new ComboBox(FXCollections.observableArrayList(years)); dayOfDeath = new ComboBox(FXCollections.observableArrayList(days)); monthOfDeath = new ComboBox(FXCollections.observableArrayList(months)); yearOfDeath = new ComboBox(FXCollections.observableArrayList(years)); // هنا قما بتعديل حجم جميع الأشياء التي سنضيفها في النافذة birthDateLabel.setPrefSize(150, 30); deathDateCheckBox.setPrefSize(150, 30); dayOfBirth.setPrefSize(65, 30); monthOfBirth.setPrefSize(65, 30); yearOfBirth.setPrefSize(70, 30); dayOfDeath.setPrefSize(65, 30); monthOfDeath.setPrefSize(65, 30); yearOfDeath.setPrefSize(70, 30); calculateAgeButton.setPrefSize(215, 50); resultLabel.setPrefSize(430, 30); // هنا قمنا بتحديد مكان ظهور كل شيء سيتم إضافته في الحاوية الرئيسية التي سنضعها في النافذة birthDateLabel.setTranslateX(174); birthDateLabel.setTranslateY(40); dayOfBirth.setTranslateX(110); dayOfBirth.setTranslateY(80); monthOfBirth.setTranslateX(182.5); monthOfBirth.setTranslateY(80); yearOfBirth.setTranslateX(255); yearOfBirth.setTranslateY(80); deathDateCheckBox.setTranslateX(150); deathDateCheckBox.setTranslateY(150); dayOfDeath.setTranslateX(110); dayOfDeath.setTranslateY(190); monthOfDeath.setTranslateX(182.5); monthOfDeath.setTranslateY(190); yearOfDeath.setTranslateX(255); yearOfDeath.setTranslateY(190); calculateAgeButton.setTranslateX(110); calculateAgeButton.setTranslateY(260); resultLabel.setTranslateX(0); resultLabel.setTranslateY(340); // يظهر جهة الوسط resultLabel هنا جعلنا نص الكائن resultLabel.setAlignment(Pos.CENTER); dayOfBirth.getSelectionModel().selectFirst(); monthOfBirth.getSelectionModel().selectFirst(); yearOfBirth.getSelectionModel().select(0); dayOfDeath.getSelectionModel().selectFirst(); monthOfDeath.getSelectionModel().selectFirst(); yearOfDeath.getSelectionModel().select(0); // هنا قما بتعديل حجم و سمك خط جميع الأشياء التي سنضيفها في النافذة birthDateLabel.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); deathDateCheckBox.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); resultLabel.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); calculateAgeButton.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); // في النافذة لأننا ننوي ترتيب العناصر فيه بشكل يدويRoot Node و الذي ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس Group root = new Group(); // هنا قمنا بإضافة جميع الأشياء التي قمنا بإنشائها في النافذة root.getChildren().add(birthDateLabel); root.getChildren().add(deathDateCheckBox); root.getChildren().add(resultLabel); root.getChildren().add(calculateAgeButton); root.getChildren().add(dayOfBirth); root.getChildren().add(monthOfBirth); root.getChildren().add(yearOfBirth); root.getChildren().add(dayOfDeath); root.getChildren().add(monthOfDeath); root.getChildren().add(yearOfDeath); // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن Scene scene = new Scene(root, 430, 420); // هنا وضعنا عنوان للنافذة stage.setTitle("Age Calculator"); // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ stage.setScene(scene); // هنا قمنا بإظهار النافذة stage.show(); // هنا جعلنا المستخدم غير قادر على تكبير أو تصغير حجم النافذة بنفسه stage.setResizable(false); // calculateAgeButton هنا قمنا بتحديد ماذا سيحدث عند النقر على الكائن // حتى تحسب و تعرض العمر displayAge() فعلياً سيتم إستدعاء الدالة calculateAgeButton.setOnAction((Action) -> { displayAge(); }); // deathDateCheckBox هنا قمنا بتحديد ماذا سيحدث عند النقر على الكائن // deathDatePicker لتفعيل أو إلغاء تفعيل الكائن toggleDeathDatePicker() فعلياً سيتم إستدعاء الدالة deathDateCheckBox.selectedProperty().addListener( (ObservableValue<? extends Boolean> ov, Boolean old_val, Boolean new_val) -> { toggleDeathDatePicker(); } ); // غير مفعل بشكل إفتراضي عند تشغيل البرنامج toggleDeathDatePicker لجعل الكائن toggleDeathDatePicker() هنا قمنا باستدعاء الدالة toggleDeathDatePicker(); } // هنا قمنا بتشغيل التطبيق public static void main(String[] args) { launch(args); } }
البرنامج الثاني
⇓ تحميل البرنامج ⇓ تحميل المشروع كاملاً
كود البرنامج
import java.time.LocalDate; import java.time.Period; import javafx.application.Application; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.stage.Stage; public class Main extends Application { // هنا قمنا بتعريف الأشياء التي سيتم وضعها في النافذة Label birthDateLabel, resultLabel; CheckBox deathDateCheckBox; Button calculateAgeButton; DatePicker birthDatePicker, deathDatePicker; // سنستخدم هذه المتغيرات لتخزين التواريخ التي إختارها المستخدم من النافذة بشكل مؤقت int d1, d2, m1, m2, y1, y2; // لتخزين تاريخ وفاته endDate لتخزين تاريخ ميلاد الشخص, و الكائن startDate سنستخدم الكائن LocalDate startDate, endDate; // سنستخدم هذه المتغيرات لعرض الفارق النهائي بين تاريخ الميلاد و التاريخ الحالي أو تاريخ الوفاة long daysCounter, monthsCounter, yearsCounter; // مفعلاً أو غير مفعل (deathDatePicker) قمنا ببناء هذه الدالة لتحديد متى سيكون الكائن الذي يمثل تاريخ الوفاة public void toggleDeathDatePicker() { // deathDatePicker يوجد عليه علامة صح, سيتم تفعيل الكائن deathDateCheckBox إذا كان الكائن if (deathDateCheckBox.isSelected()) { deathDatePicker.setDisable(false); } // deathDatePicker لا يوجد عليه علامة صح, سيتم إلغاء تفعيل الكائن deathDateCheckBox إذا كان الكائن else { deathDatePicker.setDisable(true); } } // قمنا ببناء هذه الدالة لحساب و عرض العمر سواء كان المستخدم يريد حساب عمر شخص على قيد الحياة أو شخص ميت public void displayAge() { // سنستخدم هذا المتغير لتخزين نص النتيجة التي ستظهر String text = ""; // هنا قمنا بتخزين تاريخ الميلاد الذي إختاره المستخدم في المتغيرات التالية y1 = birthDatePicker.getValue().getYear(); m1 = birthDatePicker.getValue().getMonthValue(); d1 = birthDatePicker.getValue().getDayOfMonth(); // إذا كان خيار تحديد تاريخ الوفات مفعل, سيتم تخزين التاريخ المحدد في المتغيرات التالية if (deathDateCheckBox.isSelected()) { y2 = deathDatePicker.getValue().getYear(); m2 = deathDatePicker.getValue().getMonthValue(); d2 = deathDatePicker.getValue().getDayOfMonth(); } // إذا كان خيار تحديد تاريخ الوفات غير مفعل, سيتم تخزين التاريخ الحالي في المتغيرات التالية else { y2 = LocalDate.now().getYear(); m2 = LocalDate.now().getMonthValue(); d2 = LocalDate.now().getDayOfMonth(); } // endDate و تاريخ الوفاة في الكائن startDate هنا قمنا بوضع تاريخ الميلاد الذي حدده المستخدم في الكائن startDate = LocalDate.of(y1, m1, d1); endDate = LocalDate.of(y2, m2, d2); // هنا قمنا بتخزين الفارق بين التواريخ في المتغيرات التالية yearsCounter = Period.between(startDate, endDate).getYears(); monthsCounter = Period.between(startDate, endDate).getMonths(); daysCounter = Period.between(startDate, endDate).getDays(); // هنا قلنا إذا كان التاريخين متواسيين سيتم إظهار التنبيه التالي if (yearsCounter == 0 && monthsCounter == 0 && daysCounter == 0) { resultLabel.setTextFill(Color.RED); resultLabel.setText("Cannot compare same date!"); } // هنا قلنا أنه إذا كانت التواريخ المحددة هي منطقياً صحيحة سيتم إظهار العمر else if (!Period.between(startDate, endDate).isNegative()) { if (yearsCounter == 1) { text += yearsCounter + " year "; } else if (yearsCounter > 1) { text += yearsCounter + " years "; } if (monthsCounter == 1) { if (yearsCounter > 0 && daysCounter > 0) { text += ", " + monthsCounter + " month "; } else if (yearsCounter > 0 && daysCounter == 0) { text += "and " + monthsCounter + " month "; } else { text += monthsCounter + " month "; } } if (monthsCounter > 1) { if (yearsCounter > 0 && daysCounter > 0) { text += ", " + monthsCounter + " months "; } else if (yearsCounter > 0 && daysCounter == 0) { text += "and " + monthsCounter + " months "; } else { text += monthsCounter + " months "; } } if (daysCounter == 1) { if (yearsCounter == 0 && monthsCounter == 0) { text += daysCounter + " day"; } else { text += "and " + daysCounter + " day"; } } if (daysCounter > 1) { if (yearsCounter == 0 && monthsCounter == 0) { text += daysCounter + " days"; } else { text += "and " + daysCounter + " days"; } } resultLabel.setTextFill(Color.BLACK); resultLabel.setText(text); } // هنا قلنا أنه إذا كانت التواريخ المحددة هي منطقياً غير مقبولة سيتم إظهار التنبيه التالي else { resultLabel.setTextFill(Color.RED); resultLabel.setText("Logic order of Dates is wrong!"); } } public void start(Stage stage) { // هنا قمنا بإنشاء جميع الأشياء التي سنضيفها في النافذة birthDateLabel = new Label("Birth Date"); deathDateCheckBox = new CheckBox("Death Date"); resultLabel = new Label(); calculateAgeButton = new Button("Calculate Age"); birthDatePicker = new DatePicker(LocalDate.of(2000, 1, 1)); deathDatePicker = new DatePicker(LocalDate.now()); // هنا قما بتعديل حجم و سمك خط جميع الأشياء التي سنضيفها في النافذة birthDateLabel.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); deathDateCheckBox.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); resultLabel.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); calculateAgeButton.setStyle("-fx-font-size: 17px; -fx-font-weight: bold;"); birthDatePicker.setStyle("-fx-font-size: 14px;"); deathDatePicker.setStyle("-fx-font-size: 14px;"); // هنا قما بتعديل حجم جميع الأشياء التي سنضيفها في النافذة // لم نحدد حجمه لأنه لا داعي لذلك و سنتركه يظهر في وسط النافذة مهما كان حجمه resultLabel ملاحظة: الكائن birthDateLabel.setPrefSize(200, 30); deathDateCheckBox.setPrefSize(200, 30); birthDatePicker.setPrefSize(200, 30); deathDatePicker.setPrefSize(200, 30); calculateAgeButton.setPrefSize(200, 50); // في النافذة لأننا ننوي ترتيب العناصر فيه بشكل عامودي Root Node و الذي ننوي جعله الـ VBox هنا قمنا بإنشاء كائن من الكلاس VBox root = new VBox(); // حتى يظهر محتواه في وسط النافذة مع إضافة مسافة فارغة بمقدار 20 بيكسل حوله من كل الجهات root هنا قمنا بتعديل خصائص الكائن root.setAlignment(Pos.CENTER); root.setPadding(new Insets(20)); // هنا قمنا بإضافة جميع الأشياء التي قمنا بإنشائها في النافذة root.getChildren().add(birthDateLabel); root.getChildren().add(birthDatePicker); root.getChildren().add(deathDateCheckBox); root.getChildren().add(deathDatePicker); root.getChildren().add(calculateAgeButton); root.getChildren().add(resultLabel); // هنا قمنا بإضافة مسافة فارغة حول كل شيء أضفناه في النافذة VBox.setMargin(birthDatePicker, new Insets(10, 0, 35, 0)); VBox.setMargin(deathDateCheckBox, new Insets(10, 0, 0, 0)); VBox.setMargin(deathDatePicker, new Insets(10, 0, 35, 0)); VBox.setMargin(calculateAgeButton, new Insets(10, 0, 25, 0)); VBox.setMargin(resultLabel, new Insets(10, 0, 0, 0)); // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن Scene scene = new Scene(root, 430, 420); // هنا وضعنا عنوان للنافذة stage.setTitle("Age Calculator"); // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ stage.setScene(scene); // هنا قمنا بإظهار النافذة stage.show(); // calculateAgeButton هنا قمنا بتحديد ماذا سيحدث عند النقر على الكائن // حتى تحسب و تعرض العمر displayAge() فعلياً سيتم إستدعاء الدالة calculateAgeButton.setOnAction((Action) -> { displayAge(); }); // deathDateCheckBox هنا قمنا بتحديد ماذا سيحدث عند النقر على الكائن // deathDatePicker لتفعيل أو إلغاء تفعيل الكائن toggleDeathDatePicker() فعلياً سيتم إستدعاء الدالة deathDateCheckBox.selectedProperty().addListener( (ObservableValue<? extends Boolean> ov, Boolean old_val, Boolean new_val) -> { toggleDeathDatePicker(); } ); // غير مفعل بشكل إفتراضي عند تشغيل البرنامج toggleDeathDatePicker لجعل الكائن toggleDeathDatePicker() هنا قمنا باستدعاء الدالة toggleDeathDatePicker(); } // هنا قمنا بتشغيل التطبيق public static void main(String[] args) { launch(args); } }