C, PHP, VB, .NET

Дневникът на Филип Петров


* Въведение в Swing – рамки, бутони и менюта

Публикувано на 08 януари 2013 в раздел ПИК3 Java.

Swing е набор от пакети за изграждане на програми с графична среда на Java. Самият Swing е написан на езика Java, т.е. той е платформено независим. Част е от т.нар. "Java Foundation Classes". Ще демонстрираме работата със Swing чрез няколко елементарни примера.

1. JFrame

JFrame е основният "контейнер", който се използва, за да разполагаме други обекти в него. Можете да го приемете като празна рамка, в която ще се поставят други контроли. Характеризира се със заглавие (title) и размери (в пиксели). Следният код демонстрира създаването на една празна рамка:

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Example{
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SampleFrame());
    }
}

class SampleFrame extends JFrame implements Runnable{
  public SampleFrame(){
    // задаваме заглавие
    setTitle("Simple example");
    // задаваме размери
    setSize(400, 250);
    // центрира рамката (центъра на текущия монитор)
    setLocationRelativeTo(null);
    // задава на "X" бутона да спира приложението
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  public void run() {
    this.setVisible(true);
  }
}

Ако компилирате и изпълните приложението ще видите следното:

jframe-sample

Функцията invokeLater на SwingUtilities указва, че нашата рамка ще се зареди в т.нар. "Swing Event Queue", което гарантира, че приложението ще работи коректно в многонишкова среда (каквато ние ще изграждаме). В Swing се работи основно с две нишки - една за графичните контроли и една основна (main метода в горния пример), която прави обновявания по нея.

2. JButton - създаване на бутони и събития за тях

Сега искаме да изобразим бутон, при натискането на който трябва да се извърши дадено действие. На първо време ние трябва да изобразим този бутон на екрана. Първо е необходимо да добавим библиотеките javax.swing.JButton (бутона) и javax.swing.JPanel (панел, който се разполага в текущата рамка, който ще съдържа този бутон).

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JButton;
import javax.swing.JPanel;

public class Example{
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SampleFrame());
    }
}

class SampleFrame extends JFrame implements Runnable{
  JPanel panel1;
  JButton button1;
  public SampleFrame(){
    setTitle("Simple example");
    setSize(400, 250);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  private void addJPanel1(){
    this.panel1 = new JPanel();
    panel1.setLayout(null);
    this.getContentPane().add(panel1);
  }
  private void addButton1(JPanel p){
    // в конструктора се подава текста на бутона
    this.button1 = new JButton("Test Button");
    // първи две числа са позиция, вторите две са размери
    this.button1.setBounds(150, 150, 100, 50);
    p.add(this.button1);
  }
  public void run() {
    // добавяме панел
    this.addJPanel1();
    // слагаме бутон в този панел
    this.addButton1(this.panel1);
    this.setVisible(true);
  }
}

Когато изпълните тази програма ще имате рамка, в която има един бутон, който все още не прави нищо.

jbutton-sample

Сега ще искаме да извършим дадени действия с този бутон. Например нека сменим неговия текст когато го натиснем. За целта е нужно да добавим "Action Listener", т.е. обект, който ще извика метод, който ще се изпълни при натискането на бутона. Такива методи се наричат "събития". Трябва да добавим java.awt.event.ActionEvent и java.awt.event.ActionListener (забележете, че те са от java.awt, а не от java.swing). След това ще ги използваме в addButton1 метода:

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JButton;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Example{
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SampleFrame());
    }
}

class SampleFrame extends JFrame implements Runnable{
  JPanel panel1;
  JButton button1;
  public SampleFrame(){
    setTitle("Simple example");
    setSize(400, 250);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  private void addJPanel1(){
    this.panel1 = new JPanel();
    panel1.setLayout(null);
    this.getContentPane().add(panel1);
  }
  private void addButton1(JPanel p){
    this.button1 = new JButton("Test Button");
    this.button1.setBounds(150, 150, 100, 50);
    // Добавяме ActionListener за бутона
    button1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        String text = (String)event.getActionCommand();
        if (text.equals("Test Button")){
          button1.setText("CLICK!");
        }
        else{
          button1.setText("Test Button");
        }
      }
    });
    this.button1.setToolTipText("Нашия примерен бутон");
    p.add(this.button1);
  }
  public void run() {
    this.addJPanel1();
    this.addButton1(this.panel1);
    this.setVisible(true);
  }
}

Когато стартирате програмата ще видите, че при натискане на бутона текста му ще се променя, т.е. ще сме задействали метода "actionPerformed" на събитието, което създадохме. Тук разбира се може да се извършват произволно сложни действия по обработка на информация, също така е възможно да извършвате промени по други визуализирани обекти. Забележете също, че използвахме функцията "setToolTipText" - с нея се прави т.нар. "tooltip", което е помощен текст, който се появява на екрана когато посочим бутона с мишката, но без да го натискаме:

jbutton-tooltip

2. JMenuBar - създаване на менюта

Обикновено нашите програми имат менюта (напр. "file | edit | view | help"). За да създаваме менюта в Swing са ни необходими javax.swing.JMenuBar (лентата),javax.swing.JMenu (меню) и javax.swing.JMenuItem (елемент от менюто). В следващия пример създаваме
меню "File" с команда "Quit", с която ще излизаме от програмата. За целта ще добавим нови член променливи за менюто и ще напишем нова функция "addMenuBar":

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JButton;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

public class Example{
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SampleFrame());
    }
}

class SampleFrame extends JFrame implements Runnable{
  JPanel panel1;
  JButton button1;
  JMenuBar menubar;

  public SampleFrame(){
    setTitle("Simple example");
    setSize(400, 250);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  private void addJPanel1(){
    this.panel1 = new JPanel();
    panel1.setLayout(null);
    this.getContentPane().add(panel1);
  }
  private void addButton1(JPanel p){
    this.button1 = new JButton("Test Button");
    this.button1.setBounds(150, 150, 100, 50);
    // Добавяме ActionListener за бутона
    button1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        String text = (String)event.getActionCommand();
        if (text.equals("Test Button")){
          button1.setText("CLICK!");
        }
        else{
          button1.setText("Test Button");
        }
      }
    });
    this.button1.setToolTipText("Нашия примерен бутон");
    p.add(this.button1);
  }
  private void addMenuBar(){
    this.menubar = new JMenuBar();
    // на конструктора подаваме текста на менюто
    JMenu fileMenu = new JMenu("File");
    // същото важи за items
    JMenuItem itemFileQuit = new JMenuItem("Quit");
    itemFileQuit.setToolTipText("Изход от приложението");
    // подобно на бутоните, тук също ни трябва ActionListener
    itemFileQuit.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        // изход от приложението
        System.exit(0);
      }  
    });
    // добавяме новия item към менюто file:
    fileMenu.add(itemFileQuit);
    // добавяме меню file към бара
    this.menubar.add(fileMenu);
    // добавяме менюто към текущия фрейм
    this.setJMenuBar(this.menubar);
  }
  public void run() {
    this.addJPanel1();
    this.addButton1(this.panel1);
    // добавяме менюто
    this.addMenuBar();
    this.setVisible(true);
  }
}

Нашето елементарно приложение вече ще има "падащо меню":

jmenu-sample

Забележете, че бутона се премести малко надолу в рамката! Това е така, защото координатите на обектите се изчисляват спрямо долния ръб на менюто!

Сега ще добавим още една функционалност. От реалните програми знаем, че чрез натискане на бутони от клавиатурата ние можем да извикваме команди от менютата. Нека например в нашата програма зададем "F" за менюто "File" и "Q" за "Quit". Това се прави като добавим ново събитие "java.awt.event.KeyEvent;":

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JButton;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import java.awt.event.KeyEvent;

public class Example{
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new SampleFrame());
    }
}

class SampleFrame extends JFrame implements Runnable{
  JPanel panel1;
  JButton button1;
  JMenuBar menubar;

  public SampleFrame(){
    setTitle("Simple example");
    setSize(400, 250);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  private void addJPanel1(){
    this.panel1 = new JPanel();
    panel1.setLayout(null);
    this.getContentPane().add(panel1);
  }
  private void addButton1(JPanel p){
    this.button1 = new JButton("Test Button");
    this.button1.setBounds(150, 150, 100, 50);
    button1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        String text = (String)event.getActionCommand();
        if (text.equals("Test Button")){
          button1.setText("CLICK!");
        }
        else{
          button1.setText("Test Button");
        }
      }
    });
    this.button1.setToolTipText("Нашия примерен бутон");
    p.add(this.button1);
  }
  private void addMenuBar(){
    this.menubar = new JMenuBar();
    JMenu fileMenu = new JMenu("File");
    JMenuItem itemFileQuit = new JMenuItem("Quit");
    // създаваме събитията "натискане на бутон от клавиатурата"
    fileMenu.setMnemonic(KeyEvent.VK_F);
    itemFileQuit.setMnemonic(KeyEvent.VK_Q);
    itemFileQuit.setToolTipText("Изход от приложението");
    itemFileQuit.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        System.exit(0);
      }  
    });

    fileMenu.add(itemFileQuit);
    this.menubar.add(fileMenu);
    this.setJMenuBar(this.menubar);
  }
  public void run() {
    this.addJPanel1();
    this.addButton1(this.panel1);
    // добавяме менюто
    this.addMenuBar();
    this.setVisible(true);
  }
}

Вече с натискане на "ALT+F" ще извиквате меню "File", а след това като натиснете "Q" ще се изпълни команда "Quit". Ще забележите, че "F" и "Q" в имената на съответните визуални контроли са подчертани. Това подсказва на потребителя на кой бутон от клавиатурата съответстват. Важно е различните JMenu в даден JMenuBar да имат различни KeyEvent, както и различните JMenuItem в даден JMenu също да имат различни KeyEvent.

Важен е редът, по който добавяте нови елементи в JMenu. По реда на поредните извиквания на функцията "add" се подреждат и елементите на менюто. Можете да добавяте и JMenu като елемент на JMenu. По този начин ще създадете "подменю", т.е. познатата стрелкичка надясно, която показва множество от възможности. С функцията "addSeparator()" на обект JMenu можете да добавяте хоризонтална линия - разделител между елементи.

 



5 коментара


  1. Браво! Винаги се радвам на практически уроци. Не че някога в работата някога ми се е налагало да правя desktop GUI, но тези часове в университета винаги са ми били интересни. При това заслугата беше изцяло на асистента (браво, Тошко!), щото принципно тези неща не са включени в програмата. Най-много се изкефих като правехме notepad. Не че съм го ползвал някога, но това беше първата ми смислена програма.

    Имам и един въпрос. До колко си се занимавал със Swing в големи проекти? Питам, защото по едно време логиката и външният вид се смесват много и става ужасно за поддръжка. Интересувам се от някаква библиотека, която може да се ползва за да се раздели GUI-то от джава кода. (Както например е в Android). На мен скоро ще ми се налага да ползвам нещо такова и понеже не знам - питам :)
    Поздрави.

  2. Кой е "Тошко"?

    Относно въпросът ти - или използвай готов framework, или трябва да се научиш да работиш с много високо ниво на абстракция. Препоръчвам да можеш второто, пък дори и да се възползваш от първото. С други думи - ти сам си раздели GUI от кода, независимо дали средата ти помага.

    В .Net например си е въведено като философия - т.нар. "code behind".

    Тези теми в моя сайт са за 2ри курс, 1ви семестър в ТУ-София. На последното упражнение тази година стигнахме до това:
    https://www.cphpvb.net/java/8471-excercise-8-2012-2013/

    А относно личния въпрос - с големи проекти на Swing не съм се занимавал.

  3. Тошко ми беше асистент по ООП. (става въпрос за ФМИ)

Добави коментар

Адресът на електронната поща няма да се публикува


*