5. واجهات رسومية
نقترح هنا عرض كيفية إنشاء واجهات رسومية باستخدام JAVA. سنرى أولاً ما هي الفئات الأساسية التي تسمح لنا بإنشاء واجهة رسومية. لن نستخدم في البداية أي أداة للتوليد التلقائي. ثم سنستخدم JBuilder، وهي أداة تطوير من Borland/Inprise تسهل تطوير تطبيقات Java ولا سيما إنشاء الواجهات الرسومية.
5.1. أساسيات الواجهات الرسومية
5.1.1. نافذة بسيطة
لننظر إلى الكود التالي:
// الفئات المستوردة
import javax.swing.*;
import java.awt.*;
// فئة النموذج
public class form1 extends JFrame {
// المنشئ
public form1() {
// عنوان النافذة
this.setTitle("Mon premier formulaire");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
}//المنشئ
// وظيفة الاختبار
public static void main(String[] args) {
// يتم عرض النموذج
new form1().setVisible(true);
}
}//الفئة
يؤدي تنفيذ الكود السابق إلى عرض النافذة التالية:

عادةً ما تُشتق واجهة المستخدم الرسومية من الفئة الأساسية JFrame:
public class form1 extends JFrame {
تحدد الفئة الأساسية JFrame نافذة أساسية مزودة بأزرار للإغلاق والتكبير/التصغير وحجم قابل للتعديل، وما إلى ذلك... وتدير الأحداث على هذه الكائنات الرسومية. هنا نقوم بتخصيص الفئة الأساسية عن طريق تحديد عنوان لها وعرضها (300 بكسل) وارتفاعها (100 بكسل). يتم ذلك في منشئها:
// المنشئ
public form1() {
// عنوان النافذة
this.setTitle("Mon premier formulaire");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
}//المنشئ
يتم تحديد عنوان النافذة بواسطة الطريقة setTitle وأبعادها بواسطة الطريقة setSize. تقبل هذه الطريقة كمعلمة كائن Dimension(largeur,hauteur) حيث largeur و hauteur هما عرض وارتفاع النافذة بالبكسل.
تقوم الطريقة main بتشغيل التطبيق الرسومي بالطريقة التالية:
new form1().setVisible(true);
يتم بعد ذلك إنشاء نموذج من النوع form1 (new form1()) وعرضه (setVisible(true))، ثم يبدأ التطبيق في الاستماع إلى الأحداث التي تحدث على النموذج (النقرات، تحركات الماوس، ...) ويقوم بتنفيذ الأحداث التي يديرها النموذج. هنا، لا يدير نموذجنا أحداثًا أخرى غير تلك التي تديرها الفئة الأساسية JFrame (النقرات على أزرار الإغلاق، التكبير/التصغير، تغيير حجم النافذة، تحريك النافذة، ...).
عند اختبار هذا البرنامج عن طريق تشغيله من نافذة DOS باستخدام:
لتنفيذ الملف form1.class، نلاحظ أنه عند إغلاق النافذة التي تم عرضها، لا "نستعيد السيطرة" في نافذة DOS، كما لو أن البرنامج لم ينته. وهذا هو الحال بالفعل. يتم تنفيذ البرنامج على النحو التالي:
- في البداية، يتم تشغيل مؤشر ترابط أول لتنفيذ الأسلوب main
- وعندما تقوم هذه الطريقة بإنشاء النموذج وعرضه، يتم إنشاء مؤشر ترابط ثانٍ لإدارة الأحداث المرتبطة بالنموذج على وجه التحديد
- بعد هذا الإنشاء، وفي مثالنا هذا، ينتهي مؤشر الترابط الخاص بالطريقة main ويبقى فقط مؤشر الترابط الخاص بواجهة المستخدم الرسومية.
- عند إغلاق النافذة، تختفي هذه الأخيرة ولكنها لا توقف الخيط الذي كانت تعمل فيه
- ونحن مضطرون في الوقت الحالي لإيقاف هذا الخيط بالضغط على Ctrl-C في نافذة DOS التي تم من خلالها تشغيل البرنامج.
دعونا نتحقق من وجود خيطين منفصلين، أحدهما يتم فيه تنفيذ الطريقة main، والآخر يتم فيه تنفيذ النافذة الرسومية:
// الفئات المستوردة
import javax.swing.*;
import java.awt.*;
import java.io.*;
// فئة النموذج
public class form1 extends JFrame {
// المنشئ
public form1() {
// عنوان النافذة
this.setTitle("Mon premier formulaire");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
}//المنشئ
// وظيفة الاختبار
public static void main(String[] args) {
// المتابعة
System.out.println("Début du thread main");
// يتم عرض النموذج
new form1().setVisible(true);
// المتابعة
System.out.println("Fin du thread main");
}//يدوي
}//فئة
يؤدي التنفيذ إلى النتائج التالية:

يمكن ملاحظة أن مؤشر الترابط main قد انتهى بينما لا تزال النافذة معروضة. لا يؤدي إغلاق النافذة إلى إنهاء مؤشر الترابط الذي كان يعمل فيه. لإيقاف مؤشر الترابط هذا، نضغط مرة أخرى على Ctrl-C في نافذة DOS.
لإنهاء هذا المثال، نلاحظ الحزم المستوردة:
- javax.swing للفئة JFrame
- java.awt للفئة Dimension
5.1.2. إدارة حدث
في المثال السابق، يتعين علينا إدارة إغلاق النافذة بأنفسنا حتى نتمكن من إيقاف التطبيق عند حدوث ذلك، وهو ما لم يتم حتى الآن. ولهذا الغرض، يتعين علينا إنشاء كائن "يستمع" إلى الأحداث التي تحدث على النافذة ويكتشف حدث "إغلاق النافذة". يُطلق على هذا الكائن اسم "المستمع" أو مدير الأحداث. توجد أنواع مختلفة من المديرين للأحداث المختلفة التي قد تحدث على مكونات واجهة المستخدم الرسومية. بالنسبة للمكون JFrame، يُسمى المستمع WindowListener وهو واجهة تحدد الطرق التالية (انظر وثائق Java)
ملخص الطرق | ||
void | windowActivated(WindowEvent e) تصبح النافذة هي النافذة النشطة | |
void | windowClosed(WindowEvent e) تم إغلاق النافذة | |
void | windowClosing(WindowEvent e) طلب المستخدم أو البرنامج إغلاق النافذة | |
void | windowDeactivated(WindowEvent e) لم تعد النافذة هي النافذة النشطة | |
void | windowDeiconified(WindowEvent e) تنتقل النافذة من الحالة المصغرة إلى الحالة العادية | |
void | windowIconified(WindowEvent e) تنتقل النافذة من الحالة العادية إلى الحالة المصغرة | |
void | windowOpened(WindowEvent e) تصبح النافذة مرئية لأول مرة | |
وبالتالي، هناك سبعة أحداث يمكن إدارتها. تتلقى جميع برامج الإدارة كمعلمة كائن من النوع WindowEvent الذي نتجاهله في الوقت الحالي. الحدث الذي يهمنا هنا هو إغلاق النافذة، وهو حدث يجب معالجته بواسطة الطريقة windowClosing. لإدارة هذا الحدث، يمكننا إنشاء كائن Windowlistener باستخدام فئة مجهولة بالطريقة التالية:
// إنشاء مدير أحداث
WindowListener win=new WindowListener(){
public void windowActivated(WindowEvent e){}
public void windowClosed(WindowEvent e){}
public void windowClosing(WindowEvent e){System.exit(0);}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e){}
};//تعريف win
يجب أن يحدد مدير الأحداث لدينا الذي ينفذ واجهة WindowListener الطرق السبع لهذه الواجهة. وبما أننا لا نريد سوى إدارة إغلاق النافذة، فإننا نحدد الكود فقط للطريقة windowClosing. عندما تحدث الأحداث الأخرى، سيتم إخطارنا بها ولكننا لن نقوم بأي شيء. ماذا سنفعل عندما يتم إخطارنا بأن النافذة قيد الإغلاق (windowClosing)؟ سنقوم بإيقاف التطبيق:
public void windowClosing(WindowEvent e){System.exit(0);}
لدينا هنا كائن قادر على إدارة أحداث النافذة بشكل عام. كيف نربطه بنافذة معينة؟ تحتوي الفئة JFrame على طريقة addWindowListener(WindowListener win) التي تسمح بربط مدير أحداث "نافذة" بنافذة معينة. وهكذا هنا، في منشئ النافذة سنكتب:
// إنشاء مدير أحداث
WindowListener win=new WindowListener(){
public void windowActivated(WindowEvent e){}
public void windowClosed(WindowEvent e){}
public void windowClosing(WindowEvent e){System.exit(0);}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e){}
};//تعريف win
// سيقوم مدير الأحداث هذا بإدارة أحداث النافذة الحالية
this.addWindowListener(win);
البرنامج الكامل هو كما يلي:
// الفئات المستوردة
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
// فئة النموذج
public class form2 extends JFrame {
// المنشئ
public form2() {
// عنوان النافذة
this.setTitle("Mon premier formulaire");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
// إنشاء مدير أحداث
WindowListener win=new WindowListener(){
public void windowActivated(WindowEvent e){}
public void windowClosed(WindowEvent e){}
public void windowClosing(WindowEvent e){System.exit(0);}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e){}
};//تعريف win
// سيقوم مدير الأحداث هذا بإدارة أحداث النافذة الحالية
this.addWindowListener(win);
}//المنشئ
// وظيفة الاختبار
public static void main(String[] args) {
// يتم عرض النموذج
new form2().setVisible(true);
}//main
}//فئة
تحتوي الحزمة java.awt.event على واجهة WindowListener. عند تشغيل هذا البرنامج وإغلاق النافذة التي ظهرت، نلاحظ في نافذة DOS التي تم تشغيل البرنامج منها، انتهاء تشغيل البرنامج، وهو ما لم يكن موجودًا من قبل.
في برنامجنا، يعد إنشاء الكائن المسؤول عن إدارة أحداث النافذة أمرًا ثقيلًا بعض الشيء، حيث نضطر إلى تعريف طرق حتى للأحداث التي لا نريد إدارتها. في هذه الحالة، بدلاً من استخدام الواجهة WindowListener، يمكننا استخدام الفئة WindowAdapter. هذه الفئة تنفذ الواجهة WindowListener، مع سبع طرق فارغة. من خلال اشتقاق الفئة WindowAdapter وإعادة تعريف الطرق التي تهمنا فقط، نصل إلى نفس النتيجة التي نحصل عليها مع الواجهة WindowListener ولكن دون الحاجة إلى تعريف الطرق التي لا تهمنا. تسلسل
- تعريف معالج الأحداث
- ربط المعالج بالنافذة
يمكن أن تتم بالطريقة التالية في مثالنا:
// إنشاء مدير أحداث
WindowAdapter win=new WindowAdapter(){
public void windowClosing(WindowEvent e){System.exit(0);}
};//تعريف win
// سيتولى مدير الأحداث هذا إدارة أحداث النافذة الحالية
this.addWindowListener(win);
نستخدم هنا فئة مجهولة تستمد من الفئة WindowAdapter وتعيد تعريف طريقتها windowClosing. يصبح البرنامج عندئذٍ:
// الفئات المستوردة
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
// فئة النموذج
public class form2 extends JFrame {
// المنشئ
public form2() {
// عنوان النافذة
this.setTitle("Mon premier formulaire");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
// إنشاء مدير أحداث
WindowAdapter win=new WindowAdapter(){
public void windowClosing(WindowEvent e){System.exit(0);}
};//تعريف win
// سيقوم مدير الأحداث هذا بإدارة أحداث النافذة الحالية
this.addWindowListener(win); }//المنشئ
// وظيفة الاختبار
public static void main(String[] args) {
// يتم عرض النموذج
new form2().setVisible(true);
}//main
}//فئة
يعطي نفس نتائج البرنامج السابق ولكنه أسهل في الكتابة.
5.1.3. نموذج مع زر
لنضف الآن زرًا إلى نافذتنا:
// الفئات المستوردة
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
// فئة النموذج
public class form3 extends JFrame {
// زر
JButton btnTest=null;
Container conteneur=null;
// المنشئ
public form3() {
// عنوان النافذة
this.setTitle("Formulaire avec bouton");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
// إنشاء مدير أحداث
WindowAdapter win=new WindowAdapter(){
public void windowClosing(WindowEvent e){System.exit(0);}
};//تعريف win
// سيقوم مدير الأحداث هذا بإدارة أحداث النافذة الحالية
this.addWindowListener(win);
// نسترد حاوية النافذة
conteneur=this.getContentPane();
// نختار مدير تنسيق المكونات في هذا الحاوية
conteneur.setLayout(new FlowLayout());
// يتم إنشاء زر
btnTest=new JButton();
// يتم تحديد تسميته
btnTest.setText("Test");
// يتم إضافة الزر إلى الحاوية
conteneur.add(btnTest);
}//منشئ
// وظيفة الاختبار
public static void main(String[] args) {
// عرض النموذج
new form3().setVisible(true);
}//main
}//الفئة
تحتوي نافذة JFrame على حاوية يمكن وضع المكونات الرسومية فيها (زر، مربعات اختيار، قوائم منسدلة، ...). هذه الحاوية متاحة عبر طريقة getContentPane من فئة JFrame:
Container conteneur=null;
..........
// استرداد حاوية النافذة
conteneur=this.getContentPane();
يتم وضع كل مكون في الحاوية بواسطة الطريقة add من الفئة Container. وبالتالي، لوضع المكون C في الكائن conteneur السابق، سنكتب:
أين يتم وضع هذا المكون في الحاوية؟ توجد العديد من أدوات إدارة تخطيط المكونات التي تحمل الاسم XXXLayout، حيث XXX يساوي على سبيل المثال Border، Flow، ... لكل مدير تخطيط خصائصه الخاصة. على سبيل المثال، يقوم المدير FlowLayout بترتيب المكونات في صفوف بدءًا من أعلى النموذج. وعندما يتم ملء صف ما، يتم وضع المكونات في الصف التالي. لربط مدير التنسيق بنافذة JFrame، يتم استخدام الطريقة setLayout من الفئة JFrame بالشكل التالي:
وهكذا، في مثالنا، لربط معالج من النوع FlowLayout بالنافذة، كتبنا:
// اختيار مدير تنسيق المكونات في هذا الحاوية
conteneur.setLayout(new FlowLayout());
يمكن عدم استخدام مدير التخطيط وكتابة:
في هذه الحالة، يجب تحديد الإحداثيات الدقيقة للمكون في الحاوية بالصيغة (x,y,العرض,الارتفاع) حيث (x,y) هما إحداثيات الزاوية العلوية اليسرى للمكون في الحاوية. هذه هي الطريقة التي سنستخدمها في الغالب لاحقًا.
نحن نعرف الآن كيف يتم وضع المكونات في الحاوية (add) وأين (setLayout). يبقى لنا معرفة
المكونات التي يمكن وضعها في الحاوية. هنا نضع زرًا تم تصميمه بواسطة الفئة javax.swing.JButton:
JButton btnTest=null;
..........
// يتم إنشاء زر
btnTest=new JButton();
// يتم تحديد تسميته
btnTest.setText("Test");
// يتم إضافة الزر إلى الحاوية
conteneur.add(btnTest);
عند اختبار هذا البرنامج، تظهر النافذة التالية:

إذا قمنا بتغيير حجم النموذج أعلاه، يتم استدعاء مدير تخطيط الحاوية تلقائيًا لإعادة وضع المكونات:

هذا هو الهدف الرئيسي لمديري التخطيط: الحفاظ على تخطيط متسق للمكونات مع تغير حجم الحاوية. لنستخدم مدير التخطيط null لنرى الفرق. يتم الآن وضع الزر في الحاوية بواسطة التعليمات التالية:
// يتم اختيار مدير تنسيق المكونات في هذا الحاوية
conteneur.setLayout(null);
// يتم إنشاء زر
btnTest=new JButton();
// يتم تحديد نصه
btnTest.setText("Test");
// يتم تحديد موقعه وأبعاده
btnTest.setBounds(10,20,100,20);
// يتم إضافة الزر إلى الحاوية
conteneur.add(btnTest);
هنا، نضع الزر بشكل صريح عند النقطة (10,20) من النموذج ونحدد أبعاده بـ 100 بكسل للعرض و20 بكسل للارتفاع. تصبح النافذة الجديدة كما يلي:

إذا قمنا بتغيير حجم النافذة، يبقى الزر في نفس المكان.

إذا نقرنا على الزر Test، فلن يحدث شيء. في الواقع، لم نربط بعد معالج أحداث بالزر. لمعرفة نوع معالجات الأحداث المتاحة لمكون معين، يمكن البحث في تعريف فئته عن الطرق addXXXListener التي تسمح بربط معالج أحداث بالمكون. تشتق الفئة javax.swing.JButton من الفئة javax.swing.AbstractButton التي تحتوي على الطرق التالية:
ملخص الطرق | ||
void | addActionListener(ActionListener l) | |
void | addChangeListener(ChangeListener l) | |
void | addItemListener(ItemListener l) | |
هنا، يجب قراءة الوثائق لمعرفة أي معالج أحداث يدير النقر على الزر. إنه واجهة ActionListener. هذه الواجهة لا تحدد سوى طريقة واحدة:
ملخص الطريقة | ||
void | actionPerformed(ActionEvent e) | |
تستقبل الطريقة معلمة ActionEvent التي سنتجاهلها في الوقت الحالي. لإدارة النقر على الزر btntest في برنامجنا، نربط به أولاً معالج أحداث:
btnTest.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt){
btnTest_clic(evt);
}
}//فئة مجهولة
);//مدير الأحداث
هنا، تشير الطريقة actionPerformed إلى الطريقة btnTest_clic التي نحددها على النحو التالي:
public void btnTest_clic(ActionEvent evt){
// تتبع وحدة التحكم
System.out.println("clic sur bouton");
}//btnTest_click
في كل مرة ينقر فيها المستخدم على الزر Test، يتم كتابة رسالة على وحدة التحكم. وهذا ما يوضحه التنفيذ التالي:

البرنامج الكامل هو كما يلي:
// الفئات المستوردة
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
// فئة النموذج
public class form4 extends JFrame {
// زر
JButton btnTest=null;
Container conteneur=null;
// المنشئ
public form4() {
// عنوان النافذة
this.setTitle("Formulaire avec bouton");
// أبعاد النافذة
this.setSize(new Dimension(300,100));
// إنشاء مدير أحداث
WindowAdapter win=new WindowAdapter(){
public void windowClosing(WindowEvent e){System.exit(0);}
};//تعريف win
// سيقوم مدير الأحداث هذا بإدارة أحداث النافذة الحالية
this.addWindowListener(win);
// نسترد حاوية النافذة
conteneur=this.getContentPane();
// نختار مدير تنسيق المكونات في هذا الحاوية
conteneur.setLayout(null);
// يتم إنشاء زر
btnTest=new JButton();
// يتم تحديد تسميته
btnTest.setText("Test");
// يتم تحديد موقعه وأبعاده
btnTest.setBounds(10,20,100,20);
// نربطه بمدير أحداث
btnTest.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt){
btnTest_clic(evt);
}
}//فئة مجهولة
);//معالج الأحداث
// يتم إضافة الزر إلى الحاوية
conteneur.add(btnTest);
}//منشئ
public void btnTest_clic(ActionEvent evt){
// تتبع وحدة التحكم
System.out.println("clic sur bouton");
}//btnTest_click
// وظيفة الاختبار
public static void main(String[] args) {
// يتم عرض النموذج
new form4().setVisible(true);
}//الرئيسي
}//فئة
5.1.4. مديرو الأحداث
المكونات الرئيسية لـ Swing التي سنقدمها هي النوافذ (JFrame)، والأزرار (JButton)، ومربعات الاختيار (JCheckBox)، أزرار الاختيار (JButtonRadio)، والقوائم المنسدلة (JComboBox)، والقوائم (JList)، وأدوات التمرير (JScrollBar)، الملصقات (JLabel)، ومربعات الإدخال أحادية السطر (JTextField) أو متعددة الأسطر (JTextArea)، والقوائم (JMenuBar)، عناصر القائمة (JMenuItem).
تقدم الجداول التالية قائمة ببعض مديري الأحداث والأحداث المرتبطة بهم.
معالج | المكون (المكونات) | طريقة التسجيل | الحدث |
ActionListener | JButton ، JCheckbox، JButtonRadio، JMenuItem | public void addActionListener(ActionListener) | النقر على الزر، مربع الاختيار، زر الاختيار، عنصر القائمة |
JTextField | |||
ItemListener | JComboBox، JList | public void addItemListener(ItemListener) | تم تغيير العنصر المحدد |
InputMethodListener | JTextField، JTextArea | public void addMethodInputListener(InputMethodListener) | تغير نص منطقة الإدخال أو تغير موضع مؤشر الإدخال |
CaretListener | JTextField، JTextArea | public void addcaretListener(CaretListener) | تغير موضع مؤشر الإدخال |
AdjustmentListener | JScrollBar | public void addAdjustmentListener(AdjustmentListener) | تغيرت قيمة المتغير |
MouseMotionListener | public void addMouseMotionListener(MouseMotionListener) | تحركت الماوس | |
WindowListener | JFrame | public void addWindowlistener(WindowListener) | حدث النافذة |
MouseListener | public void addMouselistener(MouseListener) | أحداث الماوس (النقر، الدخول/الخروج من نطاق مكون، الضغط على الزر، التحرير) | |
FocusListener | public void addFocuslistener(FocusListener) | حدث التركيز (تم الحصول عليه، تم فقدانه) | |
KeyListener | public void addKeylistener(KeyListener) | حدث لوحة المفاتيح (المفتاح الذي تم الضغط عليه، الضغط عليه، تحريره) |
المكون | طريقة تسجيل معالجات الأحداث |
JButton | public void addActionListener(ActionListener) |
JCheckbox | public void addItemListener(ItemListener) |
JCheckboxMenuItem | public void addItemListener(ItemListener) |
JComboBox | public void addItemListener(ItemListener) public void addActionListener(ActionListener) |
Container | public void addContainerListener(ContainerListener) |
JComponent | public void addComponentListener(ComponentListener) public void addFocusListener(FocusListener) public void addKeyListener(KeyListener) public void addMouseListener(MouseListener) public void addMouseMotionListener(MouseMotionListener) |
JFrame | public void addWindowlistener(WindowListener) |
JList | public void addItemListener(ItemListener) |
JMenuItem | public void addActionListener(ActionListener) |
JPanel | كحاوية |
JScrollPane | كحاوية |
JScrollBar | public void addAdjustmentListener(AdjustmentListener) |
JTextComponent | public void addInputMethodListener(InputMethodListener) public void addCaretListener(CaretListener) |
JTextArea | مثل JTextComponent |
JTextField | مثل JTextComponent public void addActionListener(ActionListener) |
جميع المكونات، باستثناء تلك من النوع TextXXX، كونها مشتقة من الفئة JComponent، تمتلك أيضًا الطرق المرتبطة بهذه الفئة.
5.1.5. طرق معالجات الأحداث
يسرد الجدول التالي الطرق التي يجب أن تنفذها مختلف معالجات الأحداث.
الواجهة | الطرق |
ActionListener | public void actionPerformed(ActionEvent) |
AdjustmentListener | public void adjustmentValueChanged(AdjustmentEvent) |
ComponentListener | public void componentHidden(ComponentEvent) public void componentMoved(ComponentEvent) public void componentResized(ComponentEvent) public void componentShown(ComponentEvent) |
ContainerListener | public void componentAdded(ContainerEvent) public void componentRemoved(ContainerEvent) |
FocusListener | public void focusGained(FocusEvent) public void focusLost(FocusEvent) |
ItemListener | public void itemStateChanged(ItemEvent) |
KeyListener | public void keyPressed(KeyEvent) public void keyReleased(KeyEvent) public void keyTyped(KeyEvent) |
MouseListener | public void mouseClicked(MouseEvent) public void mouseEntered(MouseEvent) public void mouseExited(MouseEvent) public void mousePressed(MouseEvent) public void mouseReleased(MouseEvent) |
MouseMotionListener | public void mouseDragged(MouseEvent) public void mouseMoved(MouseEvent) |
TextListener | public void textValueChanged(TextEvent) |
InputmethodListener | public void InputMethodTextChanged(InputMethodEvent) public void caretPositionChanged(InputMethodEvent) |
CaretLisetner | public void caretUpdate(CaretEvent) |
WindowListener | public void windowActivated(WindowEvent) public void windowClosed(WindowEvent) public void windowClosing(WindowEvent) public void windowDeactivated(WindowEvent) public void windowDeiconified(WindowEvent) public void windowIconified(WindowEvent) public void windowOpened(WindowEvent) |
5.1.6. فئات المحولات
كما رأينا في واجهة WindowListener، توجد فئات تسمى XXXAdapter التي تنفذ واجهات XXXListener بأساليب فارغة. يمكن لمُعالج الأحداث المشتق من فئة XXXAdapter أن يُنفذ فقط جزءًا من طرق واجهة XXXListener، وهي الطرق التي يحتاجها التطبيق.
لنفترض أننا نريد إدارة نقرات الماوس على مكون Frame f1. يمكننا ربط معالج أحداث به عن طريق:
وكتابة:
public class gestionnaireSouris implements MouseListener{
// كتابة الطرق الخمس للواجهة MouseListener
// mouseClicked، ...، mouseReleased)
}// نهاية الفئة
نظرًا لأننا نرغب في إدارة نقرات الماوس فقط، فمن الأفضل كتابة:
public class gestionnaireSouris extends MouseAdapter{
// نكتب طريقة واحدة فقط، وهي التي تدير نقرات الماوس
public void mouseClicked(MouseEvent evt){
…
}
}// نهاية الفئة
يوضح الجدول التالي فئات المحولات لمختلف مديري الأحداث:
مدير الأحداث | المحول |
ComponentListener | ComponentAdapter |
ContainerListener | ContainerAdapter |
FocusListener | FocusAdapter |
KeyListener | KeyAdapter |
MouseListener | MouseAdapter |
MouseMotionListener | MouseMotionAdapter |
WindowListener | WindowAdapter |
5.1.7. خلاصة
لقد عرضنا للتو المفاهيم الأساسية لإنشاء واجهات رسومية في Java:
- إنشاء نافذة
- إنشاء المكونات
- ربط المكونات بالنافذة باستخدام مدير التخطيط
- ربط مديري الأحداث بالمكونات
الآن، بدلاً من إنشاء واجهات رسومية "يدويًا" كما فعلنا للتو، سنستخدم Jbuilder، أداة تطوير Java من Borland/Inprise في إصدارها 4 و supérieure.Nous، وسنستخدم مكونات مكتبة java.swing التي توصي بها حاليًا شركة Sun، مطورة لغة Java.
5.2. إنشاء واجهة رسومية باستخدام JBuilder
5.2.1. مشروعنا الأول في JBuilder
من أجل التعرف على Jbuilder، لنقم بإنشاء تطبيق بسيط للغاية: نافذة فارغة.
- قم بتشغيل Jbuilder واختر الخيار ملف/مشروع جديد. ستظهر عندئذ الصفحة الأولى من المساعد:

- املأ الحقول التالية:
اسم المشروع | بداية سيؤدي ذلك إلى إنشاء ملف مشروع début.jpr في المجلد المحدد في الحقل "اسم مجلد المشروع" |
مسار الجذر | حدد المجلد الذي سيتم إنشاء مجلد المشروع الذي ستقوم بإنشائه تحته. |
اسم مجلد المشروع | حدد اسم المجلد الذي سيتم وضع جميع ملفات المشروع فيه. سيتم إنشاء هذا المجلد ضمن الدليل المحدد في الحقل "مسار الجذر" |
قد توضع ملفات المصدر (.java، .html، ...) وملفات الوجهة (.class، ...) وملفات النسخ الاحتياطي في مجلدات مختلفة. إذا تركت الحقول الخاصة بها فارغة، فسيتم وضعها في نفس مجلد المشروع.
قم بـ [suivant]
- تظهر شاشة لتأكيد اختيارات الخطوة السابقة

انقر على [suivant]
- تظهر شاشة جديدة تطلب منك تحديد خصائص مشروعك:

انقر على [terminer]
- تأكد من تحديد الخيار Voir/projet. يجب أن ترى هيكل مشروعك في النافذة اليسرى.

- الآن لنقم بإنشاء واجهة رسومية في هذا المشروع. اختر الخيار Fichier/Nouveau/Application:

في الحقل classe، نضع اسم الفئة التي سيتم إنشاؤها. هنا استخدمنا نفس اسم المشروع.
انقر على [suivant]
- تظهر الشاشة التالية:

الفئة | interfaceDébut هذا هو اسم الفئة المطابقة للنافذة التي سيتم إنشاؤها |
العنوان | هذا هو النص الذي سيظهر في شريط عنوان النافذة |
لاحظ أننا طلبنا أعلاه أن يتم توسيط النافذة على الشاشة عند بدء تشغيل التطبيق.
اكتب [Terminer]
- هنا تظهر فائدة Jbuilder.
- فهو يقوم بإنشاء ملفات المصدر .java للفئتين اللتين حددنا اسميهما: فئة التطبيق وفئة واجهة المستخدم الرسومية. نرى هذين الملفين يظهران في بنية المشروع في النافذة اليسرى

- للوصول إلى الكود الذي تم إنشاؤه للفئتين، ما عليك سوى النقر مرتين على ملف .java المقابل. سنعود لاحقًا إلى الكود الذي تم إنشاؤه.
- تأكد من تحديد الخيار Voir/Structure. يتيح هذا الخيار عرض بنية الفئة المحددة حاليًا (النقر المزدوج على ملف .java). فيما يلي مثال على بنية الفئة début:

نتعرف هنا على:
-
المكتبات المستوردة (imports)
-
أن هناك منشئ début()
-
أن هناك طريقة ثابتة main()
-
أن هناك سمة packFrame
ما الفائدة من الوصول إلى بنية الفئة؟
-
يمكنك الحصول على نظرة عامة عليها. وهذا مفيد إذا كانت فصلك معقدة.
-
يمكنك الوصول إلى كود إحدى الطرق بالنقر عليها في نافذة بنية الفئة. ومرة أخرى، يكون هذا مفيدًا إذا كانت فئتك تحتوي على مئات الأسطر. فأنت لست مضطرًا إلى مراجعة جميع الأسطر للعثور على الكود الذي تبحث عنه.
الكود الذي تم إنشاؤه بواسطة Jbuilder جاهز للاستخدام بالفعل. قم بتنفيذ "تشغيل/تشغيل المشروع" أو F9 وستحصل على النافذة المطلوبة:

بالإضافة إلى ذلك، يتم إغلاقها بشكل صحيح عند النقر على زر إغلاق النافذة. لقد أنشأنا للتو واجهة المستخدم الرسومية الأولى لدينا. يمكننا حفظ مشروعنا من خلال الخيار ملف/إغلاق المشاريع:

بالضغط على Tous، سيتم تحديد جميع المشاريع الموجودة في النافذة أعلاه. OK سيغلقها. إذا كنا مهتمين بالذهاب، باستخدام مستكشف Windows، إلى مجلد مشروعنا (الذي تم تحديده للمساعد في بداية تكوين المشروع)، فسنجد فيه الملفات التالية:

في مثالنا، طلبنا أن تكون جميع الملفات (المصدر .java، الوجهة .class، النسخة الاحتياطية .jpr~) في نفس مجلد مشروع .jpr.
5.2.2. الملفات التي أنشأها JBuilder لواجهة المستخدم الرسومية
دعونا الآن نلقي نظرة على الملفات المصدرية .java التي أنشأها Jbuilder. من المهم أن نعرف كيف نقرأ ما تم إنشاؤه لأننا غالبًا ما نضطر إلى إضافة كود إلى ما تم إنشاؤه. لنبدأ باستعادة مشروعنا: ملف/فتح مشروع ونختار المشروع début.jpr. نجد المشروع الذي تم إنشاؤه سابقًا.
5.2.2.1. الفئة الرئيسية
دعونا نفحص الفئة début.java بالنقر المزدوج على اسمها في النافذة التي تعرض قائمة ملفات المشروع. لدينا الكود التالي:
import javax.swing.UIManager;
import java.awt.*;
public class début {
boolean packFrame = false;
/**بناء التطبيق*/
public début() {
interfaceDébut frame = new interfaceDébut();
//التحقق من صحة الإطارات ذات الأحجام المحددة مسبقًا
//ضغط الإطارات التي تحتوي على معلومات الحجم المفضلة - على سبيل المثال من تخطيطها
if (packFrame) {
frame.pack();
}
else {
frame.validate();
}
//توسيط النافذة
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
}
/**الطريقة الرئيسية*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {
e.printStackTrace();
}
new début();
}
}
دعونا نعلق على الكود الذي تم إنشاؤه:
- تحدد الدالة main مظهر النافذة (setLookAndFeel) وتُنشئ مثيلًا للفئة début.
- ثم يتم تنفيذ المنشئ début(). وهو ينشئ مثيل frame لفئة النافذة (new interfaceDébut()). ويتم إنشاء هذا المثيل ولكن لا يتم عرضه.
- ثم يتم تحديد أبعاد النافذة وفقًا للمعلومات المتاحة لكل مكون من مكونات النافذة (frame.validate). ثم تبدأ النافذة وجودها المستقل من خلال الظهور والاستجابة لطلبات المستخدم.
- يتم توسيط النافذة على الشاشة لأننا طلبنا ذلك عند تكوين النافذة باستخدام المساعد.
لنرى ما الذي سنحصل عليه إذا قمنا بتقليص الكود début.java إلى الحد الأدنى كما فعلنا في بداية الفصل. لنقم بإنشاء فئة جديدة. اختر الخيار ملف/فئة جديدة:

قم بتسمية الفئة الجديدة début2 وقم بإنشاء [Terminer]. سيظهر ملف جديد في المشروع:

يتم اختصار الملف début2.java إلى أبسط صيغة له:
لنكمل الفئة بالطريقة التالية:
public class début2 {
// الوظيفة الرئيسية
public static void main(String args[]){
// إنشاء النافذة
new interfaceDébut().show(); // أو new interfaceDébut.setVisible(true);
}//main
}//فئة بداية2
تقوم الدالة main بإنشاء مثيل للنافذة interfaceDébut وعرضه (show). قبل تنفيذ مشروعنا، يجب أن نشير إلى أن الفئة التي تحتوي على الدالة main المراد تنفيذها هي الآن الفئة début2. انقر بزر الماوس الأيمن على المشروع début.jpr واختر الخيار propriétés ثم علامة التبويب Exécution:
21

هنا، يشار إلى أن الفئة الرئيسية هي début (1). اضغط على الزر (2) لاختيار فئة رئيسية أخرى:

اختر début2 وقم بإنشاء [OK].

قم بـ [OK] لتأكيد هذا الاختيار ثم اطلب تنفيذ المشروع عن طريق تنفيذ/تنفيذ المشروع أو F9. ستحصل على نفس النافذة التي تظهر مع الفئة début، إلا أنها ليست مركزة لأنها لم تُطلب هنا. بعد ذلك، لن نعرض الفئة الرئيسية التي تم إنشاؤها بواسطة Jbuilder لأنها تقوم دائمًا بنفس الشيء: إنشاء نافذة. سنركز من الآن فصاعدًا على فئة هذه النافذة.
5.2.2.2. فئة النافذة
لنلقِ نظرة الآن على الكود الذي تم إنشاؤه للفئة interfaceDébut:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class interfaceDébut extends JFrame {
JPanel contentPane;
BorderLayout borderLayout1 = new BorderLayout();
/**إنشاء الإطار*/
public interfaceDébut() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(400, 300));
this.setTitle("Ma première interface graphique avec Jbuilder");
}
/**تم استبداله، لذا يمكننا الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
}
5.2.2.2.1. المكتبات المستوردة
وهي المكتبات java.awt و java.awt.event و javax.swing. كانت المكتبتان الأوليتان هما الوحيدتان المتاحتان لإنشاء واجهات رسومية مع الإصدارات الأولى من Java. مكتبة javax.swing هي الأحدث. وهي ضرورية هنا للنافذة من النوع JFrame المستخدمة هنا.
5.2.2.2.2. السمات
JPanel هو نوع حاوية يمكن وضع المكونات فيها. BorderLayout هو أحد أنواع مديري التنسيق المتاحة لوضع المكونات في الحاوية. في جميع أمثلةنا، لن نستخدم أي مدير تنسيق وسنضع المكونات بأنفسنا في مكان محدد داخل الحاوية. وللقيام بذلك، سنستخدم مدير التنسيق null.
5.2.2.2.3. منشئ النافذة
/**بناء الإطار*/
public interfaceDébut() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(borderLayout1);
this.setSize(new Dimension(400, 300));
this.setTitle("Ma première interface graphique avec Jbuilder");
}
- يبدأ المُنشئ بالقول إنه سيتولى إدارة الأحداث في النافذة (enableEvents)، ثم يُطلق الطريقة jbInit.
- يتم الحصول على الحاوية (JPanel) للنافذة (JFrame) (getContentPane)
- يتم تعيين مدير التنسيق (setLayout)
- يتم تحديد حجم النافذة (setSize)
- يتم تعيين عنوان النافذة (setTitle)
5.2.2.2.4. تم تعيين مدير الأحداث
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
وقد أشار المطور إلى أن الفئة ستعالج أحداث النافذة. وتقوم الطريقة processWindowEvent بهذه المهمة. تبدأ هذه الطريقة بإرسال الحدث WindowEvent الذي تم استلامه إلى الفئة الأم (JFrame)، ثم إذا كان هذا الحدث هو WINDOW_CLOSING الناتج عن النقر على زر إغلاق النافذة، يتم إيقاف التطبيق.
5.2.2.2.5. خلاصة
يختلف كود فئة النافذة عن الكود المقدم في المثال في بداية الفصل. لو استخدمنا أداة أخرى لتوليد Java غير Jbuilder، لكان لدينا بلا شك كود مختلف تمامًا. في الممارسة العملية، سنقبل الكود الذي أنتجه Jbuilder لإنشاء النافذة حتى نركز فقط على كتابة معالجات أحداث واجهة المستخدم الرسومية.
5.2.3. رسم واجهة مستخدم رسومية
5.2.3.1. مثال
في المثال السابق، لم نضع أي مكونات في النافذة. لنقم الآن بإنشاء نافذة تحتوي على زر وعنصر نصي وحقل إدخال:

الحقول هي كما يلي:
رقم | الاسم | النوع | الدور |
1 | lblSaisie | JLabel | صياغة |
2 | txtSaisie | JTextField | منطقة إدخال |
3 | cmdAfficher | JButton | لعرض محتوى منطقة الإدخال في مربع حوار txtSaisie |
بالطريقة نفسها المتبعة في المشروع السابق، قم بإنشاء المشروع interface2.jpr دون إضافة أي مكونات إلى النافذة في الوقت الحالي.

في النافذة أعلاه، حدد فئة interface2.java من النافذة. على يمين هذه النافذة، يوجد ملف بأوراق:

تتيح علامة التبويب Source الوصول إلى كود المصدر للفئة interface2.java. تسمح علامة التبويب Conception بإنشاء النافذة بصريًا. حدد علامة التبويب هذه. أمامك الآن حاوية النافذة، التي ستستقبل المكونات التي ستضعها فيها. وهي فارغة في الوقت الحالي. تظهر في النافذة اليسرى بنية الفئة:

this | تمثل النافذة |
contentPane | الحاوية التي سيتم وضع المكونات فيها، بالإضافة إلى طريقة تنسيق هذه المكونات داخل الحاوية (BorderLayout بشكل افتراضي) |
borderLayout1 | مثيل لمدير التنسيق |
حدد الكائن this. ستظهر نافذة خصائصه على اليمين:

تجدر الإشارة إلى بعض هذه الخصائص:
background | لتحديد لون خلفية النافذة |
foreground | لتحديد لون الرسومات على النافذة |
JMenuBar | لربط قائمة بالنافذة |
title | لتسمية النافذة |
resizable | لتحديد نوع النافذة |
font | لتحديد خط الحروف في النافذة |
مع استمرار تحديد الكائن this، يمكن تغيير حجم الحاوية المعروضة على الشاشة عن طريق سحب نقاط الربط الموجودة حول الحاوية:

نحن الآن جاهزون لوضع المكونات في الحاوية أعلاه. قبل ذلك، سنقوم بتغيير مدير التنسيق. حدد الكائن contentPane في نافذة الهيكل:

ثم في نافذة خصائص هذا الكائن، حدد الخاصية layout واختر من بين القيم الممكنة، القيمة null:

سيسمح لنا عدم وجود مدير التنسيق بوضع المكونات بحرية في الحاوية. حان الوقت الآن لاختيار هذه المكونات.
عند تحديد الجزء Conception، تتوفر المكونات في مجلد بعلامات تبويب أعلى نافذة التصميم:

لبناء الواجهة الرسومية، لدينا مكونات swing (1) ومكونات awt (2). سنستخدم هنا مكونات swing. في شريط المكونات أعلاه، اختر مكون JLabel (3)، ومكون JTextField (4)، ومكون JButton (5) وضعها في حاوية نافذة التصميم.

الآن، لنقوم بتخصيص كل مكون من هذه المكونات الثلاثة:
- العلامة (JLabel) jLabel1
حدد المكون لفتح نافذة خصائصه. في هذه النافذة، قم بتعديل الخصائص التالية: الاسم: lblSaisie، النص: إدخال
- حقل الإدخال (JTextField) jTextfield1
حدد المكون لفتح نافذة خصائصه. في هذه النافذة، قم بتعديل الخصائص التالية: name: txtSaisie، text: لا تضع شيئًا
- الزر (JButton): الاسم: cmdAfficher، النص: عرض
لدينا الآن النافذة التالية:

والهيكل التالي:

يمكننا تشغيل (F9) مشروعنا للحصول على نظرة أولية على النافذة أثناء العمل:

أغلق النافذة. يتبقى لنا كتابة الإجراء المرتبط بالنقر على الزر Afficher. حدد الزر للوصول إلى نافذة خصائصه. تحتوي هذه النافذة على علامتي تبويب: propriétés و événements. اختر événements.

تسرد العمود الأيسر من النافذة الأحداث المحتملة على الزر. النقر على زر ما يتوافق مع الحدث actionPerformed. يحتوي العمود الأيمن على اسم الإجراء الذي يتم استدعاؤه عند حدوث الحدث المقابل. انقر على الخلية الموجودة على يمين الحدث actionPerformed:

يقوم JBuilder بإنشاء اسم افتراضي لكل معالج أحداث بالصيغة nomComposant_nomEvénement، كما في المثال cmdAfficher_actionPerformed. يمكن حذف الاسم المقترح افتراضيًا وإدخال اسم آخر. للوصول إلى كود معالج cmdAfficher_actionPerformed، يكفي النقر مرتين على اسمه أعلاه. عندها ننتقل تلقائيًا إلى جزء مصدر الفئة الموضوع على هيكل كود معالج الحدث:
لم يتبق لنا سوى إكمال هذا الرمز. هنا، نريد عرض مربع حوار يحتوي على محتوى الحقل txtSaisie:
void cmdAfficher_actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(),
"Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
}
JOptionPane هي فئة من مكتبة javax.swing. وهي تسمح بعرض رسائل مصحوبة برمز أو طلب معلومات من المستخدم. هنا، نستخدم طريقة ثابتة من الفئة:

parentComponent | الكائن الحاوية "الأصل" لمربع الحوار: هنا this. |
رسالة | كائن المراد عرضه. هنا محتوى حقل الإدخال |
title | عنوان مربع الحوار |
messageType | نوع الرسالة المراد عرضها. يحدد الرمز الذي سيتم عرضه في المربع بجانب الرسالة. القيم الممكنة: INFORMATION_MESSAGE، QUESTION_MESSAGE، ERROR_MESSAGE، WARNING_MESSAGE، PLAIN_MESSAGE |
دعونا نقوم بتشغيل تطبيقنا (F9):


5.2.3.2. رمز فئة النافذة
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import javax.swing.event.*;
public class interface2 extends JFrame {
JPanel contentPane;
JLabel lblSaisie = new JLabel();
JTextField txtSaisie = new JTextField();
JButton cmdAfficher = new JButton();
/**بناء الإطار*/
public interface2() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
lblSaisie.setText("Saisie");
lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(null);
this.setSize(new Dimension(304, 129));
this.setTitle("Saisies & boutons - 1");
txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
cmdAfficher.setText("Afficher");
cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdAfficher_actionPerformed(e);
}
});
contentPane.add(lblSaisie, null);
contentPane.add(txtSaisie, null);
contentPane.add(cmdAfficher, null);
}
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
void cmdAfficher_actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
}
}
5.2.3.2.1. السمات
JPanel contentPane;
JLabel lblSaisie = new JLabel();
JTextField txtSaisie = new JTextField();
JButton cmdAfficher = new JButton();
يوجد صندوق المكونات من النوع JPanel والمكونات الثلاثة.
5.2.3.2.2. الشركة المصنعة
/**بناء الإطار*/
public interface2() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
lblSaisie.setText("Saisie");
lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(null);
this.setSize(new Dimension(304, 129));
this.setTitle("Saisies & boutons - 1");
txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
cmdAfficher.setText("Afficher");
cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdAfficher_actionPerformed(e);
}
});
contentPane.add(lblSaisie, null);
contentPane.add(txtSaisie, null);
contentPane.add(cmdAfficher, null);
}
مُنشئ interface2 مشابه لمُنشئ واجهة المستخدم الرسومية السابقة التي تمت دراستها. توجد الاختلافات في الطريقة jbInit: يعتمد كود إنشاء النافذة على المكونات التي تم وضعها فيها. يمكننا إعادة استخدام كود jbInit مع إضافة تعليقاتنا الخاصة:
private void jbInit() throws Exception {
// النافذة نفسها (الحجم، العنوان)
this.setSize(new Dimension(304, 129));
this.setTitle("Saisies & boutons - 1");
// حاوية المكونات
contentPane = (JPanel) this.getContentPane();
// لا يوجد مدير تنسيق لهذا الحاوية
contentPane.setLayout(null);
// العلامة lblSaisie (التسمية، الموضع، الحجم)
lblSaisie.setText("Saisie");
lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
// حقل الإدخال (الموضع، الحجم)
txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
// زر العرض (التسمية، الموضع، الحجم)
cmdAfficher.setText("Afficher");
cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
// مدير أحداث الزر
cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdAfficher_actionPerformed(e);
}
});
// إضافة المكونات الثلاثة إلى الحاوية
contentPane.add(lblSaisie, null);
contentPane.add(txtSaisie, null);
contentPane.add(cmdAfficher, null);
}//jbInit
تجدر الإشارة إلى نقطتين:
- كان من الممكن كتابة هذا الكود يدويًا. وهذا يعني أنه يمكن الاستغناء عن Jbuilder لإنشاء واجهة رسومية.
- الطريقة التي تم بها تعيين معالج الأحداث للزر cmdAfficher. كان من الممكن إعلان معالج الأحداث للمكون cmdAfficher بواسطة cmdAfficher.addActionListener(new manager()) حيث يكون gestionnaire فئة تحتوي على طريقة عامة actionPerformed مسؤولة عن إدارة النقر على الزر Afficher. هنا، يستخدم Jbuilder كمُعالج مثيلًا لفئة مجهولة:
new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdAfficher_actionPerformed(e);
}
يتم إنشاء مثيل جديد للواجهة ActionListener مع تعريف طريقتها actionPerformed في نفس الوقت. تكتفي هذه الطريقة باستدعاء طريقة من الفئة interface2. كل هذا ليس سوى حيلة لتعريف إجراءات معالجة أحداث مكونات هذه النافذة في نفس فئة النافذة. يمكننا القيام بذلك بطريقة أخرى:
مما يعني أن الطريقة actionPerformed سيتم البحث عنها في this، أي في فئة النافذة. تبدو هذه الطريقة الثانية أبسط، لكن الطريقة الأولى تتمتع بميزة: فهي تسمح باستخدام معالجات مختلفة لأزرار مختلفة، في حين أن الطريقة الثانية لا تسمح بذلك. في الحالة الأخيرة، في الواقع، يجب أن تعالج الطريقة الوحيدة actionPeformed النقرات على الأزرار المختلفة، وبالتالي تبدأ بتحديد أي منها هو مصدر الحدث قبل البدء في العمل.
5.2.3.2.3. معالجات الأحداث
نجد هنا تلك التي تمت دراستها سابقًا:
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
// النقر على زر "عرض"
void cmdAfficher_actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
}
5.2.3.3. Conclusion
من المشروعين اللذين تمت دراستهما، يمكننا أن نستنتج أنه بمجرد إنشاء الواجهة الرسومية باستخدام Jbuilder، فإن عمل المطور يتمثل في كتابة معالجات الأحداث التي يريد إدارتها لهذه الواجهة الرسومية.
5.2.4. البحث عن المساعدة
مع Java، غالبًا ما نحتاج إلى المساعدة بسبب العدد الكبير جدًا من الفئات المتاحة. نقدم هنا بعض الإرشادات للعثور على المساعدة بشأن فئة ما. اختر خيار "المساعدة/مواضيع المساعدة" من القائمة.

تحتوي شاشة المساعدة عادةً على نافذتين:
- النافذة اليسرى حيث نحدد ما نبحث عنه. تحتوي على ثلاث علامات تبويب: Sommaire و Index و Chercher.
- والنافذة اليمنى التي تعرض نتيجة البحث
تتوفر مساعدة حول كيفية استخدام مساعدة Jbuilder. اختر من مساعدة Jbuilder الخيار "مساعدة/استخدام المساعدة". سيتم شرح كيفية استخدام المساعدة. على سبيل المثال، سيتم توضيح المكونات المختلفة لعارض المساعدة:

دعونا نلقي نظرة عن قرب على الصفحتين Sommaire و Index.
5.2.4.1. المساعدة: المحتويات

5.2.4.1.1. الملخص: مقدمة إلى Java
ستجد هنا العناصر الأساسية لـ Java، ولكن ليس هذا فقط، كما توضح قائمة الموضوعات التي يتم تناولها في هذا الخيار:

5.2.4.1.2. المحتويات: دروس
إذا اخترنا خيار "الدروس" في المحتويات أعلاه، فستعرض لنا النافذة اليمنى قائمة بالدروس المتاحة:

تعد الدروس الأساسية مفيدة بشكل خاص لبدء التعرف على Jbuilder. هناك العديد من الدروس الأخرى غير تلك المعروضة أعلاه، وعندما ترغب في تطوير تطبيق، قد يكون من المفيد التحقق مسبقًا مما إذا كان هناك درس يمكن أن يساعدك.
5.2.4.1.3. الملخص: JDK
عند تحديد الخيار Java 2 JDK 1.3، يمكن الوصول إلى جميع مكتبات JDK. بشكل عام، لا يجب البحث هنا إذا كنا بحاجة إلى معلومات عن فئة معينة ولا نعرف في أي مكتبة توجد. في المقابل، يكون هذا الخيار مفيدًا إذا كنا مهتمين بالحصول على نظرة عامة على مكتبات Java.
5.2.4.2. المساعدة: الفهرس
حدد علامة التبويب "فهرس" في النافذة اليسرى في المساعدة. تتيح لك هذه الخيار، على سبيل المثال، العثور على مساعدة بشأن فئة ما. لنفترض، على سبيل المثال، أنك تريد معرفة طرق حقول الإدخال swing و JTextField. اكتب JTextField في حقل إدخال النص المطلوب البحث عنه:
![]()
ستعرض المساعدة إدخالات الفهرس التي تبدأ بالنص الذي تمت كتابته:

ما عليك سوى النقر مرتين على الإدخال الذي يهمك، وهو هنا JTextField class. ستظهر المساعدة الخاصة بهذه الفئة في النافذة اليمنى:

سيتم بعد ذلك تقديم وصف كامل للفئة.
5.2.5. بعض مكونات Swing
نقدم الآن مجموعة متنوعة من التطبيقات التي تستخدم المكونات الأكثر شيوعًا من طراز swing، بهدف التعرف على أهم أساليبها وخصائصها. بالنسبة لكل تطبيق، نعرض الواجهة الرسومية والرمز البرمجي ذي الصلة، ولا سيما رمز معالجات الأحداث.
5.2.5.1. المكونات JLabel و JTextField
لقد تعرفنا بالفعل على هذين المكونين. JLabel هو مكون نصي و JTextField هو مكون حقل إدخال. طريقتاهما الرئيسيتان هما
String getText() | للحصول على محتوى حقل الإدخال أو نص التسمية |
void setText(String unTexte) | لوضع unTexte في الحقل أو العنوان |
الأحداث التي تُستخدم عادةً لـ JTextField هي التالية:
actionPerformed | يشير إلى أن المستخدم قد أقر (بضغطة مفتاح Enter) النص الذي أدخله |
caretUpdate | يشير إلى أن المستخدم قد حرك مؤشر الإدخال |
inputMethodChanged | يشير إلى أن المستخدم قد عدّل حقل الإدخال |
فيما يلي مثال يستخدم الحدث caretUpdate لتتبع التغييرات في حقل الإدخال:

رقم | النوع | الاسم | الدور |
1 | JTextField | txtSaisie | حقل الإدخال |
2 | JTextField | txtControle | يعرض نص 1 في الوقت الفعلي |
3 | JButton | cmdEffacer | لمسح الحقلين 1 و 2 |
4 | JButton | cmdQuitter | للخروج من التطبيق |
الرمز ذو الصلة لهذا التطبيق هو التالي:
import java.awt.*;
....
public class Cadre1 extends JFrame {
JPanel contentPane;
JTextField txtSaisie = new JTextField();
JLabel jLabel1 = new JLabel();
JLabel jLabel2 = new JLabel();
JTextField txtControle = new JTextField();
JButton CmdEffacer = new JButton();
JButton CmdQuitter = new JButton();
/**بناء الإطار*/
public Cadre1() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
....
txtSaisie.addCaretListener(new javax.swing.event.CaretListener() {
public void caretUpdate(CaretEvent e) {
txtSaisie_caretUpdate(e);
}
});
...
CmdEffacer.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
CmdEffacer_actionPerformed(e);
}
});
...
CmdQuitter.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
CmdQuitter_actionPerformed(e);
}
});
....
}
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
...
}
void txtSaisie_caretUpdate(CaretEvent e) {
// تم تحريك مؤشر الإدخال
txtControle.setText(txtSaisie.getText());
}
void CmdQuitter_actionPerformed(ActionEvent e) {
// الخروج من التطبيق
System.exit(0);
}
void CmdEffacer_actionPerformed(ActionEvent e) {
// مسح محتوى حقل الإدخال
txtSaisie.setText("");
}
}
فيما يلي مثال على التنفيذ:

5.2.5.2. المكون JComboBox


المكون JComboBox عبارة عن قائمة منسدلة مصحوبة بحقل إدخال: يمكن للمستخدم إما اختيار عنصر من (2) أو كتابة نص في (1). بشكل افتراضي، لا يمكن تحرير مكونات JComboBox. يجب استدعاء الطريقة setEditable(true) صراحةً لتصبح قابلة للتحرير. لاكتشاف الفئة JComboBox، اكتب JComboBox في فهرس المساعدة.
يمكن إنشاء الكائن JComboBox بعدة طرق:
new JComboBox() | ينشئ قائمة منسدلة فارغة |
new JComboBox (Object[] items) | ينشئ قائمة منسدلة تحتوي على مصفوفة من الكائنات |
new JComboBox(Vector items) | نفس الشيء مع متجه من الكائنات |
قد يكون من المدهش أن تحتوي قائمة منسدلة على كائنات في حين أنها تحتوي عادةً على سلاسل أحرف. من الناحية المرئية، سيكون هذا هو الحال. إذا كانت قائمة منسدلة JComboBox تحتوي على كائن obj، فإنها تعرض السلسلة obj.toString(). نتذكر أن كل كائن له طريقة toString موروثة من الفئة Object والتي تُرجع سلسلة أحرف "تمثل" الكائن.
الطرق المفيدة للفئة JCombobox هي التالية:
void addItem(Object unObjet) | تضيف كائنًا إلى القائمة المنسدلة |
int getItemCount() | يعطي عدد عناصر القائمة المنسدلة |
Object getItemAt(int i) | يعطي الكائن رقم i من القائمة المنسدلة |
void insertItemAt(Object unObjet, int i) | يدرج unObjet في الموضع i من القائمة المنسدلة |
int getSelectedIndex() | يعطي رقم العنصر المحدد في القائمة المنسدلة |
Object getSelectedItem() | يعطي الكائن المحدد في القائمة المنسدلة |
void setSelectedIndex(int i) | يختار العنصر i من القائمة المنسدلة |
void setSelectedItem(Object unObjet) | يحدد الكائن المحدد في القائمة المنسدلة |
void removeAllItems() | يفرغ القائمة المنسدلة |
void removeItemAt(int i) | يزيل العنصر رقم i من القائمة المنسدلة |
void removeItem(Object unObjet) | إزالة الكائن المحدد من القائمة المنسدلة |
void setEditable(boolean val) | يجعل القائمة المنسدلة قابلة للتحرير (val=true) أو غير قابلة للتحرير (val=false) |
عند اختيار عنصر من القائمة المنسدلة، يحدث الحدث actionPerformed الذي يمكن استخدامه للإشعار بتغيير التحديد في القائمة المنسدلة. في التطبيق التالي، نستخدم هذا الحدث لعرض العنصر الذي تم تحديده في القائمة.

نقدم فقط الكود ذي الصلة من النافذة.
public class Cadre1 extends JFrame {
JPanel contentPane;
JComboBox jComboBox1 = new JComboBox();
JLabel jLabel1 = new JLabel();
/**إنشاء الإطار*/
public Cadre1() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
// المعالجة - ملء القائمة المنسدلة
String[] infos={"un","deux","trois","quatre"};
for(int i=0;i<infos.length;i++)
jComboBox1.addItem(infos[i]);
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
....
jComboBox1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jComboBox1_actionPerformed(e);
}
});
....
}
void jComboBox1_actionPerformed(ActionEvent e) {
// تم تحديد عنصر جديد - يتم عرضه
JOptionPane.showMessageDialog(this,jComboBox1.getSelectedItem(),
"actionPerformed",JOptionPane.INFORMATION_MESSAGE);
}
}
5.2.5.3. المكون JList
يعد مكون swing JList أكثر تعقيدًا من نظيره في المكتبة awt. هناك اختلافان مهمان:
- يتم إدارة محتوى القائمة بواسطة كائن مختلف عن القائمة نفسها. هنا سنستخدم كائن DefaultListModel، وهو كائن يُستخدم مثل Vector، ولكنه يُعلم كائن JList فور تغيير محتواه حتى يتغير العرض المرئي للقائمة أيضًا.
- القائمة غير قابلة للتمرير بشكل افتراضي. يجب وضع القائمة في حاوية ScrollPane التي تسمح بالتمرير.
في شفرة المصدر، يمكن تعريف قائمة بالطريقة التالية:
// متجه قيم القائمة
DefaultlistModel valeurs=new DefaultListModel();
// القائمة نفسها التي نربط بها متجه قيمها
JList jList1 = new JList(valeurs);
// الحاوية المتحركة التي توضع فيها القائمة للحصول على قائمة متحركة
JScrollPane jScrollPane1 = new JScrollPane(jList1);
لإدراج القائمة jList1 في الحاوية jScrollPane1، يعمل الرمز الذي تم إنشاؤه بواسطة JBuilder بطريقة مختلفة:
- إعلان الحاوية في سمات النافذة
- ثم في كود jbInit، يتم ربط القائمة بالحاوية
لإضافة قيم إلى القائمة JList1 أعلاه، يكفي إضافتها إلى متجه قيمها valeurs:
وبذلك نحصل على النافذة التالية:

كيف تم إنشاء هذه الواجهة؟
- نختار مكون JScrollPane من صفحة "حاويات swing" للمكونات ونضعه في النافذة مع ضبط حجمه حسب الحجم المطلوب
- نختار مكون JList في صفحة "swing" للمكونات ونضعه في الحاوية JScrollPane حيث يشغل كل المساحة.
يجب تعديل الكود الذي تم إنشاؤه بواسطة Jbuilder قليلاً للحصول على الكود التالي:
public class interfaceAppli extends JFrame {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
DefaultListModel valeurs=new DefaultListModel();
JList jList1 = new JList(valeurs);
JScrollPane jScrollPane1 = new JScrollPane();
JLabel jLabel2 = new JLabel();
/**بناء الإطار*/
public interfaceAppli() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
// المعالجة
// يتم تضمين القائمة في scrollPane
// تعيين القائمة
for(int i=0;i<10;i++)
valeurs.addElement(""+i);
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
....
// القائمة jList1 مرتبطة بالحاوية jcrollPane1
jScrollPane1.getViewport().add(jList1, null);
}
/**تم استبداله، وبالتالي يمكننا الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
....
}
}
لنكتشف الآن الطرق الرئيسية للفئة JList من خلال البحث عن JList في فهرس المساعدة. يمكن إنشاء الكائن JList بعدة طرق:

إحدى الطرق البسيطة هي تلك التي استخدمناها: إنشاء كائن DefaultListModel فارغ ثم ربطه بالقائمة المراد إنشاؤها بواسطة new JList(V). لا يتم إدارة محتوى القائمة بواسطة الكائن JList بل بواسطة الكائن الذي يحتوي على قيم القائمة. إذا تم إنشاء المحتوى باستخدام كائن DefaultListModel الذي يعتمد على الفئة Vector، فيمكن استخدام أساليب الفئة Vector لإضافة عناصر إلى القائمة وإدراجها وحذفها. يمكن أن تكون القائمة ذات اختيار فردي أو متعدد. ويتم تحديد ذلك بواسطة الطريقة setSelectionMode:

يمكن معرفة وضع التحديد الحالي باستخدام getSelectionMode:
![]()
يمكن الحصول على العنصر أو العناصر المحددة عبر الطرق التالية:

نستطيع ربط متجه قيم بقائمة باستخدام المنشئ JList(DefaultListModel). وبالعكس، يمكننا الحصول على الكائن DefaultListModel من قائمة JList عن طريق:

نحن نعرف ما يكفي لكتابة التطبيق التالي:

مكونات هذه النافذة هي كما يلي:
رقم | النوع | الاسم | الدور |
1 | JTextField | txtSaisie | حقل الإدخال |
2 | JList | jList1 | قائمة موجودة في حاوية jScrollPane1 |
3 | JList | jList2 | قائمة موجودة في حاوية jScrollPane2 |
4 | JButton | cmd1To2 | ينقل العناصر المحددة من القائمة 1 إلى القائمة 2 |
5 | JButton | cmd2To1 | يقوم بالعكس |
6 | JButton | cmdRaz1 | أفرغ القائمة 1 |
7 | JButton | cmdRaz2 | إفراغ القائمة 2 |
يقوم المستخدم بكتابة نص في الحقل (1) ثم يقوم بتأكيده. عندها يحدث الحدث actionPerformed في حقل الإدخال الذي يتم استخدامه لإضافة النص المدخل إلى القائمة 1. فيما يلي الكود المفيد لهذه الوظيفة الأولى:
public class interfaceAppli extends JFrame {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
JLabel jLabel2 = new JLabel();
JLabel jLabel3 = new JLabel();
JTextField txtSaisie = new JTextField();
JButton cmd1To2 = new JButton();
JButton cmd2To1 = new JButton();
DefaultListModel v1=new DefaultListModel();
DefaultListModel v2=new DefaultListModel();
JList jList1 = new JList(v1);
JList jList2 = new JList(v2);
JScrollPane jScrollPane1 = new JScrollPane();
JScrollPane jScrollPane2 = new JScrollPane();
JButton cmdRaz1 = new JButton();
JButton cmdRaz2 = new JButton();
JLabel jLabel4 = new JLabel();
/**بناء الإطار*/
public interfaceAppli() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}//interfaceAppli
/**تهيئة المكون*/
private void jbInit() throws Exception {
...
txtSaisie.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
txtSaisie_actionPerformed(e);
}
});
...
// يتم وضع Jlist1 في الحاوية jScrollPane1
jScrollPane1.getViewport().add(jList1, null);
// يتم وضع Jlist2 في الحاوية jScrollPane2
jScrollPane2.getViewport().add(jList2, null);
...
}
/**تم استبداله، وبذلك يمكننا الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
...
}
void txtSaisie_actionPerformed(ActionEvent e) {
// تم التحقق من صحة نص الإدخال
// يتم استرداده بعد إزالة المسافات في البداية والنهاية
String texte=txtSaisie.getText().trim();
// إذا كان فارغًا، فلا نريده
if(texte.equals("")){
// رسالة خطأ
JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
"Erreur",JOptionPane.WARNING_MESSAGE);
// النهاية
return;
}//if
// إذا لم يكن فارغًا، نضيفه إلى قيم القائمة 1
v1.addElement(texte);
// ونفرغ حقل الإدخال
txtSaisie.setText("");
}/// txtSaisie_actionperformed
}//فئة
رمز نقل العناصر المحددة من قائمة إلى أخرى هو التالي:
void cmd1To2_actionPerformed(ActionEvent e) {
// نقل العناصر المحددة من القائمة 1 إلى القائمة 2
transfert(jList1,jList2);
}//cmd1To2
void cmd2To1_actionPerformed(ActionEvent e) {
// نقل العناصر المحددة في jList2 إلى jList1
transfert(jList2,jList1);
}//cmd2TO1
private void transfert(JList L1, JList L2){
// نقل العناصر المحددة في القائمة 1 إلى القائمة 2
// نسترد جدول مؤشرات العناصر المحددة في L1
int[] indices=L1.getSelectedIndices();
// هل هناك شيء يجب فعله؟
if (indices.length==0) return;
// استرداد قيم L1
DefaultListModel v1=(DefaultListModel)L1.getModel();
// وتلك الموجودة في L2
DefaultListModel v2=(DefaultListModel)L2.getModel();
for(int i=indices.length-1;i>=0;i--){
// نضيف إلى L2 القيم المحددة في L1
v2.addElement(v1.elementAt(indices[i]));
// يجب حذف عناصر L1 التي تم نسخها إلى L2 من L1
v1.removeElementAt(indices[i]);
}//لـ
}//نقل
الرمز المرتبط بالأزرار Raz هو أبسط ما يكون:
void cmdRaz1_actionPerformed(ActionEvent e) {
// إفراغ القائمة 1
v1.removeAllElements();
}//cmd Raz1
void cmdRaz2_actionPerformed(ActionEvent e) {
// إفراغ القائمة 2
v2.removeAllElements();
}///cmd Raz2
5.2.5.4. مربعات الاختيار JCheckBox، أزرار الاختيار JButtonRadio
نقترح كتابة التطبيق التالي:

مكونات النافذة هي كما يلي:
رقم | النوع | الاسم | الدور |
1 | JButtonRadio | jButtonRadio1 jButtonRadio2 jButtonRadio3 | 3 أزرار اختيار تابعة للمجموعة buttonGroup1 |
2 | JCheckBox | jCheckBox1 jCheckBox2 jCheckBox3 | 3 خانات للتحديد |
3 | JList | jList1 | قائمة في حاوية jScrollPane1 |
4 | ButtonGroup | buttonGroup1 | مكون غير مرئي - يستخدم لتجميع الأزرار الثلاثة بحيث عندما يضيء أحدها، تنطفئ البقية. |
يمكن إنشاء مجموعة من أزرار الاختيار بالطريقة التالية:
- نضع كل زر من أزرار الراديو دون الاهتمام بتجميعها
- نضع في الحاوية مكون Swing ButtonGroup. هذا المكون غير مرئي. لذلك لا يظهر في مصمم النافذة. لكنه يظهر في هيكلها:

نرى أعلاه في الفرع Autre السمات غير المرئية للنافذة. بمجرد إنشاء مجموعة من أزرار الاختيار، يمكن ربط كل زر من أزرار الاختيار بها. للقيام بذلك، نختار خصائص زر الاختيار:

وفي خاصية buttonGroup لزر الاختيار، نضع اسم المجموعة التي نريد وضع زر الاختيار فيها، وهي هنا buttonGroup1. نكرر هذه العملية للأزرار الثلاثة.
الطريقة الرئيسية لأزرار الاختيار ومربعات الاختيار هي الطريقة isSelected() التي تشير إلى ما إذا كان المربع أو الزر محددًا. يمكن معرفة النص المرتبط بالمكون باستخدام getText() وتعيينه باستخدام setText(String unTexte). يمكن تحديد مربع الاختيار/زر الاختيار باستخدام الطريقة setSelected(boolean value).
عند النقر على زر الاختيار أو مربع الاختيار، يتم تشغيل الحدث actionPerformed. في الكود التالي، نستخدم هذا الحدث لتتبع تغييرات قيم أزرار الاختيار ومربعات الاختيار:
public class interfaceAppli extends JFrame {
JPanel contentPane;
JRadioButton jRadioButton1 = new JRadioButton();
JRadioButton jRadioButton2 = new JRadioButton();
JRadioButton jRadioButton3 = new JRadioButton();
JCheckBox jCheckBox1 = new JCheckBox();
JCheckBox jCheckBox2 = new JCheckBox();
JCheckBox jCheckBox3 = new JCheckBox();
ButtonGroup buttonGroup1 = new ButtonGroup();
JScrollPane jScrollPane1 = new JScrollPane();
DefaultListModel valeurs=new DefaultListModel();
JList jList1 = new JList(valeurs);
/**إنشاء الإطار*/
public interfaceAppli() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
jRadioButton1.setSelected(true);
jRadioButton1.setText("un");
jRadioButton1.setBounds(new Rectangle(57, 31, 49, 23));
jRadioButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
afficheRadioButtons(e);
}
});
jRadioButton2.setBounds(new Rectangle(113, 30, 49, 23));
jRadioButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
afficheRadioButtons(e);
}
});
jRadioButton2.setText("deux");
jRadioButton3.setBounds(new Rectangle(168, 30, 49, 23));
jRadioButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
afficheRadioButtons(e);
}
});
jRadioButton3.setText("trois");
// أزرار الاختيار مجمعة
buttonGroup1.add(jRadioButton1);
buttonGroup1.add(jRadioButton2);
buttonGroup1.add(jRadioButton3);
// مربعات الاختيار
jCheckBox1.setText("A");
jCheckBox1.setBounds(new Rectangle(58, 69, 32, 17));
jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
afficheCases(e);
}
});
jCheckBox2.setBounds(new Rectangle(112, 69, 40, 17));
jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
afficheCases(e);
}
});
jCheckBox2.setText("B");
jCheckBox3.setText("C");
jCheckBox3.setBounds(new Rectangle(170, 69, 37, 17));
jCheckBox3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
afficheCases(e);
}
});
....
}
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
...
}
private void afficheRadioButtons(ActionEvent e){
// يعرض قيم أزرار الاختيار الثلاثة
valeurs.addElement("boutons radio=("+jRadioButton1.isSelected()+","+
jRadioButton2.isSelected()+","+jRadioButton3.isSelected()+")");
}//afficheRadioButtons
void afficheCases(ActionEvent e) {
// يعرض قيم مربعات الاختيار
valeurs.addElement("cases à cocher=["+jCheckBox1.isSelected()+","+
jCheckBox2.isSelected()+","+jCheckBox3.isSelected()+")");
}//afficheCases
}//تصنيف
فيما يلي مثال على التنفيذ:

5.2.5.5. المكون JScrollBar
لنقم بتنفيذ التطبيق التالي:

رقم | النوع | الاسم | الدور |
1 | JScrollBar | jScrollBar1 | محول أفقي |
2 | JScrollBar | jScrollBar2 | محول عمودي |
3 | JTextField | txtvaleurHS | يعرض قيمة المتغير الأفقي 1 - يسمح أيضًا بتثبيت هذه القيمة |
4 | JTextField | txtvaleurVS | يعرض قيمة المتغير الرأسي 2 - يسمح أيضًا بتثبيت هذه القيمة |
- يتيح المنظم JScrollBar للمستخدم اختيار قيمة من نطاق من القيم الصحيحة التي يرمز لها "شريط" المنظم الذي يتحرك عليه المؤشر.
- بالنسبة لمزلاج أفقي، يمثل الطرف الأيسر القيمة الدنيا للنطاق، والطرف الأيمن القيمة القصوى، والمؤشر القيمة الحالية المختارة. بالنسبة لمزلاج عمودي، يمثل الطرف العلوي القيمة الدنيا، والطرف السفلي القيمة القصوى. تبلغ قيمة الزوج (min,max) افتراضيًا (0,100).
- يؤدي النقر على طرفي المنظم إلى تغيير القيمة بمقدار زيادة (إيجابية أو سلبية) وفقًا للطرف الذي تم النقر عليه المسمى unitIncrement والذي يساوي 1 افتراضيًا.
- يؤدي النقر على جانبي المؤشر إلى تغيير القيمة بمقدار زيادة (إيجابية أو سلبية) وفقًا للطرف الذي تم النقر عليه المسمى blockIncrement والذي يساوي 10 افتراضيًا.
- يمكن الحصول على هذه القيم الخمس (الحد الأدنى، الحد الأقصى، القيمة، unitIncrement، blockIncrement) باستخدام الطرق getMinimum()، getMaximum()، getValue()، getUnitIncrement()، getBlockIncrement() التي ترجع جميعها عددًا صحيحًا ويمكن تعيينها بواسطة الطرق setMinimum(int min)، setMaximum(int max)، setValue(int val)، setUnitIncrement(int uInc)، setBlockIncrement(int bInc)
هناك بعض النقاط التي يجب معرفتها عند استخدام مكونات JScrollBar. أولاً، يمكن العثور عليها في شريط مكونات Swing:

عند وضعه على الحاوية، يكون الوضع الافتراضي له هو الوضع الرأسي. يمكن جعله أفقيًا باستخدام الخاصية orientation أدناه:

في ورقة الخصائص أعلاه، نرى أن لدينا إمكانية الوصول إلى الخصائص minimum، maximum، value، unitIncrement، blockIncrement و JScrollbar. وبالتالي يمكننا تعيينها عند التصميم. عند وضع شريط التمرير على الحاوية، لا يظهر "نطاق التغيير" الخاص بها:

يمكن حل هذه المشكلة بإضافة حدود للمكون. يتم ذلك باستخدام خاصية border التي يمكن أن تأخذ قيمًا مختلفة:

إليك مثال على النتيجة التي يعطيها RaisedBevel:
![]()
عند النقر على الطرف العلوي لمزلاج عمودي، تنخفض قيمته. قد يفاجئ هذا المستخدم العادي الذي يتوقع عادةً أن ترتفع القيمة. يتم حل هذه المشكلة عن طريق تعيين قيمة سالبة لـ unitIncrement و blockIncrement.
كيف يمكن متابعة التغيرات في شريط التمرير؟ عندما تتغير قيمته، يحدث الحدث adjustmentValueChanged. يكفي ربط إجراء بهذا الحدث لتلقي إشعار بكل تغيير في قيمة شريط التمرير.

الرمز المفيد لتطبيقنا هو التالي:
....
public class cadreAppli extends JFrame {
JPanel contentPane;
JScrollBar jScrollBar1 = new JScrollBar();
Border border1;
JTextField txtValeurHS = new JTextField();
JScrollBar jScrollBar2 = new JScrollBar();
JTextField txtValeurVS = new JTextField();
TitledBorder titledBorder1;
/**إنشاء الإطار*/
public cadreAppli() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
...
// حدود لأشرطة التمرير
border1 = BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.white,Color.white,new Color(134, 134, 134),new Color(93, 93, 93));
// لا يوجد عنوان للحد
titledBorder1 = new TitledBorder("");
jScrollBar1.setOrientation(JScrollBar.HORIZONTAL);
jScrollBar1.setBorder(BorderFactory.createRaisedBevelBorder());
jScrollBar1.setAutoscrolls(true);
jScrollBar1.setBounds(new Rectangle(37, 17, 218, 20));
jScrollBar1.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
jScrollBar1_adjustmentValueChanged(e);
}
});
txtValeurHS.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
txtValeurHS_actionPerformed(e);
}
});
jScrollBar2.setBounds(new Rectangle(39, 51, 30, 27));
jScrollBar2.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
jScrollBar2_adjustmentValueChanged(e);
}
});
jScrollBar2.setAutoscrolls(true);
jScrollBar2.setUnitIncrement(-1);
jScrollBar2.setBorder(BorderFactory.createRaisedBevelBorder());
txtValeurVS.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
txtValeurVS_actionPerformed(e);
}
});
......
}
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
...
}
void jScrollBar1_adjustmentValueChanged(AdjustmentEvent e) {
// تغيرت قيمة شريط التمرير 1
txtValeurHS.setText(""+jScrollBar1.getValue());
}
void jScrollBar2_adjustmentValueChanged(AdjustmentEvent e) {
// تغيرت قيمة شريط التمرير 2
txtValeurVS.setText(""+jScrollBar2.getValue());
}
void txtValeurHS_actionPerformed(ActionEvent e) {
// يتم تعيين قيمة شريط التمرير الأفقي
setValeur(jScrollBar1,txtValeurHS);
}
void txtValeurVS_actionPerformed(ActionEvent e) {
// يتم تعيين قيمة شريط التمرير الرأسي
setValeur(jScrollBar2,txtValeurVS);
}
private void setValeur(JScrollBar jS, JTextField jT){
// تثبيت قيمة شريط التمرير jS بنص الحقل jT
int valeur=0;
try{
valeur=Integer.parseInt(jT.getText());
jS.setValue(valeur);
}
catch (Exception e){
// عرض الخطأ
afficher(""+e);
}//try-catch
}//setValeur
void afficher(String message){
// يعرض رسالة في مربع
JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
}//عرض
}
فيما يلي مثال على التنفيذ:

5.2.5.6. المكون JTextArea
المكون JTextArea هو مكون يمكن فيه إدخال عدة أسطر من النص، على عكس المكون JTextField الذي لا يمكن فيه إدخال سوى سطر واحد. إذا تم وضع هذا المكون في حاوية قابلة للتمرير (JScrollPane)، فسيكون لدينا حقل إدخال نص قابل للتمرير. يمكن أن نجد هذا النوع من المكونات، على سبيل المثال، في تطبيق بريد إلكتروني حيث يتم كتابة نص الرسالة المراد إرسالها في مكون JTextArea. الطرق المعتادة هي String getText() لمعرفة محتوى منطقة النص، و setText(String unTexte) لوضع unTexte في منطقة النص، و append(String unTexte) لإضافة unTexte إلى النص الموجود بالفعل في منطقة النص. لنفكر في التطبيق التالي:

رقم | النوع | الاسم | الدور |
1 | JTextArea | txtTexte | مجال نص متعدد الأسطر |
2 | JButton | cmdAfficher | يعرض محتوى 1 في مربع حوار |
3 | JButton | cmdEffacer | مسح محتوى 1 |
4 | JTextField | txtAjout | نص مضاف إلى نص 1 عند تأكيده بواسطة مفتاح الإدخال. |
5 | JScrollPane | jScrollPane1 | حاوية متحركة تم وضع منطقة النص 1 فيها للحصول على منطقة نص متحركة. |
الرمز المفيد هو التالي:
.....
public class cadreAppli extends JFrame {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
JButton cmdAfficher = new JButton();
JScrollPane jScrollPane1 = new JScrollPane();
JTextArea txtTexte = new JTextArea();
JLabel jLabel2 = new JLabel();
JTextField txtAjout = new JTextField();
JButton cmdEffacer = new JButton();
/**إنشاء الإطار*/
public cadreAppli() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdAfficher_actionPerformed(e);
}
});
txtAjout.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
txtAjout_actionPerformed(e);
}
});
cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdEffacer_actionPerformed(e);
}
});
.......
jScrollPane1.getViewport().add(txtTexte, null);
}
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
........
}
void cmdAfficher_actionPerformed(ActionEvent e) {
// يعرض محتوى TextArea
afficher(txtTexte.getText());
}
void afficher(String message){
// يعرض رسالة في مربع
JOptionPane.showMessageDialog(this,message,"Suivi",JOptionPane.INFORMATION_MESSAGE);
}// عرض
void cmdEffacer_actionPerformed(ActionEvent e) {
txtTexte.setText("");
}//cmdEffacer_actionPerformed
void txtAjout_actionPerformed(ActionEvent e) {
// إضافة نص
txtTexte.append(txtAjout.getText());
// مسح الإضافة
txtAjout.setText("");
}//
}
5.2.6. أحداث الماوس
عند الرسم في حاوية، من المهم معرفة موضع الماوس لعرض نقطة عند النقر، على سبيل المثال. تؤدي تحركات الماوس إلى حدوث أحداث في الحاوية التي يتحرك فيها. فيما يلي، على سبيل المثال، الأحداث التي يقترحها JBuilder لحاوية JPanel:

mouseClicked | نقرة بالماوس |
mouseDragged | تحريك الماوس مع الضغط على الزر الأيسر |
mouseEntered | دخلت الماوس للتو إلى سطح الحاوية |
mouseExited | الماوس قد غادر للتو سطح الحاوية |
mouseMoved | الماوس يتحرك |
mousePressed | الضغط على الزر الأيسر للماوس |
mouseReleased | تحرير الزر الأيسر للماوس |
فيما يلي برنامج يتيح فهم أفضل للأوقات التي تحدث فيها أحداث الماوس المختلفة:

رقم | النوع | الاسم | الدور |
1 | JTextField | txtPosition | لعرض موضع الماوس في الحاوية (ربما MouseMoved) |
2 | JList | lstAffichage | لعرض أحداث الماوس بخلاف MouseMoved |
3 | JButton | cmdEffacer | لمسح محتوى 2 |
عند تشغيل هذا البرنامج، إليك ما نحصل عليه عند النقر:

يتم تكديس الأحداث من الأعلى في القائمة. كما تشير لقطة الشاشة أعلاه إلى أن النقر يؤدي إلى ثلاثة أحداث، بالترتيب:
- MousePressed عند الضغط على الزر
- MouseReleased عند تحريره
- MouseClicked مما يشير إلى أن تسلسل الحدثين السابقين يُعتبر نقرة. قد تكون هذه نقرة مزدوجة. لكن في الأعلى، تشير المعلومة clickCount=1 إلى أنها نقرة مفردة.
الآن إذا ضغطنا على الزر، وحركنا الماوس، ثم أطلقنا الزر:

نرى هنا الأحداث الثلاثة:
- MousePressed عند الضغط على الزر في البداية
- MouseDragged عند تحريك الماوس مع الضغط على الزر
- MouseReleased عند تحرير الزر
في المثالين أعلاه، نرى أن حدث الماوس يحمل معه معلومات متنوعة بما في ذلك إحداثيات (x، y) للماوس، على سبيل المثال (408، 65) في السطر الأول أعلاه.
وإذا واصلنا على هذا النحو، نكتشف أن الحدث MouseExited يتم تشغيله بمجرد خروج الماوس من الحاوية أو مروره فوق أحد مكوناتها. في الحالة الأخيرة، يتلقى الحاوية الحدث MouseExited والمكون الحدث MouseEntered. وسيحدث العكس عندما يغادر الماوس المكون للعودة إلى الحاوية.
ماذا يحدث عند النقر المزدوج؟

نحصل على نفس الأحداث تمامًا كما في حالة النقر مرة واحدة. إلا أن الحدث يحمل معه المعلومات clickCount=2 (انظر أعلاه) التي تشير إلى أنه كان هناك بالفعل نقر مزدوج.
الرمز المفيد لهذا التطبيق هو التالي:
public class Cadre1 extends JFrame {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
JTextField txtPosition = new JTextField();
JScrollPane jScrollPane1 = new JScrollPane();
DefaultListModel valeurs=new DefaultListModel();
JList lstAffichage = new JList(valeurs);
JButton cmdEffacer = new JButton();
/**إنشاء الإطار*/
public Cadre1() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
contentPane.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
contentPane_mouseMoved(e);
}
public void mouseDragged(MouseEvent e) {
contentPane_mouseDragged(e);
}
});
contentPane.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(MouseEvent e) {
contentPane_mouseEntered(e);
}
public void mouseExited(MouseEvent e) {
contentPane_mouseExited(e);
}
public void mousePressed(MouseEvent e) {
contentPane_mousePressed(e);
}
public void mouseClicked(MouseEvent e) {
contentPane_mouseClicked(e);
}
public void mouseReleased(MouseEvent e) {
contentPane_mouseReleased(e);
}
});
cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdEffacer_actionPerformed(e);
}
});
jScrollPane1.getViewport().add(lstAffichage, null);
...............
}
/**تم استبداله، حتى نتمكن من الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
.........
}
void contentPane_mouseMoved(MouseEvent e) {
txtPosition.setText("("+e.getX()+","+e.getY()+")");
}
void contentPane_mouseDragged(MouseEvent e) {
afficher(e);
}
void contentPane_mouseEntered(MouseEvent e) {
afficher(e);
}
void afficher(MouseEvent e){
// يعرض الحدث e في القائمة lstAffichage
valeurs.insertElementAt(e,0);
}
void contentPane_mouseExited(MouseEvent e) {
afficher(e);
}
void contentPane_mousePressed(MouseEvent e) {
afficher(e);
}
void contentPane_mouseClicked(MouseEvent e) {
afficher(e);
}
void contentPane_mouseReleased(MouseEvent e) {
afficher(e);
}
void cmdEffacer_actionPerformed(ActionEvent e) {
// مسح القائمة
valeurs.removeAllElements();
}
}
5.2.7. إنشاء نافذة مع قائمة
لنرى الآن كيفية إنشاء نافذة مع قائمة باستخدام Jbuilder. سنقوم بإنشاء النافذة التالية:


أنشئ مشروعًا جديدًا يبدأ بنافذة فارغة. اختر من قائمة مكونات "Conteneurs Swing" المكون JMenuBar (انظر 1 أدناه) وقم بإفلاته على النافذة قيد التصميم.

لا يظهر أي شيء في نافذة التصميم، لكن المكون JMenuBar يظهر في لوحة هيكل النافذة:

انقر نقرًا مزدوجًا على العنصر jMenuBar1 أعلاه للوصول إلى القائمة في وضع التصميم:

1 | إدراج عنصر قائمة |
2 | إدراج فاصل |
3 | إدراج قائمة متداخلة |
4 | حذف عنصر من القائمة |
5 | تعطيل عنصر من القائمة |
6 | عنصر قائمة قابل للتحديد |
7 | عكس زر الاختيار |
لإنشاء عنصر القائمة الأول، اكتب "خيارات أ" في المربع أ أعلاه، ثم أدناه بالترتيب: A1، A2، فاصل، A3، A4.

ثم بجانبه:

استخدم الأداة 3 للإشارة إلى أن B3 هو قائمة فرعية متداخلة.
مع تقدم تصميم القائمة، تتطور البنية المنطقية لنافذتنا:

إذا قمنا بتشغيل تطبيقنا الآن، فسنرى نافذة فارغة بدون قائمة. علينا ربط القائمة التي أنشأناها بنافذتنا. للقيام بذلك، في بنية النافذة، حدد الكائن this:

وبذلك يمكنك الوصول إلى خصائص this:

إحدى هذه الخصائص هي JMenuBar التي تُستخدم لتحديد القائمة التي سيتم ربطها بالنافذة. انقر في الخلية الموجودة على يمين JMenuBar. سيتم عندئذ عرض جميع القوائم التي تم إنشاؤها. هنا، لن يكون لدينا سوى jMenuBar1. حدده.
ابدأ تشغيل التطبيق (F9):

الآن، لدينا قائمة ولكن الخيارات لا تؤدي أي وظيفة في الوقت الحالي. يتم التعامل مع خيارات القائمة كمكونات: فهي تحتوي على خصائص وأحداث. في بنية القائمة، حدد الخيار jMenuItem1:

وبذلك يمكنك الوصول إلى خصائصها وأحداثها:

حدد صفحة الأحداث وانقر على الخلية الموجودة على يمين الحدث actionPerformed: هذا هو الحدث الذي يحدث عند النقر على عنصر من عناصر القائمة. يتم اقتراح إجراء معالجة لك بشكل افتراضي. انقر عليه مرتين للوصول إلى كوده:

سنكتب الكود البسيط التالي:
void jMenuItem1_actionPerformed(ActionEvent e) {
afficher("L'option A1 a été sélectionnée");
}
void afficher(String message){
// يعرض رسالة في مربع
JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
}//عرض
قم بتشغيل التطبيق وحدد الخيار A1 للحصول على الرسالة التالية:

الرمز المفيد لهذا التطبيق هو التالي:
.....
public class Cadre1 extends JFrame {
JPanel contentPane;
BorderLayout borderLayout1 = new BorderLayout();
JMenuBar jMenuBar1 = new JMenuBar();
JMenu jMenu1 = new JMenu();
JMenuItem jMenuItem1 = new JMenuItem();
JMenuItem jMenuItem2 = new JMenuItem();
JMenuItem jMenuItem3 = new JMenuItem();
JMenuItem jMenuItem4 = new JMenuItem();
JMenu jMenu2 = new JMenu();
JMenuItem jMenuItem5 = new JMenuItem();
JMenuItem jMenuItem6 = new JMenuItem();
JMenu jMenu3 = new JMenu();
JMenuItem jMenuItem7 = new JMenuItem();
JMenuItem jMenuItem8 = new JMenuItem();
JMenuItem jMenuItem9 = new JMenuItem();
/**إنشاء الإطار*/
public Cadre1() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
/**تهيئة المكون*/
private void jbInit() throws Exception {
// النافذة مرتبطة بقائمة
this.setJMenuBar(jMenuBar1);
jMenu1.setText("Options A");
jMenuItem1.setText("A1");
jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jMenuItem1_actionPerformed(e);
}
});
jMenuItem2.setText("A2");
jMenuItem3.setText("A3");
jMenuItem4.setText("A4");
jMenu2.setText("Options B");
jMenuItem5.setText("B1");
jMenuItem6.setText("B2");
jMenu3.setText("B3");
jMenuItem7.setText("B31");
jMenuItem8.setText("B32");
jMenuItem9.setText("B4");
jMenuBar1.add(jMenu1);
jMenuBar1.add(jMenu2);
jMenu1.add(jMenuItem1);
jMenu1.add(jMenuItem2);
jMenu1.addSeparator();
jMenu1.add(jMenuItem3);
jMenu1.add(jMenuItem4);
jMenu2.add(jMenuItem5);
jMenu2.add(jMenuItem6);
jMenu2.add(jMenu3);
jMenu2.add(jMenuItem9);
jMenu3.add(jMenuItem7);
jMenu3.add(jMenuItem8);
}
/**تم استبداله، وبالتالي يمكننا الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
....
}
void jMenuItem1_actionPerformed(ActionEvent e) {
afficher("L'option A1 a été sélectionnée");
}
void afficher(String message){
// عرض رسالة في مربع
JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
}//عرض
}
5.3. مربعات الحوار
5.3.1. مربعات الرسائل
لقد استخدمنا بالفعل الفئة JOptionPane لعرض الرسائل. وبالتالي، فإن الكود التالي:
import javax.swing.*;
public class dialog1 {
public static void main(String arg[]){
JOptionPane.showMessageDialog(null,"Un message","Titre de la boîte",JOptionPane.INFORMATION_MESSAGE);
}
}
يؤدي إلى عرض المربع التالي:

عند إغلاق هذه النافذة، تختفي ولكن مؤشر الترابط الذي كان يعمل فيه لا يتوقف. لا يحدث هذا عادةً. تُستخدم مربعات الحوار داخل تطبيق يستخدم في وقت ما الأمر System.exit(n) لإيقاف جميع مؤشرات الترابط. سنذكر ذلك في الأمثلة القادمة التي تم إنشاؤها جميعًا على نفس النموذج. في نظام التشغيل DOS، يمكن إيقاف التطبيق باستخدام Ctrl-C. مع JBuilder، سنستخدم خيار "تشغيل/إعادة تعيين البرنامج" (Ctrl-F2). من ناحية أخرى، الحجة الأولى لـ showMessageDialog هنا هي null. بشكل عام، هذا ليس هو الحال. بل هي بالأحرى this حيث تشير this إلى النافذة الرئيسية للتطبيق.
5.3.2. المظهر والشكل
قد يختلف مظهر المربع أعلاه. يمكن ضبط هذا المظهر عبر الفئة javax.swing.UIManager. عندما قمنا بتعليق الكود الذي تم إنشاؤه بواسطة JBuilder لنافذتنا الأولى، صادفنا تعليماً لم نتوقف عنده:
تسمح الطريقة setLookAndFeel للفئة UIManager (UI=واجهة المستخدم) بتحديد مظهر الواجهات الرسومية. تحتوي الفئة UIManager على طريقة تسمح بمعرفة "المظاهر" الممكنة للواجهات:
static UIManager.LookAndFeelInfo[] | getInstalledLookAndFeels() |
تُرجع الطريقة مصفوفة من العناصر من النوع LookAndFeelInfo. تحتوي هذه الفئة على طريقة:
String | getClassName() |
التي تعطي اسم الفئة التي "تنفذ" مظهرًا معينًا. لنجرب البرنامج التالي:
import javax.swing.*;
public class LookAndFeels {
// يعرض أشكال المظهر المتاحة
public static void main(String[] args) {
// قائمة بأشكال المظهر المثبتة
UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
// عرض
for(int i=0;i<lf.length;i++){
System.out.println(lf[i].getClassName());
}//من أجل
}//يدوي
}//تصنيف
ويعطي النتائج التالية:
javax.swing.plaf.metal.MetalLookAndFeel
com.sun.java.swing.plaf.motif.MotifLookAndFeel
com.sun.java.swing.plaf.windows.WindowsLookAndFeel
يبدو إذن أن هناك ثلاثة "مظاهر" مختلفة. لنعد إلى برنامجنا لعرض الرسائل ونجرب المظاهر المختلفة الممكنة:
import javax.swing.*;
public class LookAndFeel2 {
// يعرض أشكال المظهر المتاحة
public static void main(String[] args) {
// قائمة بأشكال المظهر المثبتة
UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
// العرض
for(int i=0;i<lf.length;i++){
// مدير المظهر
try{
UIManager.setLookAndFeel(lf[i].getClassName());
}catch(Exception ex){
System.err.println(ex.getMessage());
}//catch
// رسالة
JOptionPane.showMessageDialog(null,"Un message",lf[i].getClassName(),JOptionPane.INFORMATION_MESSAGE);
}//for
}//main
}//فئة
يؤدي التنفيذ إلى عرض النتائج التالية:
![]() | ![]() | ![]() |
التي تتوافق من اليمين إلى اليسار مع "المظاهر" Metal و Motif و Windows.
5.3.3. مربعات التأكيد
تحتوي الفئة JOptionPane على طريقة showConfirmDialog لعرض مربعات التأكيد مع الأزرار Oui، Non، Annuler. هناك العديد من الطرق showConfirmDialog المُثبَّتة. سنقوم بدراسة إحداها:
static int | showConfirmDialog(Component parentComponent, Object message, String title, int optionType) |
parentComponent | المكون الأصلي لمربع الحوار. غالبًا ما يكون النافذة أو القيمة null |
message | الرسالة المراد عرضها |
title | عنوان مربع الحوار |
optionType | JOptionPane.YES_NO_OPTION: أزرار نعم، لا JOptionPane.YES_NO_CANCEL_OPTION: أزرار نعم، لا، إلغاء |
النتيجة التي تعطيها الطريقة هي:
JOptionPane.YES_OPTION | نقر المستخدم على "نعم" |
JOptionPane.NO_OPTION | نقر المستخدم على "لا" |
JOptionPane.CANCEL_OPTION | نقر المستخدم على "إلغاء" |
JOptionPane.CLOSED_OPTION | أغلق المستخدم المربع |
فيما يلي مثال:
import javax.swing.*;
public class confirm1 {
public static void main(String[] args) {
// عرض مربعات التأكيد
int réponse;
affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_OPTION));
affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_CANCEL_OPTION));
}//الرئيسي
private static void affiche(int réponse){
// يحدد نوع الرد الذي تم الحصول عليه
switch(réponse){
case JOptionPane.YES_OPTION :
System.out.println("Oui");
break;
case JOptionPane.NO_OPTION :
System.out.println("Non");
break;
case JOptionPane.CANCEL_OPTION :
System.out.println("Annuler");
break;
case JOptionPane.CLOSED_OPTION :
System.out.println("Fermeture");
break;
}//
}//شاشة
}//تصنيف
![]() | ![]() |
على وحدة التحكم، يتم عرض الرسالتين Non و Annuler.
5.3.4. مربع الإدخال
توفر الفئة JOptionPane أيضًا إمكانية الإدخال باستخدام الطريقة showInputDialog. وهنا أيضًا، توجد عدة طرق مفرطة. نقدم إحداها:
static String | showInputDialog(Component parentComponent, Object message, String title, int messageType) |
المعلمات هي نفسها التي سبق أن تناولناها عدة مرات. تعرض هذه الطريقة سلسلة الأحرف التي كتبها المستخدم. إليك مثال على ذلك:
import javax.swing.*;
public class input1 {
public static void main(String[] args) {
// الإدخال
System.out.println("Chaîne saisie ["+
JOptionPane.showInputDialog(null,"Quel est votre nom","Saisie du nom",JOptionPane.QUESTION_MESSAGE )
+ "]");
}//يدوي
}//فئة
عرض مربع الإدخال:

عرض وحدة التحكم:
5.4. مربعات الاختيار
ننتقل الآن إلى عدد من مربعات الاختيار المحددة مسبقًا في Java 2:
JFileChooser | مربع اختيار يسمح بتحديد ملف في شجرة الملفات |
JColorChooser | مربع اختيار يسمح باختيار لون |
5.4.1. مربع الاختيار JFileChooser
سنقوم بإنشاء التطبيق التالي:

العناصر التالية:
رقم | النوع | الاسم | الدور |
0 | JScrollPane | jScrollPane1 | حاوية متحركة لمربع النص 1 |
1 | JTextArea نفسه في JScrollPane | txtTexte | نص مكتوب من قبل المستخدم أو تم تحميله من ملف |
2 | JButton | btnSauvegarder | يسمح بحفظ النص الموجود في 1 في ملف نصي |
3 | JButton | btnCharger | يسمح بتحميل محتوى ملف نصي في 1 |
4 | JButton | btnEffacer | يمسح محتوى 1 |
يتم استخدام عنصر تحكم غير مرئي: jFileChooser1. يتم أخذه من لوحة حاويات swing الخاصة بـ JBuilder:

نضع المكون في نافذة التصميم ولكن خارج النموذج. يظهر في قائمة المكونات:

نقدم الآن الكود المفيد للبرنامج للحصول على نظرة عامة عليه:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;
public class dialogues extends JFrame {
// مكونات الإطار
JPanel contentPane;
JButton btnSauvegarder = new JButton();
JButton btnCharger = new JButton();
JButton btnEffacer = new JButton();
JScrollPane jScrollPane1 = new JScrollPane();
JTextArea txtTexte = new JTextArea();
JFileChooser jFileChooser1 = new JFileChooser();
// مرشحات الملفات
javax.swing.filechooser.FileFilter filtreTxt = null;
//إنشاء الإطار
public dialogues() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
// عمليات التهيئة الأخرى
moreInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
// moreInit
private void moreInit(){
// عمليات التهيئة عند إنشاء النافذة
// مرشح *.txt
filtreTxt = new javax.swing.filechooser.FileFilter(){
public boolean accept(File f){
// هل نقبل f؟
return f.getName().toLowerCase().endsWith(".txt");
}//قبول
public String getDescription(){
// وصف المرشح
return "Fichiers Texte (*.txt)";
}//getDescription
};
// نضيف المرشح
jFileChooser1.addChoosableFileFilter(filtreTxt);
// نريد أيضًا مرشحًا لجميع الملفات
jFileChooser1.setAcceptAllFileFilterUsed(true);
// نحدد الدليل الأصلي للصندوق FileChooser في الدليل الحالي
jFileChooser1.setCurrentDirectory(new File("."));
}
//تهيئة المكون
private void jbInit() throws Exception {
.......................
}
//تم استبداله، وبذلك يمكننا الخروج عند إغلاق النافذة
protected void processWindowEvent(WindowEvent e) {
......................
}
}
void btnCharger_actionPerformed(ActionEvent e) {
// اختيار ملف باستخدام كائن JFileChooser
// تحديد المرشح الأولي
jFileChooser1.setFileFilter(filtreTxt);
// عرض مربع الاختيار
int returnVal = jFileChooser1.showOpenDialog(this);
// هل اختار المستخدم شيئًا ما؟
if(returnVal == JFileChooser.APPROVE_OPTION) {
// نضع الملف في حقل النص
lireFichier(jFileChooser1.getSelectedFile());
}//if
}//btnCharger_actionPerformed
// lireFichier
private void lireFichier(File fichier){
// يعرض محتوى ملف النص في حقل النص
// يتم مسح حقل النص
txtTexte.setText("");
// بعض البيانات
BufferedReader IN=null;
String ligne=null;
try{
// فتح الملف للقراءة
IN=new BufferedReader(new FileReader(fichier));
// يتم قراءة الملف سطراً سطراً
while((ligne=IN.readLine())!=null){
txtTexte.append(ligne+"\n");
}//while
// إغلاق الملف
IN.close();
}catch(Exception ex){
// حدث خطأ
txtTexte.setText(""+ex);
}//catch
}
// مسح
void btnEffacer_actionPerformed(ActionEvent e) {
// يتم مسح مربع النص
txtTexte.setText("");
}
// حفظ
void btnSauvegarder_actionPerformed(ActionEvent e) {
// حفظ محتوى مربع النص في ملف
// تعيين المرشح الأولي
jFileChooser1.setFileFilter(filtreTxt);
// يتم عرض مربع اختيار الحفظ
int returnVal = jFileChooser1.showSaveDialog(this);
// هل اختار المستخدم شيئًا ما؟
if(returnVal == JFileChooser.APPROVE_OPTION) {
// يتم كتابة محتوى مربع النص في ملف
écrireFichier(jFileChooser1.getSelectedFile());
}//if
}
// lireFichier
private void écrireFichier(File fichier){
// يكتب محتوى مربع النص في ملف
// بعض البيانات
PrintWriter PRN=null;
try{
// فتح الملف للكتابة
PRN=new PrintWriter(new FileWriter(fichier));
// يتم كتابة محتوى مربع النص
PRN.print(txtTexte.getText());
// إغلاق الملف
PRN.close();
}catch(Exception ex){
// حدث خطأ
txtTexte.setText(""+ex);
}//التقاط
}
لن نعلق على كود الطرق btnEffacer_Click و lireFichier و écrireFichier التي لا تضيف شيئًا لم نره من قبل. سنركز على الفئة JFileChooser واستخدامها. هذه الفئة معقدة، من النوع "المبهم". نستخدم هنا فقط الطرق التالية:
addChoosableFilter(FileFilter) | تحدد أنواع الملفات المعروضة للاختيار |
setAcceptAllFileFilterUsed(boolean) | تحدد ما إذا كان يجب عرض نوع "جميع الملفات" للاختيار أم لا |
File getSelectedFile() | الملف (File) الذي اختاره المستخدم |
int showSaveDialog() | طريقة تعرض مربع اختيار الحفظ. تُرجع نتيجة من النوع int. تشير القيمة jFileChooser.APPROVE_OPTION إلى أن المستخدم قد قام باختيار صحيح. وإلا، فإما أنه ألغى الاختيار، أو حدث خطأ. |
setCurrentDirectory | لتعيين الدليل الأولي الذي سيبدأ المستخدم منه استكشاف نظام الملفات |
setFileFilter(FileFilter) | لتعيين المرشح النشط |
تعرض الطريقة showSaveDialog مربع اختيار مشابه لما يلي:

1 | قائمة منسدلة تم إنشاؤها باستخدام الطريقة addChoosableFilter. وهي تحتوي على ما يُسمى بمرشحات الاختيار التي تمثلها الفئة FileFilter. يتعين على المطور تعريف هذه المرشحات وإضافتها إلى القائمة 1. |
2 | المجلد الحالي، الذي يتم تحديده بواسطة الطريقة setCurrentDirectory إذا تم استخدام هذه الطريقة، وإلا فإن المجلد الحالي سيكون مجلد "المستندات" في نظام Windows أو المجلد الرئيسي في نظام Unix. |
3 | اسم الملف الذي تم اختياره أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا باستخدام الطريقة getSelectedFile() |
4 | أزرار "حفظ"/"إلغاء". إذا تم استخدام الزر Enregistrer، فإن الطريقة showSaveDialog تُرجع النتيجة jFileChooser.APPROVE_OPTION |
كيف يتم إنشاء عوامل تصفية الملفات في القائمة المنسدلة 1؟ تتم إضافة عوامل التصفية إلى القائمة 1 باستخدام الطريقة:
addChoosableFilter(FileFilter) | تحدد أنواع الملفات المتاحة للاختيار |
من الفئة JFileChooser. يبقى لنا التعرف على الفئة FileFilter. إنها في الواقع الفئة javax.swing.filechooser.FileFilter التي هي فئة مجردة، c.a.d. وهي فئة لا يمكن إنشاء مثيل لها بل يمكن فقط اشتقاقها. وهي معرّفة على النحو التالي:
FileFilter() | منشئ |
boolean accept(File) | تشير إلى ما إذا كان الملف f ينتمي إلى المرشح أم لا |
String getDescription() | سلسلة وصف المرشح |
لنأخذ مثالاً. نريد أن توفر القائمة المنسدلة 1 مرشحاً لاختيار الملفات *.txt مع الوصف "ملفات نصية (*.txt)".
- علينا إنشاء فئة مشتقة من الفئة FileFilter
- استخدام الطريقة boolean accept(File f) لإرجاع القيمة true إذا كان اسم الملف f ينتهي بـ .txt
- استخدام الطريقة String getDescription() لإرجاع الوصف "ملفات نصية (*.txt)"
يمكن تعريف هذا المرشح في تطبيقنا بالطريقة التالية:
javax.swing.filechooser.FileFilter filtreTxt = new javax.swing.filechooser.FileFilter(){
public boolean accept(File f){
// هل نقبل f؟
return f.getName().toLowerCase().endsWith(".txt");
}//قبول
public String getDescription(){
// وصف المرشح
return "Fichiers Texte (*.txt)";
}//getDescription
};
سيتم إضافة هذا الفلتر إلى قائمة فلاتر الكائن jFileChooser1 بواسطة الأمر:
يتم تنفيذ كل هذا وبعض عمليات التهيئة الأخرى في الأسلوب moreInit الذي يتم تنفيذه عند إنشاء النافذة (انظر البرنامج الكامل أعلاه). رمز الزر Sauvegarder هو التالي:
void btnSauvegarder_actionPerformed(ActionEvent e) {
// حفظ محتوى مربع النص في ملف
// يتم تعيين المرشح الأولي
jFileChooser1.setFileFilter(filtreTxt);
// يتم عرض مربع اختيار الحفظ
int returnVal = jFileChooser1.showSaveDialog(this);
// هل اختار المستخدم شيئًا ما؟
if(returnVal == JFileChooser.APPROVE_OPTION) {
// يتم كتابة محتوى مربع النص في ملف
écrireFichier(jFileChooser1.getSelectedFile());
}//if
}
تسلسل العمليات هو كما يلي:
- يتم تعيين المرشح النشط على المرشح *.txt لتمكين المستخدم من البحث عن هذا النوع من الملفات بشكل مفضل. يتوفر أيضًا مرشح "جميع الملفات". وقد تمت إضافته في الإجراء moreInit. وبذلك يكون لدينا مرشحان.
- يتم عرض مربع اختيار الحفظ. هنا نفقد السيطرة، حيث يستخدم المستخدم مربع الاختيار لتحديد ملف من نظام الملفات.
- عندما يغادر مربع الاختيار، يتم اختبار قيمة الإرجاع لمعرفة ما إذا كان يجب حفظ مربع النص أم لا. إذا كان الجواب نعم، فيجب حفظه في الملف الذي تم الحصول عليه بواسطة الطريقة getSelectedFile.
الرمز المرتبط بالزر "تحميل" مشابه جدًا لرمز الزر "حفظ".
void btnCharger_actionPerformed(ActionEvent e) {
// اختيار ملف باستخدام كائن JFileChooser
// يتم تعيين الفلتر الأولي
jFileChooser1.setFileFilter(filtreTxt);
// عرض مربع الاختيار
int returnVal = jFileChooser1.showOpenDialog(this);
// هل اختار المستخدم شيئًا ما؟
if(returnVal == JFileChooser.APPROVE_OPTION) {
// نضع الملف في حقل النص
lireFichier(jFileChooser1.getSelectedFile());
}//if
}//btnCharger_actionPerformed
هناك اختلافان:
- لعرض مربع اختيار الملفات، يتم استخدام الطريقة showOpenDialog بدلاً من الطريقة showSaveDialog. مربع الاختيار المعروض مشابه للمربع المعروض بواسطة الطريقة showSaveDialog.
- إذا قام المستخدم بتحديد ملف، يتم استدعاء الطريقة lireFichier بدلاً من الطريقة écrireFichier.
5.4.1.1. مربعات الاختيار JColorChooser و JFontChooser
نواصل المثال السابق بإضافة زرين جديدين:

رقم | النوع | الاسم | الدور |
6 | JButton | btnCouleur | لتحديد لون أحرف TextBox |
7 | JButton | btnPolice | لتحديد خط الأحرف في TextBox |
يمكن العثور على المكون JColorChooser الذي يسمح بعرض مربع اختيار اللون في قائمة مكونات swing الخاصة بـ JBuilder:

نضع هذا المكون في نافذة التصميم، ولكن خارج النموذج. وهو غير مرئي في النموذج ولكنه موجود مع ذلك في قائمة مكونات النافذة:

الفئة JColorChooser بسيطة للغاية. نعرض مربع اختيار الألوان باستخدام الطريقة int showDialog:
static Color | showDialog(Component component, String title, Color initialColor) |
Component component | المكون الأصلي لمربع الاختيار، وعادةً ما يكون نافذة JFrame |
String title | العنوان في شريط العنوان لمربع الاختيار |
اللون intialColor | اللون المحدد مسبقًا في مربع الاختيار |
إذا اختار المستخدم لونًا، فإن الطريقة تُرجع لونًا، وإلا تُرجع القيمة null. الرمز المرتبط بالزر Couleur هو التالي:
void btnCouleur_actionPerformed(ActionEvent e) {
// اختيار لون النص باستخدام المكون JColorChooser
Color couleur;
if((couleur=jColorChooser1.showDialog(this,"Choix d'une couleur",Color.BLACK))!=null){
// تحديد لون أحرف مربع النص
txtTexte.setForeground(couleur);
}//if
}
يتم تعريف الفئة Color في java.awt.Color. يتم تعريف ثوابت متنوعة فيها، بما في ذلك Color.BLACK للون الأسود. مربع الاختيار المعروض هو التالي

من الغريب أن مكتبة Swing لا توفر فئة لاختيار خط. لحسن الحظ، هناك موارد Java على الإنترنت. عند البحث عن الكلمة الرئيسية "JFontChooser" التي تبدو اسمًا محتملاً لمثل هذه الفئة، نجد العديد منها. سيتيح لنا المثال التالي فرصة تكوين JBuilder لاستخدام حزم غير متوفرة في تكوينه الأولي.
يُسمى الحزمة التي تم استردادها swingextras.jar وتم وضعها في المجلد <jdk>\jre\lib\perso حيث يشير <jdk> إلى دليل تثبيت jdk المستخدم بواسطة JBuilder. كان من الممكن وضعه في أي مكان آخر.
![]() | ![]() |
لنلقِ نظرة على محتوى الحزمة SwingExtras.jar:

يوجد فيه كود جافا المصدري للمكونات المتوفرة في الحزمة. كما توجد فيه الفئات نفسها:

من هذا الأرشيف، نلاحظ أن الفئة JFontChooser تسمى في الواقع com.lamatek.swingextras.JFontChooser. إذا لم نرغب في كتابة الاسم الكامل، فسيتعين علينا كتابة ما يلي في بداية البرنامج:
كيف سيقوم المُجمِّع والآلة الافتراضية لـ Java بالعثور على هذه الحزمة الجديدة؟ في حالة الاستخدام المباشر لـ JDK، فقد تم شرح ذلك بالفعل ويمكن للقارئ العثور على التفسيرات في الفقرة الخاصة بحزم Java. نوضح هنا الطريقة التي يجب استخدامها مع JBuilder. يتم تكوين الحزم في القائمة "خيارات/تكوين JDK":

1 | يتيح الزر Modifier (1) إخبار JBuilder بالحزمة JDK المستخدمة. في هذا المثال، كان JBuilder قد جلب معه JDK 1.3.1. عندما تم إصدار JDK 1.4، تم تثبيته بشكل منفصل عن JBuilder واستخدمنا زر Modifier لإعلام JBuilderيستخدم من الآن فصاعدًا الإصدار JDK 1.4 من خلال تحديد مجلد تثبيت هذا الأخير |
2 | المجلد الأساسي لـ JDK الذي يستخدمه حاليًا JBuilder |
3 | قائمة أرشيفات جافا (.jar) المستخدمة بواسطة JBuilder. يمكن إضافة أرشيفات أخرى باستخدام الزر Ajouter (4) |
4 | يتيح الزر Ajouter (4) إضافة حزم جديدة سيستخدمها JBuilder في كل من تجميع البرامج وتشغيلها. وقد استخدمناه هنا لإضافة الحزمة SwingExtras.jar (5) |
الآن تم تكوين JBuilder بشكل صحيح ليتمكن من استخدام الفئة JFontChooser. ومع ذلك، سنحتاج إلى الوصول إلى تعريف هذه الفئة لاستخدامها بشكل صحيح. يحتوي الأرشيف swingextras.jar على ملفات html يمكن استخراجها لاستخدامها. هذا غير ضروري. يمكن الوصول مباشرةً إلى وثائق Java المضمنة في ملفات .jar من JBuilder. ولهذا الغرض، يجب تكوين علامة التبويب Documentation (6) أعلاه. تظهر لنا النافذة الجديدة التالية:

يتيح لنا الزر Ajouter تحديد أن الملف SwingExtras.jar يجب استكشافه بحثًا عن الوثائق. بعد تأكيد هذه الخطوة، يمكننا ملاحظة أننا نتمتع بالفعل بإمكانية الوصول إلى وثائق SwingExtras.jar. وينتج عن ذلك العديد من المزايا:
- إذا بدأنا في كتابة تعليمة الاستيراد
سنلاحظ أن JBuilder يقدم لنا المساعدة:

تم العثور على الحزمة com.lamatek.
- إذا انتقلنا الآن إلى البرنامج التالي:
عند استخدام F1 على الكلمة الرئيسية JFontChooser، نحصل على مساعدة بشأن هذه الفئة:

نرى في شريط الحالة 1 أنه يتم بالفعل استخدام ملف html من الحزمة swingextras.jar. المثال المقدم أعلاه واضح بما يكفي لكتابة كود الزر Police في تطبيقنا:
void btnPolice_actionPerformed(ActionEvent e) {
// اختيار خط النص باستخدام المكون JFontChooser
jFontChooser1 = new JFontChooser(new Font("Arial", Font.BOLD, 12));
if (jFontChooser1.showDialog(this, "Choix d'une police") == JFontChooser.ACCEPT_OPTION) {
// تغيير خط أحرف مربع النص
txtTexte.setFont(jFontChooser1.getSelectedFont());
}//if
}
مربع الاختيار المعروض بواسطة الطريقة showDialog أعلاه هو التالي:

5.5. التطبيق الرسومي IMPOTS
نعود إلى التطبيق IMPOTS الذي تمت معالجته مرتين بالفعل. نضيف إليه الآن واجهة رسومية:

العناصر التالية
رقم | النوع | الاسم | الدور |
1 | JRadioButton | rdOui | ضع علامة إذا كنت متزوجًا |
2 | JRadioButton | rdNon | ضع علامة إذا لم تكن متزوجًا |
3 | JSpinner | spinEnfants | عدد أطفال المكلف الحد الأدنى=0، الحد الأقصى=20، الزيادة=1 |
4 | JTextField | txtSalaire | الراتب السنوي للمكلف بالضريبة بالفرنك الفرنسي |
5 | JLabel | lblImpots | مبلغ الضريبة المستحقة |
6 | JTextField | txtStatus | حقل رسائل الحالة - غير قابل للتعديل |
القائمة هي كما يلي:
الخيار الرئيسي | الخيار الثانوي | الاسم | الدور |
الضرائب | |||
التهيئة | mnuInitialiser | تحميل البيانات اللازمة للحساب من ملف نصي | |
حساب | mnuCalculer | يحسب الضريبة المستحقة عندما تكون جميع البيانات اللازمة موجودة وصحيحة | |
مسح | mnuEffacer | يعيد النموذج إلى حالته الأصلية | |
خروج | mnuQuitter | ينهي التطبيق |
قواعد التشغيل
- يظل القائمة Calculer مغلقًا طالما لم يتم إدخال أي قيمة في حقل الراتب
- إذا تبين عند بدء الحساب أن الراتب غير صحيح، يتم الإبلاغ عن الخطأ:

البرنامج موضح أدناه. يستخدم البرنامج الفئة impots التي تم إنشاؤها في الفصل الخاص بالفئات. لم يتم هنا إعادة إنتاج جزء من الكود الذي تم إنتاجه تلقائيًا بواسطة JBuilder.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;
import java.util.*;
import javax.swing.event.*;
public class frmImpots extends JFrame {
// مكونات النافذة
JPanel contentPane;
JMenuBar jMenuBar1 = new JMenuBar();
JMenu jMenu1 = new JMenu();
JMenuItem mnuInitialiser = new JMenuItem();
JMenuItem mnuCalculer = new JMenuItem();
JMenuItem mnuEffacer = new JMenuItem();
JMenuItem mnuQuitter = new JMenuItem();
JLabel jLabel1 = new JLabel();
JFileChooser jFileChooser1 = new JFileChooser();
JTextField txtStatus = new JTextField();
JRadioButton rdOui = new JRadioButton();
JRadioButton rdNon = new JRadioButton();
JLabel jLabel2 = new JLabel();
JLabel jLabel3 = new JLabel();
JTextField txtSalaire = new JTextField();
JLabel jLabel4 = new JLabel();
JLabel jLabel5 = new JLabel();
JLabel lblImpots = new JLabel();
JLabel jLabel7 = new JLabel();
ButtonGroup buttonGroup1 = new ButtonGroup();
JSpinner spinEnfants=null;
FileFilter filtreTxt=null;
// سمات الفئة
double[] limites=null;
double[] coeffr=null;
double[] coeffn=null;
impots objImpots=null;
//إنشاء الإطار
public frmImpots() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
// عمليات تهيئة أخرى
moreInit();
}
// تهيئة النموذج
private void moreInit(){
// قائمة "حساب" معطلة
mnuCalculer.setEnabled(false);
// مجالات الإدخال معطلة
txtSalaire.setEditable(false);
txtSalaire.setBackground(Color.WHITE);
// حقل الحالة
txtStatus.setBackground(Color.WHITE);
// مؤشر الدوران "الأطفال" - بين 0 و20 طفلاً
spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
spinEnfants.setBounds(new Rectangle(137,60,40,27));
contentPane.add(spinEnfants);
// مرشح *.txt لمربع الاختيار
filtreTxt = new javax.swing.filechooser.FileFilter(){
public boolean accept(File f){
// هل يُقبل f؟
return f.getName().toLowerCase().endsWith(".txt");
}//قبول
public String getDescription(){
// وصف المرشح
return "Fichiers Texte (*.txt)";
}//getDescription
};
// نضيف المرشح *.txt
jFileChooser1.addChoosableFileFilter(filtreTxt);
// نريد أيضًا مرشحًا لجميع الملفات
jFileChooser1.setAcceptAllFileFilterUsed(true);
// يتم تحديد دليل البداية للصندوق FileChooser
jFileChooser1.setCurrentDirectory(new File("."));
}//moreInit
//تهيئة المكون
private void jbInit() throws Exception {
................
}
//تم الاستبدال، وبالتالي يمكننا الخروج عند إغلاق النافذة
protected void processWindowEvent(WindowEvent e) {
.....................
}
void mnuQuitter_actionPerformed(ActionEvent e) {
// نغادر التطبيق
System.exit(0);
}
void mnuInitialiser_actionPerformed(ActionEvent e) {
// تحميل ملف البيانات
// الذي يتم تحديده باستخدام مربع الاختيار JFileChooser1
jFileChooser1.setFileFilter(filtreTxt);
if (jFileChooser1.showOpenDialog(this)!=JFileChooser.APPROVE_OPTION)
return;
// يتم استغلال الملف المحدد
try{
// قراءة البيانات
lireFichier(jFileChooser1.getSelectedFile());
// إنشاء كائن الضرائب
objImpots=new impots(limites,coeffr,coeffn);
// التأكيد
txtStatus.setText("Données chargées");
// يمكن تعديل الراتب
txtSalaire.setEditable(true);
// لا يمكن إجراء المزيد من التغييرات
mnuInitialiser.setEnabled(false);
}catch(Exception ex){
// مشكلة
txtStatus.setText("Erreur : " + ex.getMessage());
// نهاية
return;
}//اكتشاف
}
private void lireFichier(File fichier) throws Exception {
// جداول البيانات
ArrayList aLimites=new ArrayList();
ArrayList aCoeffR=new ArrayList();
ArrayList aCoeffN=new ArrayList();
String[] champs=null;
// فتح الملف للقراءة
BufferedReader IN=new BufferedReader(new FileReader(fichier));
// يتم قراءة الملف سطراً سطراً
// وهي على شكل الحد coeffr coeffn
String ligne=null;
int numLigne=0; // رقم السطر الحالي
while((ligne=IN.readLine())!=null){
// سطر إضافي
numLigne++;
// يتم تقسيم السطر إلى حقول
champs=ligne.split("\\s+");
// 3 حقول؟
if(champs.length!=3)
throw new Exception("ligne " + numLigne + "erronée dans fichier des données");
// نسترد الحقول الثلاثة
aLimites.add(champs[0]);
aCoeffR.add(champs[1]);
aCoeffN.add(champs[2]);
}//while
// إغلاق الملف
IN.close();
// نقل البيانات إلى جداول محدودة
int n=aLimites.size();
limites=new double[n];
coeffr=new double[n];
coeffn=new double[n];
for(int i=0;i<n;i++){
limites[i]=Double.parseDouble((String)aLimites.get(i));
coeffr[i]=Double.parseDouble((String)aCoeffR.get(i));
coeffn[i]=Double.parseDouble((String)aCoeffN.get(i));
}//for
}
void mnuCalculer_actionPerformed(ActionEvent e) {
// حساب الضريبة
// التحقق من الراتب
int salaire=0;
try{
salaire=Integer.parseInt(txtSalaire.getText().trim());
if(salaire<0) throw new Exception();
}catch (Exception ex){
// رسالة خطأ
txtStatus.setText("Salaire incorrect. Recommencez");
// التركيز على الحقل الخاطئ
txtSalaire.requestFocus();
// العودة إلى الواجهة
return;
}
// عدد الأطفال
Integer InbEnfants=(Integer)spinEnfants.getValue();
// حساب الضريبة
lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),InbEnfants.intValue(),salaire));
// مسح رسالة الحالة
txtStatus.setText("");
}
void txtSalaire_caretUpdate(CaretEvent e) {
// تغير الراتب - يتم تحديث قائمة الحساب
mnuCalculer.setEnabled(! txtSalaire.getText().trim().equals(""));
}
void mnuEffacer_actionPerformed(ActionEvent e) {
// إعادة تعيين النموذج
rdNon.setSelected(true);
spinEnfants.getModel().setValue(new Integer(0));
txtSalaire.setText("");
mnuCalculer.setEnabled(false);
lblImpots.setText("");
}
}
استخدمنا هنا مكونًا متاحًا فقط بدءًا من الإصدار 1.4 من JDK، وهو JSpinner. وهو عبارة عن عداد تصاعدي، يتيح للمستخدم هنا تحديد عدد العناصر الفرعية. لم يكن هذا المكون Swing متاحًا في شريط مكونات Swing الخاص بـ JBuilder 6 المستخدم في الاختبار. لكن هذا لا يمنع استخدامه، حتى وإن كانت الأمور أكثر تعقيدًا قليلاً مقارنة بالمكونات المتوفرة في شريط المكونات. في الواقع، لا يمكن وضع المكون JSpinner على النموذج أثناء التصميم. يجب القيام بذلك أثناء التنفيذ. لنلقِ نظرة على الكود الذي يقوم بذلك:
// مؤشر الدوران للأطفال - بين 0 و 20 طفلاً
spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
spinEnfants.setBounds(new Rectangle(137,60,40,27));
contentPane.add(spinEnfants);
السطر الأول ينشئ المكون JSpinner. يمكن استخدام هذا المكون لأغراض متنوعة وليس فقط كمُزيد للأعداد الصحيحة كما هو الحال هنا. الحجة الخاصة بالمُنشئ JSpinner هي هنا نموذج لمرقم الأرقام الذي يقبل أربعة معلمات (القيمة، الحد الأدنى، الحد الأقصى، الزيادة). المكون له الشكل التالي:
![]()
valeur | القيمة الأولية المعروضة في المكون |
min | القيمة الدنيا التي يمكن عرضها في المكون |
max | القيمة القصوى التي يمكن عرضها في المكون |
incrément | قيمة الزيادة في القيمة المعروضة عند استخدام أسهم الأعلى/الأسفل للمكون |
يتم الحصول على قيمة المكون عبر طريقة getValue التي تُرجع نوع Object. ومن هنا تأتي بعض عمليات التحويل للحصول على العدد الصحيح المطلوب:
// عدد الأطفال
Integer InbEnfants=(Integer)spinEnfants.getValue();
// حساب الضريبة
lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),
InbEnfants.intValue(),salaire));
بمجرد تعريف المكون JSpinner، يتم وضعه في النافذة:
قبل أي شيء، يجب على المستخدم استخدام خيار القائمة Initialiser الذي ينشئ كائن impots باستخدام المنشئ
لفئة impots. نذكر أن هذه الفئة تم تعريفها كمثال في فصل الفئات. يمكن الرجوع إليه إذا لزم الأمر. يتم ملء الجداول الثلاثة اللازمة للمنشئ من محتوى ملف نصي بالصيغة التالية:
12620.0 | 0 | 0 |
13190 | 0.05 | 631 |
15640 | 0.1 | 1290.5 |
24740 | 0.15 | 2072.5 |
31810 | 0.2 | 3309.5 |
39970 | 0.25 | 4900 |
48360 | 0.3 | 6898.5 |
55790 | 0.35 | 9316.5 |
92970 | 0.4 | 12106 |
127860 | 0.45 | 16754.5 |
151250 | 0.50 | 23147.5 |
172040 | 0.55 | 30710 |
195000 | 0.60 | 39312 |
0 | 0.65 | 49062 |
تتضمن كل سطر ثلاثة أرقام مفصولة بمسافة واحدة على الأقل. يتم اختيار هذا الملف النصي من قبل المستخدم باستخدام مكون JFileChooser. بمجرد إنشاء الكائن من النوع impots، لا يتبقى سوى السماح للمستخدم بإدخال المعلومات الثلاثة المطلوبة: متزوج أم لا، عدد الأطفال، الراتب السنوي، ثم استدعاء الطريقة calculer من الفئة impots. يمكن تكرار العملية عدة مرات. أما الكائن من النوع impots فيتم إنشاؤه مرة واحدة فقط، عند استخدام الخيار Initialiser.
5.6. كتابة التطبيقات الصغيرة
5.6.1. مقدمة
عند كتابة تطبيق بواجهة رسومية، من السهل تحويله إلى applet. عند تخزينه على جهاز A، يمكن تنزيله بواسطة متصفح ويب من جهاز B عبر الإنترنت. وبذلك يتم مشاركة التطبيق الأصلي بين العديد من المستخدمين، وهذا هو الهدف الرئيسي من تحويل التطبيق إلى تطبيق صغير. ومع ذلك، لا يمكن تحويل أي تطبيق بهذه الطريقة: حتى لا يضر المستخدم الذي يستخدم تطبيقًا صغيرًا في متصفحه، فإن بيئة التطبيق الصغير خاضعة للوائح:
- لا يمكن لبرنامج applet القراءة أو الكتابة على قرص المستخدم
- ولا يمكنها التواصل إلا مع الجهاز الذي تم تنزيلها منه بواسطة المتصفح.
هذه قيود صارمة. فهي تعني، على سبيل المثال، أن أي تطبيق يحتاج إلى قراءة معلومات من ملف أو قاعدة بيانات سيتعين عليه طلبها عبر تطبيق وسيط موجود على الخادم الذي تم تنزيله منه.
الهيكل العام لتطبيق ويب بسيط هو كما يلي:

- يطلب العميل مستندًا HTML من خادم الويب، عادةً باستخدام متصفح. قد يحتوي هذا المستند على تطبيق صغير يعمل كتطبيق رسومي مستقل داخل المستند HTML المعروض بواسطة متصفح العميل.
- يمكن أن يكون لهذا التطبيق الصغير حق الوصول إلى البيانات، ولكن فقط تلك الموجودة على خادم الويب. ولن يكون له حق الوصول إلى موارد الجهاز العميل الذي يقوم بتشغيله، ولا إلى موارد الأجهزة الأخرى على الشبكة بخلاف الجهاز الذي تم تنزيله منه.
5.6.2. الفئة JApplet
5.6.2.1. Définition
يمكن تنزيل تطبيق عبر متصفح الويب إذا كان مثيلًا للفئة java.applet.Applet أو الفئة javax.swing.JApplet. وتشتق هذه الأخيرة من الأولى التي تشتق بدورها من الفئة Panel التي تشتق بدورها من الفئة Container. وبما أن مثيل Applet أو JApplet هو من النوع container، فإنه يمكن أن يحتوي على مكونات (Component) مثل الأزرار، ومربعات الاختيار، والقوائم، ... فيما يلي بعض الإرشادات حول دور الطرق المختلفة:
الطريقة | الدور |
public void destroy(); | تدمر مثيل Applet |
public AppletContext getAppletContext(); | يسترد سياق تشغيل التطبيق الصغير (المستند HTML الذي يوجد فيه، والتطبيقات الصغيرة الأخرى في نفس المستند، ...) |
public String getAppletInfo(); | تُرجع سلسلة أحرف تعطي معلومات عن التطبيق الصغير |
public AudioClip getAudioClip(URL url); | يقوم بتحميل الملف الصوتي المحدد بواسطة URL |
public AudioClip getAudioClip(URL url, String name); | تحميل الملف الصوتي المحدد بواسطة URL/name |
public URL getCodeBase(); | يعرض URL من التطبيق الصغير |
public URL getDocumentBase(); | يعرض URL من المستند HTML الذي يحتوي على التطبيق الصغير |
public Image getImage(URL url); | يسترد الصورة المحددة بواسطة URL |
public Image getImage(URL url, String name); | يسترد الصورة المحددة بواسطة URL/name |
public String getParameter(String name); | يسترد قيمة المعلمة name الموجودة في العلامة <applet> في المستند HTML الذي يحتوي على التطبيق الصغير. |
public void init(); | يتم تشغيل هذه الطريقة بواسطة المتصفح عند التشغيل الأولي للتطبيق الصغير |
public boolean isActive(); | حالة التطبيق الصغير |
public void play(URL url); | تشغيل ملف الصوت المحدد بواسطة URL |
public void play(URL url, String name); | تشغيل ملف الصوت المحدد بواسطة UR/name |
public void resize(Dimension d); | يحدد أبعاد إطار التطبيق الصغير |
public void resize(int width, int height); | نفس الشيء |
public final void setStub(AppletStub stub); | |
public void showStatus(String msg); | يضع رسالة في شريط الحالة الخاص بالتطبيق الصغير |
public void start(); | يتم تشغيل هذه الطريقة بواسطة المتصفح عند كل عرض للوثيقة التي تحتوي على التطبيق الصغير |
public void stop(); | يتم تشغيل هذه الطريقة بواسطة المتصفح في كل مرة يتم فيها التخلي عن المستند الذي يحتوي على التطبيق الصغير لصالح مستند آخر (تغيير URL بواسطة المستخدم) |
أدخلت الفئة JApplet بعض التحسينات على الفئة Applet، ولا سيما القدرة على احتواء مكونات JMenuBar وc.a.d. القوائم، وهو ما لم يكن ممكنًا مع الفئة Applet.
5.6.2.2. تشغيل تطبيق صغير: الطرق init و start و stop
عندما يقوم متصفح بتحميل تطبيق صغير، فإنه يستدعي ثلاث طرق منه:
init | يتم استدعاء هذه الطريقة عند التحميل الأولي للتطبيق الصغير. لذلك، سنضع فيها التهيئات اللازمة للتطبيق. |
start | يتم استدعاء هذه الطريقة في كل مرة يصبح فيها المستند الذي يحتوي على التطبيق الصغير هو المستند الحالي للمتصفح. وبالتالي، عندما يقوم المستخدم بتحميل تطبيق صغير، يتم تنفيذ طريقتي init و start بهذا الترتيب. وعندما يغادر المستخدم المستند لعرض مستند آخر، يتم تنفيذ طريقة stop. وعندما يعود إليه لاحقًا، يتم تنفيذ طريقة start. |
stop | يتم استدعاء هذه الطريقة في كل مرة يغادر فيها المستخدم المستند الذي يحتوي على التطبيق الصغير. |
بالنسبة للعديد من التطبيقات الصغيرة، لا يلزم سوى استخدام طريقة init. أما طريقتا start و stop فلا تكونان ضروريتين إلا إذا كان التطبيق يشغل مهام (خيوط) تعمل بشكل متوازٍ ومستمر، غالبًا دون علم المستخدم. وعندما يغادر المستخدم المستند، يختفي الجزء المرئي من التطبيق، لكن هذه المهام التي تعمل في الخلفية تستمر في العمل. وغالبًا ما يكون ذلك غير ضروري. لذلك، نستفيد من استدعاء المتصفح لطريقة stop لإيقافها. إذا عاد المستخدم إلى المستند، نستفيد من استدعاء المتصفح لطريقة start لإعادة تشغيلها.
لنأخذ على سبيل المثال تطبيق صغير يحتوي على ساعة في واجهته الرسومية. يتم الحفاظ على هذه الساعة بواسطة مهمة خلفية (خيط). عندما يغادر المستخدم صفحة التطبيق الصغير في المتصفح، لا فائدة من الحفاظ على الساعة التي أصبحت غير مرئية: في طريقة stop الخاصة بالتطبيق الصغير، سنوقف الخيط الذي يدير الساعة. وفي طريقة start، سنعيد تشغيله حتى عندما يعود المستخدم إلى صفحة التطبيق الصغير، يجد ساعة تعمل بالوقت الصحيح.
5.6.3. تحويل تطبيق رسومي إلى تطبيق صغير
نفترض هنا أن هذا التحويل ممكن، أي أنه يتوافق مع قيود تشغيل التطبيقات الصغيرة. يتم تشغيل التطبيق بواسطة الطريقة main لإحدى فئاته:
يتم تحويل مثل هذا التطبيق إلى تطبيق صغير بالطريقة التالية:
import java.applet.JApplet;
…
public class application extends JApplet{
…
public void init(){
…
}
…
}// نهاية الفصل
تجدر الإشارة إلى النقاط التالية:
- أصبحت الفئة application مشتقة الآن من الفئة JApplet
- تم استبدال الطريقة main بالطريقة init.
على سبيل المثال، نعود إلى أحد التطبيقات التي تمت دراستها: إدارة القوائم.

مكونات هذه النافذة هي التالية:
رقم | النوع | الاسم | الدور |
1 | JTextField | txtSaisie | حقل الإدخال |
2 | JList | jList1 | قائمة موجودة في حاوية jScrollPane1 |
3 | JList | jList2 | قائمة موجودة في حاوية jScrollPane2 |
4 | JButton | cmd1To2 | ينقل العناصر المحددة من القائمة 1 إلى القائمة 2 |
5 | JButton | cmd2To1 | يقوم بالعكس |
6 | JButton | cmdRaz1 | أفرغ القائمة 1 |
7 | JButton | cmdRaz2 | إفراغ القائمة 2 |
كان الهيكل الأساسي للتطبيق كما يلي:
public class interfaceAppli extends JFrame {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
JLabel jLabel2 = new JLabel();
JLabel jLabel3 = new JLabel();
JTextField txtSaisie = new JTextField();
JButton cmd1To2 = new JButton();
JButton cmd2To1 = new JButton();
DefaultListModel v1=new DefaultListModel();
DefaultListModel v2=new DefaultListModel();
JList jList1 = new JList(v1);
JList jList2 = new JList(v2);
JScrollPane jScrollPane1 = new JScrollPane();
JScrollPane jScrollPane2 = new JScrollPane();
JButton cmdRaz1 = new JButton();
JButton cmdRaz2 = new JButton();
JLabel jLabel4 = new JLabel();
/**إنشاء الإطار*/
public interfaceAppli() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}//interfaceAppli
/**تهيئة المكون*/
private void jbInit() throws Exception {
...
txtSaisie.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
txtSaisie_actionPerformed(e);
}
});
...
// يتم وضع Jlist1 في الحاوية jScrollPane1
jScrollPane1.getViewport().add(jList1, null);
// يتم وضع Jlist2 في الحاوية jScrollPane2
jScrollPane2.getViewport().add(jList2, null);
...
}
/**تم استبداله، وبذلك يمكننا الخروج عند إغلاق النافذة*/
protected void processWindowEvent(WindowEvent e) {
...
}
void txtSaisie_actionPerformed(ActionEvent e) {
.............
}//فئة
void cmdRaz1_actionPerformed(ActionEvent e) {
// إفراغ القائمة 1
v1.removeAllElements();
}//cmd Raz1
void cmdRaz2_actionPerformed(ActionEvent e) {
// إفراغ القائمة 2
v2.removeAllElements();
}///cmd Raz2
لتحويل الفئة appli إلى applet، يجب اتباع الخطوات التالية:
- تعديل الفئة interfaceAppli السابقة بحيث لا تستمد من JFrame بل من JApplet:
- حذف التعليمات التي تحدد عنوان النافذة JFrame. لا تحتوي التطبيقات الصغيرة JApplet على شريط العنوان
- تغيير المنشئ إلى طريقة init وفي هذه الطريقة حذف إدارة أحداث النافذة (WindowListener، ...). التطبيق الصغير هو حاوية لا يمكن تغيير حجمها أو إغلاقها.
/**بناء الإطار*/
public init() {
// enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}//interfaceAppli
- يجب حذف الطريقة processWindowEvent أو تعليقها
/**تم استبداله، حتى نتمكن من الخروج عندما تكون النافذة مغلقة*/
//protected void processWindowEvent(WindowEvent e) {
// super.processWindowEvent(e);
// if (e.getID() == WindowEvent.WINDOW_CLOSING) {
// System.exit(0);
// }
// }
على سبيل المثال، نقدم الكود الكامل للبرنامج الصغير باستثناء الجزء الذي تم إنشاؤه تلقائيًا بواسطة JBuilder
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class interfaceAppli extends JApplet {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
JLabel jLabel2 = new JLabel();
JLabel jLabel3 = new JLabel();
JTextField txtSaisie = new JTextField();
JButton cmd1To2 = new JButton();
JButton cmd2To1 = new JButton();
DefaultListModel v1=new DefaultListModel();
DefaultListModel v2=new DefaultListModel();
JList jList1 = new JList(v1);
JList jList2 = new JList(v2);
JScrollPane jScrollPane1 = new JScrollPane();
JScrollPane jScrollPane2 = new JScrollPane();
JButton cmdRaz1 = new JButton();
JButton cmdRaz2 = new JButton();
JLabel jLabel4 = new JLabel();
/**بناء الإطار*/
public void init() {
// enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}//interfaceAppli
/**تهيئة المكون*/
private void jbInit() throws Exception {
//setIconImage(Toolkit.getDefaultToolkit().createImage(interfaceAppli.class.getResource("[Votre icône]")));
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(null);
this.setSize(new Dimension(400, 308));
// this.setTitle("JList");
jScrollPane1.setBounds(new Rectangle(37, 133, 124, 101));
jScrollPane2.setBounds(new Rectangle(232, 132, 124, 101));
.............
}
void txtSaisie_actionPerformed(ActionEvent e) {
// تم التحقق من صحة النص الذي تم إدخاله
// يتم استرداده بعد إزالة المسافات في البداية والنهاية
String texte=txtSaisie.getText().trim();
// إذا كان فارغًا، فلا نريده
if(texte.equals("")){
// رسالة خطأ
JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
"Erreur",JOptionPane.WARNING_MESSAGE);
// النهاية
return;
}//if
// إذا لم يكن فارغًا، نضيفه إلى قيم القائمة 1
v1.addElement(texte);
// ويتم إفراغ حقل الإدخال
txtSaisie.setText("");
}
void cmd1To2_actionPerformed(ActionEvent e) {
// نقل العناصر المحددة في القائمة 1 إلى القائمة 2
transfert(jList1,jList2);
}//cmd1To2
private void transfert(JList L1, JList L2){
// نقل العناصر المحددة في القائمة 1 إلى القائمة 2
// نسترد جدول مؤشرات العناصر المحددة في L1
int[] indices=L1.getSelectedIndices();
// هل هناك شيء يجب فعله؟
if (indices.length==0) return;
// استرداد قيم L1
DefaultListModel v1=(DefaultListModel)L1.getModel();
// وتلك الموجودة في L2
DefaultListModel v2=(DefaultListModel)L2.getModel();
for(int i=indices.length-1;i>=0;i--){
// نضيف إلى L2 القيم المحددة في L1
v2.addElement(v1.elementAt(indices[i]));
// يجب حذف عناصر L1 التي تم نسخها إلى L2 من L1
v1.removeElementAt(indices[i]);
}//لـ
}//النقل
private void affiche(String message){
// عرض رسالة
JOptionPane.showMessageDialog(this,message,
"Suivi",JOptionPane.INFORMATION_MESSAGE);
}// يعرض
void cmd2To1_actionPerformed(ActionEvent e) {
// نقل العناصر المحددة في jList2 إلى jList1
transfert(jList2,jList1);
}//cmd2TO1
void cmdRaz1_actionPerformed(ActionEvent e) {
// إفراغ القائمة 1
v1.removeAllElements();
}//الأمر Raz1
void cmdRaz2_actionPerformed(ActionEvent e) {
// قائمة فارغة 2
v2.removeAllElements();
}//cmd Raz2
}//تصنيف
يمكن تجميع مصدر هذا التطبيق الصغير. نقوم بذلك هنا باستخدام JDK، بدون JBuilder.
E:\data\serge\Jbuilder\interfaces\JApplets\1>javac.bat interfaceAppli.java
E:\data\serge\Jbuilder\interfaces\JApplets\1>dir
12/06/2002 16:40 6 148 interfaceAppli.java
12/06/2002 16:41 527 interfaceAppli$1.class
12/06/2002 16:41 525 interfaceAppli$2.class
12/06/2002 16:41 525 interfaceAppli$3.class
12/06/2002 16:41 525 interfaceAppli$4.class
12/06/2002 16:41 525 interfaceAppli$5.class
12/06/2002 16:41 4 759 interfaceAppli.class
يمكن اختبار التطبيق باستخدام البرنامج AppletViewer من JDK الذي يسمح بتشغيل التطبيقات الصغيرة أو متصفح. للقيام بذلك، يجب إنشاء المستند HTML appli.htm الذي سيحتوي على التطبيق الصغير:
<html>
<head>
<title>listes swing</title>
</head>
<body>
<applet
code="interfaceAppli.class"
width="400"
height="300">
</applet>
</body>
</html>
لدينا هنا مستند HTML كلاسيكي باستثناء وجود علامة applet. وقد تم استخدام هذه العلامة مع ثلاثة معلمات:
code="interfaceAppli.class" | اسم الفئة JAVA المُجمَّعة المطلوب تحميلها لتنفيذ التطبيق الصغير |
width="400" | عرض إطار التطبيق الصغير في المستند |
height="300" | ارتفاع إطار التطبيق الصغير في المستند |
عندما يتم كتابة الملف appli.htm، يمكن تحميله باستخدام البرنامج appletviewer من JDK أو باستخدام متصفح. نكتب في نافذة DOS الأمر التالي في مجلد الملف appli.htm:
appletviewer appli.htm
نفترض هنا أن دليل الملف القابل للتنفيذ appletviewer.exe موجود في PATH في نافذة DOS، وإلا فسيكون من الضروري تحديد المسار الكامل للملف القابل للتنفيذ appletviewer.exe. ستظهر النافذة التالية:

نوقف تشغيل التطبيق الصغير باستخدام الخيار Applet/Quitter. لنختبر الآن التطبيق الصغير باستخدام متصفح. يجب أن يستخدم المتصفح آلة افتراضية Java 2 ليتمكن من عرض مكونات Swing. حتى وقت قريب (2001)، كان هذا القيد يمثل مشكلة لأن Sun كانت تنتج JDK التي لم يتبعها مطورو المتصفحات إلا بعد تأخير كبير. أخيرًا، تخلصت Sun من هذا العائق من خلال توفير تطبيق للمستخدمين يسمى "Java plugin" والذي يسمح لمتصفحي Internet Explorer و Netscape Navigator باستخدام أحدث إصدارات JRE التي تنتجها Sun (JRE=Java Runtime Environment). أجريت الاختبارات التالية باستخدام IE 5.5 المزود بمكوّن Java الإضافي 1.4.
تتمثل الطريقة الأولى لاختبار التطبيق الصغير interfaceAppli.class في تحميل الملف HTML appli.htm مباشرة في المتصفح بالنقر المزدوج عليه. يعرض المتصفح عندئذٍ الصفحة HTML والتطبيق الصغير الخاص بها دون الحاجة إلى خادم ويب:

يمكننا أن نرى من خلال URL (1) أن الملف تم تحميله في المتصفح مباشرة. في المثال التالي، تم طلب الملف appli.htm من خادم ويب Apache يعمل على المنفذ 81 للجهاز المحلي:

5.6.4. خيار التنسيق <applet> في مستند HTML
لقد رأينا أنه في المستند HTML، تمت الإشارة إلى التطبيق الصغير من خلال علامة التنسيق (<applet>). ويمكن أن تحتوي هذه العلامة على معلمات متنوعة:
<applet
code=
width=
height=
codebase=
align=
hspace=
vspace=
alt=
>
<param name1=nom1 value=val1>
<param name2=nom2 value=val2>
texte
>
</applet>
معنى المعلمات هو كما يلي:
المعلمة | المعنى |
code | إلزامي - اسم ملف .class المراد تنفيذه |
width | إلزامي - عرض التطبيق الصغير في المستند |
height | إلزامي - الارتفاع … |
codebase | اختياري - URL الدليل الذي يحتوي على التطبيق الصغير المراد تنفيذه. إذا لم يتم تحديد codebase، فسيتم البحث عن التطبيق الصغير في دليل المستند html الذي يشير إليه |
align | اختياري - موضع (LEFT، RiGHT، CENTER) التطبيق الصغير في المستند |
hspace | اختياري - الهامش الأفقي المحيط بالتطبيق الصغير معبراً عنه بالبكسل |
vspace | اختياري - الهامش الرأسي المحيط بالتطبيق الصغير معبراً عنه بالبكسل |
alt | اختياري - نص يظهر بدلاً من التطبيق الصغير إذا تعذر على المتصفح تحميله |
param | اختياري - معلمة يتم تمريرها إلى التطبيق الصغير تحدد اسمه (name) وقيمته (value). سيتمكن التطبيق الصغير من استرداد قيمة المعلمة name1 بواسطة val1=getParameter("name1") يمكن استخدام أي عدد من المعلمات |
texte | اختياري - سيتم عرضه بواسطة أي متصفح غير قادر على تشغيل تطبيق صغير، على سبيل المثال لأنه لا يمتلك آلة افتراضية Java. |
لنأخذ مثالاً لتوضيح تمرير المعلمات إلى التطبيق الصغير:
<html>
<head>
<title>paramètres applet</title>
</head>
<body>
<applet code="interfaceParams.class" width="400" height="300">
<param name="param1" value="val1">
<param name="param2" value="val2">
</applet>
</body>
</html>
تم إنشاء كود Java الخاص بالتطبيق الصغير interfaceParams كما هو موضح سابقًا. تم إنشاء تطبيق JBuilder ثم تم إجراء بعض التعديلات المذكورة أعلاه:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class interfaceParams extends JApplet {
JPanel contentPane;
JScrollPane jScrollPane1 = new JScrollPane();
JLabel jLabel1 = new JLabel();
DefaultListModel params=new DefaultListModel();
JList lstParams = new JList(params);
//إنشاء الإطار
public void init() {
// enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
// عمليات تهيئة أخرى
moreInit();
}
// عمليات التهيئة
public void moreInit(){
// يعرض قيم معلمات التطبيق الصغير
params.addElement("nom=param1 valeur="+getParameter("param1"));
params.addElement("nom=param2 valeur="+getParameter("param2"));
}//المزيد من التهيئة
//تهيئة المكون
private void jbInit() throws Exception {
............
this.setSize(new Dimension(205, 156));
//this.setTitle("معلمات التطبيق الصغير")؛
jScrollPane1.setBounds(new Rectangle(19, 53, 160, 73));
............
}
}
التنفيذ باستخدام AppletViewer:

5.6.5. الوصول إلى الموارد البعيدة من تطبيق صغير
تستخدم العديد من التطبيقات المعلومات الموجودة في الملفات أو قواعد البيانات. وقد أشرنا إلى أن التطبيق الصغير لا يمكنه الوصول إلى موارد الجهاز الذي يتم تشغيله عليه. وهذا إجراء منطقي. وإلا يكفي كتابة تطبيق صغير لـ"التجسس" على القرص الخاص بمن يقومون بتحميله. ومع ذلك، يمكن للتطبيق الصغير الوصول إلى موارد الخادم الذي تم تنزيله منه، مثل الملفات. وهذا ما سنراه الآن.
5.6.5.1. الفئة URL
يمكن لأي تطبيق Java قراءة ملف موجود على جهاز في الشبكة بفضل الفئة java.net.URL (URL=Uniform Resource Locator). تحدد فئة URL موردًا على الشبكة، والجهاز الذي يوجد عليه، بالإضافة إلى البروتوكول ومنفذ الاتصال الذي يجب استخدامه لاسترداده بالشكل التالي:
البروتوكول:المنفذ//الجهاز/الملف
وبالتالي، يشير URL http://www.ibm.com/index.html إلى الملف index.html الموجود على الجهاز www.ibm.com. ويمكن الوصول إليه عبر بروتوكول http. لم يتم تحديد المنفذ: سيستخدم المتصفح المنفذ 80 الذي هو المنفذ الافتراضي لخدمة http.
دعونا نستعرض بالتفصيل بعض عناصر الفئة URL:
public URL(String spec) | ينشئ مثيلًا لـ URL من سلسلة من النوع "protocol:port//machine/file" - يطلق استثناءً إذا كانت صيغة السلسلة لا تتوافق مع URL |
public String getFile() | يسمح بالحصول على حقل الملف من السلسلة "protocol:port//machine/file" من URL |
public String getHost() | يسمح بالحصول على حقل الجهاز من السلسلة "بروتوكول:منفذ//جهاز/ملف" من URL |
public String getPort() | يسمح بالحصول على حقل المنفذ من السلسلة "بروتوكول:منفذ//جهاز/ملف" من URL |
public String getProtocol() | يسمح بالحصول على حقل البروتوكول من السلسلة "protocole:port//machine/fichier" من URL |
public URLConnection openConnection() | يفتح الاتصال بالجهاز البعيد getHost() على منفذه getPort() وفقًا لبروتوكول getProtocol() بهدف قراءة الملف getFile(). يلقي باستثناء إذا تعذر فتح الاتصال |
public final InputStream openStream() | اختصار لـ openConnection().getInputStream(). يسمح بالحصول على دفق إدخال يمكن من خلاله قراءة محتوى الملف getFile(). يطلق استثناءً إذا تعذر الحصول على الدفق |
public String toString() | يعرض هوية مثيل URL |
هناك طريقتان تهماننا هنا:
- public URL(String spec) لتحديد الملف المراد استغلاله
- public final InputStream openStream() لاستخدامه
5.6.5.2. مثال على وحدة التحكم
سنكتب برنامج Java، بدون واجهة رسومية، مكلف بعرض محتوى ملف URL الذي نمرره كمعلمة على الشاشة. قد يكون مثال الاستدعاء كما يلي:
البرنامج بسيط نسبيًا:
import java.net.*; // لفئة URL
import java.io.*; // للتدفقات (stream)
public class urlcontenu{
// يعرض محتوى URL الذي تم تمريره كحجة
// يجب أن يكون هذا المحتوى نصًا حتى يكون قابلاً للقراءة
public static void main (String arg[]){
// التحقق من المعلمات
if(arg.length==0){
System.err.println("Syntaxe pg url");
System.exit(0);
}
try{
// إنشاء URL
URL url=new URL(arg[0]);
// قراءة المحتوى
try{
// إنشاء تدفق الإدخال
BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));
try{
// قراءة أسطر النص في تدفق الإدخال
String ligne;
while((ligne=is.readLine())!=null)
System.out.println(ligne);
} catch (Exception e){
System.err.println("Erreur lecture : " +e);
}
finally{
try { is.close(); } catch (Exception e) {}
}
} catch (Exception e){
System.err.println("Erreur openStream : " +e);
}
} catch (Exception e){
System.err.println("Erreur création URL : " +e);
}
}// نهاية main
}// نهاية الفئة
بعد التحقق من وجود حجة، نحاول إنشاء URL باستخدامها:
// إنشاء URL
URL url=new URL(arg[0]);
قد يفشل هذا الإنشاء إذا لم تتوافق الحجة مع صيغة URL protocole:port//machine/fichier. إذا كان لدينا URL صحيح من الناحية النحوية، نحاول إنشاء تدفق إدخال يمكننا من خلاله قراءة أسطر النص. يوفر تدفق الإدخال المقدم بواسطة URL URL.openStream() تدفقًا من النوع InputStream وهو تدفق موجه للأحرف: تتم القراءة حرفًا حرفًا ويجب تشكيل الأسطر بنفسك. لتمكين قراءة أسطر النص، يجب استخدام الطريقة readLine من الفئات BufferedReader أو DataInputStream.On، وقد تم اختيار BufferedReader هنا. لم يتبق لنا سوى تحويل التدفق InputStream من URL إلى تدفق BufferedReader. ويتم ذلك عن طريق إنشاء تدفق وسيط InputStreamReader:
BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));
قد يفشل إنشاء هذا التدفق. يتم التعامل مع الاستثناء المقابل. بمجرد الحصول على التدفق، لا يتبقى لنا سوى قراءة أسطر النص وعرضها على الشاشة.
String ligne;
while((ligne=is.readLine())!=null)
System.out.println(ligne);
هنا أيضًا، قد تؤدي القراءة إلى حدوث استثناء يجب معالجته. فيما يلي مثال على تنفيذ البرنامج الذي يتطلب URL محلي:
E:\data\serge\Jbuilder\interfaces\JApplets\3>java urlcontenu http://المضيف المحلي
<html>
<head>
<title>Bienvenue dans le Serveur Web personnel</title>
</head>
<body bgcolor="#FFFFFF" link="#0080FF" vlink="#0080FF"
topmargin="0" leftmargin="0">
<table border="0" cellpadding="0" cellspacing="0" width="100%"
bgcolor="#000000">
............
</table>
</center></div></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
5.6.5.3. مثال بياني
ستكون الواجهة الرسومية كما يلي:

الرقم | الاسم | النوع | الدور |
1 | txtURL | JTextField | URL للقراءة |
2 | btnCharger | JButton | زر تشغيل قراءة URL |
3 | JScrollPane1 | JScrollPane | لوحة متحركة |
4 | lstURL | JList | قائمة تعرض محتوى URL المطلوب |
عند التنفيذ، نحصل على نتيجة مشابهة لتلك التي يظهرها برنامج وحدة التحكم:

الرمز ذو الصلة بالتطبيق هو التالي:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.net.*;
import java.io.*;
public class interfaceURL extends JFrame {
JPanel contentPane;
JLabel jLabel1 = new JLabel();
JTextField txtURL = new JTextField();
JButton btnCharger = new JButton();
JScrollPane jScrollPane1 = new JScrollPane();
DefaultListModel lignes=new DefaultListModel();
JList lstURL = new JList(lignes);
//إنشاء الإطار
public interfaceURL() {
..............
}
//تهيئة المكون
private void jbInit() throws Exception {
..............
}
//تم استبداله، وبالتالي يمكننا الخروج عند إغلاق النافذة
protected void processWindowEvent(WindowEvent e) {
...................
}
void txtURL_caretUpdate(CaretEvent e) {
// يحدد حالة زر التحميل
btnCharger.setEnabled(! txtURL.getText().trim().equals(""));
}
void btnCharger_actionPerformed(ActionEvent e) {
// يعرض محتوى URL في القائمة
try{
afficherURL(txtURL.getText().trim());
}catch(Exception ex){
// عرض الخطأ
JOptionPane.showMessageDialog(this,"Erreur : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
}//محاولة-التقاط
}
private void afficherURL(String strURL) throws Exception {
// يعرض محتوى URL strURL في القائمة
// لا يتم التعامل مع أي استثناءات بشكل محدد. نكتفي بإعادة توجيهها
// إنشاء URL
URL url=new URL(strURL);
// إنشاء تدفق الإدخال
BufferedReader IN=new BufferedReader(new InputStreamReader(url.openStream()));
// قراءة أسطر النص في تدفق الإدخال
String ligne;
while((ligne=IN.readLine())!=null)
lignes.addElement(ligne);
// إغلاق تدفق القراءة
IN.close();
}
}
5.6.5.4. تطبيق صغير
تم تحويل التطبيق الرسومي السابق إلى تطبيق صغير كما تم عرضه عدة مرات. يتم إدراج التطبيق الصغير في مستند HTML appliURL.htm:
<html>
<head>
<title>Applet lisant une URL</title>
</head>
<body>
<h1>Applet lisant une URL</h1>
<applet
code="appletURL.class"
width="400"
height="300"
>
</applet>
</body>
</html>

في المثال أعلاه، طلب المتصفح http://localhost:81/Japplets/2/appliURL.htm من خادم ويب يعمل على المنفذ 81. ثم تم عرض التطبيق الصغير في المتصفح. في هذه الحالة، تم طلب http://localhost:81/Japplets/2/appliURL.htm مرة أخرى للتحقق من حصولنا على الملف appliURL.htm الذي قمنا بإنشائه. الآن دعونا نحاول تحميل ملف URL لا ينتمي إلى الجهاز الذي أصدر التطبيق الصغير (هنا localhost):

تم رفض تحميل ملف URL. نجد هنا القيد المرتبط بالتطبيقات الصغيرة: لا يمكنها الوصول إلا إلى موارد الشبكة الخاصة بالجهاز الذي تم تنزيلها منه. هناك طريقة لتجاوز هذا القيد، وهي تفويض طلبات الشبكة إلى برنامج خادم موجود على الجهاز الذي تم تنزيل التطبيق الصغير منه. سيقوم هذا البرنامج بإجراء طلبات الشبكة بدلاً من التطبيق الصغير وإرسال النتائج إليه. ويُطلق على هذا البرنامج اسم برنامج الترحيل.
5.7. التطبيق الصغير IMPOTS
سنقوم الآن بتحويل التطبيق الرسومي IMPOTS إلى تطبيق صغير. وهذا أمر مهم: سيصبح التطبيق متاحًا لأي مستخدم للإنترنت لديه متصفح ومكون إضافي Java 1.4 (بسبب المكون JSpinner). وبذلك يصبح تطبيقنا عالميًا... سيتطلب هذا التعديل أكثر قليلاً من مجرد التحويل من تطبيق رسومي إلى تطبيق صغير الذي أصبح الآن أمرًا معتادًا. يستخدم التطبيق خيار قائمة Initialiser الذي يهدف إلى قراءة محتوى ملف محلي. ولكن إذا تصورنا تطبيق ويب، نرى أن هذا المخطط لم يعد صالحًا:

من الواضح أن البيانات التي تحدد جداول الضرائب ستكون موجودة على خادم الويب وليس على كل جهاز من أجهزة العملاء. سيتم وضع الملف المراد قراءته على خادم الويب هنا في نفس المجلد الذي يوجد فيه التطبيق الصغير، وسيتعين على الخيار Initialiser البحث عنه هناك. أظهرت الأمثلة السابقة كيفية القيام بذلك. علاوة على ذلك، لاختيار ملف البيانات محليًا، استخدمنا مكونًا JFileChooser لم يعد له مكان هنا.
سيكون المستند HTML الذي يحتوي على التطبيق الصغير كما يلي:
<html>
<head>
<title>Applet de calcul d'impôts</title>
</head>
<body>
<h2>Calcul d'impôts</h2>
<applet
code="appletImpots.class"
width="320"
height="270"
>
<param name="data" value="impots.txt">
تجدر الإشارة إلى المعلمة data التي تمثل اسم الملف الذي يحتوي على بيانات جدول الضرائب. يوجد المستند HTML، والفئة appletImpots، والفئة impots، والملف impots.txt جميعها في نفس المجلد على خادم الويب:
E:\data\serge\web\JApplets\impots>dir
12/06/2002 13:24 247 impots.txt
13/06/2002 10:17 654 appletImpots$2.class
13/06/2002 10:17 653 appletImpots$3.class
13/06/2002 10:17 653 appletImpots$4.class
13/06/2002 10:17 651 appletImpots$5.class
13/06/2002 10:06 651 appletImpots$6.class
13/06/2002 10:17 8 655 appletImpots.class
13/06/2002 10:17 657 appletImpots$1.class
13/06/2002 10:24 286 appletImpots.htm
13/06/2002 10:17 1 305 impots.class
فيما يلي كود التطبيق الصغير (لم نبرز سوى التغييرات مقارنة بالتطبيق الرسومي):
...........
import java.net.*;
import java.applet.Applet;
public class appletImpots extends JApplet {
// مكونات النافذة
JPanel contentPane;
............................
// سمات الفئة
double[] limites=null;
double[] coeffr=null;
double[] coeffn=null;
impots objImpots=null;
String urlDATA=null;
//إنشاء الإطار
public void init() {
//enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
// عمليات تهيئة أخرى
moreInit();
}
// تهيئة النموذج
private void moreInit(){
// قائمة "حساب" معطلة
mnuCalculer.setEnabled(false);
................
// استرداد اسم ملف جدول الضرائب
String nomFichier=getParameter("data");
// خطأ؟
if(nomFichier==null){
// رسالة خطأ
txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
// يتم حظر خيار التهيئة
mnuInitialiser.setEnabled(false);
// نهاية
return;
}//if
// يتم تعيين URL للبيانات
urlDATA=getCodeBase()+"/"+nomFichier;
}//moreInit
//تهيئة المكون
private void jbInit() throws Exception {
...................
}
void mnuQuitter_actionPerformed(ActionEvent e) {
// الخروج من التطبيق
System.exit(0);
}
void mnuInitialiser_actionPerformed(ActionEvent e) {
// تحميل ملف البيانات
try{
// قراءة البيانات
lireDATA();
// إنشاء كائن الضرائب
objImpots=new impots(limites,coeffr,coeffn);
................
}
private void lireDATA() throws Exception {
// جداول البيانات
ArrayList aLimites=new ArrayList();
ArrayList aCoeffR=new ArrayList();
ArrayList aCoeffN=new ArrayList();
String[] champs=null;
// فتح الملف للقراءة
BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));
// قراءة الملف سطراً سطراً
....................
}
void mnuCalculer_actionPerformed(ActionEvent e) {
// حساب الضريبة
......................
}
void txtSalaire_caretUpdate(CaretEvent e) {
..........
}
void mnuEffacer_actionPerformed(ActionEvent e) {
...............
}
}
نحن نعلق هنا فقط على التغييرات التي نتجت عن كون ملف البيانات المراد قراءته موجودًا الآن على جهاز بعيد بدلاً من جهاز محلي:
يتم الحصول على اسم ملف البيانات المراد قراءته URL من خلال الكود التالي:
// استرداد اسم ملف جدول الضرائب
String nomFichier=getParameter("data");
// خطأ؟
if(nomFichier==null){
// رسالة خطأ
txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
// يتم حظر الخيار "تعيين"
mnuInitialiser.setEnabled(false);
// نهاية
return;
}//if
// يتم تعيين URL للبيانات
urlDATA=getCodeBase()+"/"+nomFichier;
لنتذكر أن اسم الملف "impots.txt" تم تمريره في المعلمة data الخاصة بالتطبيق الصغير:
لذا يبدأ الكود أعلاه باسترداد قيمة المعلمة data مع معالجة أي خطأ محتمل. إذا كان URL للوثيقة HTML التي تحتوي على التطبيق الصغير هو http://localhost:81/JApplets/impots/appletImpots.htm، فإن URL للملف impots.txt سيكون http://localhost:81/JApplets/impots/impots.txt. علينا إنشاء اسم هذا الملف URL. توفر الطريقة getCodeBase() الخاصة بالتطبيق الصغير (applet) الملف URL الخاص بالمجلد الذي تم فيه استرداد المستند HTML الذي يحتوي على التطبيق الصغير، لذلك في مثالنا http://localhost:81/JApplets/impots. تسمح التعليمات التالية ببناء URL لملف البيانات:
نجد في الطريقة lireFichier() التي تقرأ محتوى URL urlData، إنشاء تدفق الإدخال الذي سيسمح بقراءة البيانات:
// فتح الملف للقراءة
BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));
من هذا المنطلق، لم يعد بالإمكان التمييز بين كون البيانات تأتي من ملف بعيد أم محلي. فيما يلي مثال على تشغيل التطبيق الصغير:

5.8. خلاصة
قدم هذا الفصل
- مقدمة حول إنشاء واجهات رسومية باستخدام Jbuilder
- مكونات Swing الأكثر شيوعًا
- إنشاء التطبيقات الصغيرة
سنستنتج أن
- يمكن كتابة الكود الذي تم إنشاؤه بواسطة JBuilder يدويًا. وبمجرد الحصول على هذا الكود بأي طريقة كانت، يكفي استخدام JDK لتنفيذه، وبذلك يصبح JBuilder غير ضروري.
- استخدام أداة مثل JBuilder يمكن أن يؤدي إلى مكاسب كبيرة في الإنتاجية:
- إذا كان من الممكن كتابة الكود الذي تم إنشاؤه بواسطة JBuilder يدويًا، فقد يستغرق ذلك وقتًا طويلاً ولا يقدم سوى فائدة قليلة، حيث أن منطق التطبيق يكون عادةً في مكان آخر.
- يمكن أن يكون الكود الذي تم إنشاؤه مفيدًا. قراءته ودراسته طريقة جيدة لاكتشاف بعض الأساليب وخصائص المكونات التي نستخدمها لأول مرة.
- JBuilder متعدد المنصات. لذلك نحتفظ بما اكتسبناه عند الانتقال من منصة إلى أخرى. إن القدرة على كتابة برامج تعمل في كل من Windows وLinux هي بالطبع عامل إنتاجية مهم جدًا. لكن هذا يرجع إلى Java نفسها وليس إلى JBuilder.
5.9. Jbuilder على نظام Linux
تم اختبار جميع الأمثلة السابقة على نظام Windows 98. قد يتساءل المرء عما إذا كانت البرامج المكتوبة قابلة للتنفيذ كما هي على نظام Linux. شريطة أن يحتوي جهاز Linux المعني على الفئات المستخدمة من قبل البرامج المختلفة، فإنها قابلة للتنفيذ. إذا قمت، على سبيل المثال، بتثبيت JBuilder على نظام Linux، فإن الفئات اللازمة موجودة بالفعل على جهازك. فيما يلي مثال على نتيجة تشغيل أحد برامجنا على المكون JList مع JBuilder 4 في نظام Linux:
نقدم أدناه تثبيت JBuilder 4 Foundation على جهاز Linux. لا يطرح تثبيته على جهاز Win9x أي مشكلة وهو مشابه للعملية التي سيتم وصفها الآن. قد يختلف تثبيت الإصدارات اللاحقة بلا شك، لكن هذا المستند قد يوفر مع ذلك بعض المعلومات المفيدة.
يتوفر Jbuilder على موقع Inprise على الرابط http://www.inprise.com/jbuilder

توجد في هذه الصفحة روابط خاصة بنظامي Windows وLinux. سنقوم الآن بوصف تثبيت JBuilder على جهاز يعمل بنظام Linux مزود بواجهة المستخدم الرسومية KDE.
عند اتباع الرابط Jbuilder 4 for Linux، يظهر نموذج. نقوم بملئه ونحصل في النهاية على ملفين:
jb4docs_fr.tar.gz: الوثائق والأمثلة الخاصة بـ Jbuilder4
jb4fndlinux_fr.tar.gz: Jbuilder4 Foundation. وهي نسخة محدودة من Jbuilder4 التجاري ولكنها كافية في السياق التعليمي.
سيتم إرسال مفتاح تنشيط البرنامج إليك عبر البريد الإلكتروني. سيسمح لك هذا المفتاح باستخدام Jbuilder4 وسيُطلب منك إدخاله عند أول استخدام. إذا فقدت هذا المفتاح، يمكنك العودة إلى الرابط أعلاه واتباع الرابط "get your activation key". نفترض بعد ذلك أنك root وأنك موجود في مجلد الملفين tar.gz أعلاه. نقوم بفك ضغط الملفين:
[ls -l]
drwxr-xr-x 3 nobody nobody 4096 oct 10 2000 docs
drwxr-xr-x 3 nobody nobody 4096 déc 5 13:00 foundation
[ls -l foundation]
-rw-r--r-- 1 nobody nobody 1128 déc 5 13:00 deploy.txt
-rwxr-xr-x 1 nobody nobody 69035365 déc 5 13:00 fnd_linux_install.bin
drwxr-xr-x 2 nobody nobody 4096 déc 5 13:00 images
-rw-r--r-- 1 nobody nobody 15114 déc 5 13:00 index.html
-rw-r--r-- 1 nobody nobody 23779 déc 5 13:00 license.txt
-rw-r--r-- 1 nobody nobody 75739 déc 5 13:00 release_notes.html
-rw-r--r-- 1 nobody nobody 31902 déc 5 13:00 whatsnew.html
[ls -l docs]
-rw-r--r-- 1 nobody nobody 1128 oct 10 2000 deploy.txt
-rwxr-xr-x 1 nobody nobody 40497874 oct 10 2000 doc_install.bin
drwxr-xr-x 2 nobody nobody 4096 oct 10 2000 images
-rw-r--r-- 1 nobody nobody 9210 oct 10 2000 index.html
-rw-r--r-- 1 nobody nobody 23779 oct 10 2000 license.txt
-rw-r--r-- 1 nobody nobody 75739 oct 10 2000 release_notes.html
-rw-r--r-- 1 nobody nobody 31902 oct 10 2000 whatsnew.html
في المجلدين اللذين تم إنشاؤهما، الملف .bin هو ملف التثبيت. بالإضافة إلى ذلك، استخدم متصفحًا لعرض الملف index.html الموجود في كل من المجلدين. فهما يقدمان الإرشادات اللازمة لتثبيت المنتجين. لنبدأ بتثبيت Jbuilder Foundation:
تظهر الشاشة الأولى للتثبيت. ستتبعها العديد من الشاشات الأخرى:

قم بالتأكيد. تلي ذلك شاشة توضيحية:

قم بـ [suivant].

اقبل عقد الترخيص وقم بتنفيذ [suivant].

اقبل الموقع المقترح لـ Jbuilder (دوّن هذا الموقع، فستحتاج إليه لاحقًا) واضغط على [suivant]. تتم عملية التثبيت بسرعة.

نحن جاهزون لإجراء الاختبار الأول. في KDE، قم بتشغيل مدير الملفات للانتقال إلى دليل الملفات التنفيذية لـ Jbuilder: /opt/jbuilder4/bin (إذا كنت قد قمت بتثبيت jbuilder في /opt/jbuilder4):

انقر على أيقونة jbuilder أعلاه. نظرًا لأن هذه هي المرة الأولى التي تستخدم فيها Jbuilder، سيتعين عليك إدخال مفتاح التفعيل الخاص بك:

املأ الحقول Nom و Société. انقر على [Ajouter] لإدخال مفتاح التفعيل الخاص بك. نذكرك بأن هذا المفتاح قد تم إرساله إليك عبر البريد الإلكتروني، وإذا فقدته، يمكنك الحصول عليه من الرابط الذي قمت بتنزيل Jbuilder 4 منه (انظر بداية التثبيت). بمجرد قبول مفتاح التفعيل الخاص بك، سيتم تذكيرك بشروط الترخيص:

بالضغط على OK، ستعود إلى نافذة إدخال المعلومات التي سبق أن رأيتها:

اضغط على OK لإنهاء هذه المرحلة التي لا يتم تنفيذها إلا عند التشغيل الأول. ثم تظهر بيئة تطوير Jbuilder:

اخرج من Jbuilder لتثبيت الوثائق الآن. ستقوم هذه الوثائق بتثبيت عدد من الملفات التي سيتم استخدامها في دليل Jbuilder، وهو دليل غير مكتمل في الوقت الحالي. عد إلى الدليل الذي قمت فيه بفك ضغط ملفات Jbuilder tar.gz. يوجد برنامج التثبيت في الدليل docs. انتقل إليه:
[cd docs]
[ls -l]
-rw-r--r-- 1 nobody nobody 1128 oct 10 2000 deploy.txt
-rwxr-xr-x 1 nobody nobody 40497874 oct 10 2000 doc_install.bin
drwxr-xr-x 2 nobody nobody 4096 oct 10 2000 images
-rw-r--r-- 1 nobody nobody 9210 oct 10 2000 index.html
-rw-r--r-- 1 nobody nobody 23779 oct 10 2000 license.txt
-rw-r--r-- 1 nobody nobody 75739 oct 10 2000 release_notes.html
-rw-r--r-- 1 nobody nobody 31902 oct 10 2000 whatsnew.html
ملف التثبيت هو doc_install.bin ولكن لا يمكن تشغيله على الفور. إذا قمت بذلك، فسيفشل التثبيت دون أن تفهم السبب. اقرأ الملف index.html باستخدام متصفح للحصول على وصف دقيق لطريقة التثبيت. يحتاج المثبت إلى آلة افتراضية جافا، وهي في الواقع برنامج يسمى java ويجب أن يكون موجودًا في PATH على جهازك. نذكركم بأن PATH هو متغير Unix تشير قيمته بالصيغة rep1:rep2:...:repn إلى الدلائل repi التي يجب استكشافها للبحث عن ملف قابل للتنفيذ. هنا، يطلب المثبت تشغيل برنامج java. قد يكون لديك عدة إصدارات من هذا البرنامج حسب عدد الأجهزة الافتراضية Java التي قمت بتثبيتها هنا أو هناك. وبشكل منطقي، سنستخدم الإصدار الذي يأتي مع Jbuilder4 والموجود في /opt/jbuilder4/jdk1.3/bin. لإدراج هذا الدليل في المتغير PATH:
[echo $PATH]
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
[export PATH=/opt/jbuilder4/jdk1.3/bin/:$PATH]
[echo $PATH]
/opt/jbuilder4/jdk1.3/bin/:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
يمكننا الآن بدء تثبيت الوثائق:
سيتم إجراء تثبيت رسومي:

وهي تشبه إلى حد كبير عملية التثبيت في Jbuilder Foundation. ولذلك لن ندخل في تفاصيلها. هناك شاشة لا يجب أن تفوتها:

من المفترض أن يحدد المثبت تلقائيًا المكان الذي قمت بتثبيت Jbuilder Foundation فيه. لذا، لا تقم بتغيير الخيار المقترح إلا إذا كان غير صحيح بالطبع.
بمجرد تثبيت الوثائق، نكون جاهزين لإجراء اختبار جديد لـ Jbuilder. قم بتشغيل التطبيق كما هو موضح أعلاه. بمجرد ظهور Jbuilder، اختر الخيار "مساعدة/موضوع المساعدة". في الإطار الأيسر، انقر على الرابط Tutoriels:

يأتي Jbuilder مزودًا بمجموعة من البرامج التعليمية التي ستتيح لك البدء في برمجة Java. أدناه، اتبعنا البرنامج التعليمي "إنشاء تطبيق" المذكور أعلاه.

في Jbuilder، انقر على ملف/مشروع جديد. سيظهر لك مساعد 3 شاشات:

سنقوم بإنشاء نافذة بسيطة بعنوان "مرحبًا بالجميع". سنسمي هذا المشروع coucou. تم تنفيذ المثال بواسطة root. يقترح Jbuilder بعد ذلك تجميع جميع المشاريع في مجلد jbproject ضمن مجلد الاتصال الخاص بـ root. سيتم وضع المشروع coucou في المجلد coucou (حقل اسم مجلد المشروع) التابع لـ jbproject. قم بإنشاء [suivant].

هذه الشاشة الثانية هي ملخص للمسارات المختلفة لمشروعك. لا يوجد شيء لتعديله. قم بإنشاء [suivant].

تطلب منك الشاشة الثالثة تخصيص مشروعك. أكمل المعلومات واضغط على [Terminer].
الآن قم بتنفيذ Fichier/Nouveau:

اختر Application وانقر على OK. سيظهر لك مساعد جديد شاشتين:

يستقبل الحقل paquet اسم المشروع. يطلب الحقل "الفئة" (class) القيمة nom التي ستُعطى للفئة الرئيسية للمشروع. خذ الاسم أعلاه وقم بإدخال [suivant]:

تتضمن تطبيقنا فئة Java ثانية لنافذة التطبيق. قم بتسمية هذه الفئة. الحقل Titre هو العنوان الذي تريد تسمية هذه النافذة به. يقدم لك الإطار options خيارات لنافذتك. اخترهم جميعًا وقم بإنشاء [Terminer]. ستعود بعد ذلك إلى بيئة Jbuilder التي تم إثرائها بالمعلومات التي أدخلتها لمشروعك:

في النافذة اليسرى/العلوية (1)، توجد قائمة بالملفات التي يتكون منها مشروعك. وتوجد على وجه الخصوص الفئتان .java اللتان قمنا بتسميتهما للتو. في النافذة اليمنى (2) يوجد كود Java الخاص بالفئة coucouCadre. في النافذة اليسرى/السفلية، تجد بنية مشروعك (الفئات، الطرق، السمات). المشروع جاهز للتنفيذ. اضغط على الزر 4 أعلاه أو انقر على "تنفيذ/تنفيذ المشروع" أو انقر على F9. يجب أن تظهر لك النافذة التالية:

بالنقر على الخيار Aide، ستجد المعلومات التي أدخلتها في مساعدات الإنشاء. لن نذهب أبعد من ذلك. حان الوقت الآن للانغماس في الدروس التعليمية.
قبل الانتهاء، دعونا نوضح ببساطة أنه يمكنك استخدام ليس Jbuilder وواجهته الرسومية، بل jdk الذي أحضره معه ووضعه في /opt/jbuilder4/jdk1.3. قم بإنشاء الملف essai1.java التالي:
import java.io.*;
public class essai1{
public static void main(String args[]){
System.out.println("coucou");
System.exit(0);
}
}
لنقوم بتجميعه وتشغيله باستخدام jdk من Jbuilder:






