Про java swing - часть 3

January 16, 2008

Менеджеры раскладок



Для графических библиотек java начиная с awt было не характерно управление расположением элементов на форме с помощью задания координат элементов с точностью до пикселя. Ранее мы перед добавлением элементов делали вызов контейнер.setLayout (null). Этим мы отказывались от услуг "менеджеров управления размещением элементов". И после добавления элемента были вынуждены указать для элемента его местоположение и размеры (с точностью до пикселя). Также мы были должны добавить обработчик события для изменения размеров компонента (панели или формы) и в этом обработчике мы пересчитывали координаты элемента. Хотя такой способ дает определенные преимущества, он занимает слишком много времени и чаще можно обойтись использованием "типовых методик расположения элементов".
  1. public static void main(String[] args) {
  2.   JFrame jf = new JFrame("Old Layout Model");
  3.   jf.getContentPane().setLayout(null);
  4.   final JLabel lab = new JLabel("Label Object");
  5.   lab.setBorder (new TitledBorder ("Текстовая надпись"));
  6.   jf.getContentPane().add (lab);
  7.  
  8.   jf.getContentPane().addComponentListener(
  9.     new ComponentAdapter() {
  10.       public void componentResized(ComponentEvent e) {
  11.         Dimension d  = e.getComponent().getSize();
  12.         Dimension pd = lab.getPreferredSize();
  13.         int l = (d.width - pd.width) / 2;
  14.         int t = (d.height - pd.height) / 2;
  15.         lab.setBounds(l , t, pd.width , pd.height);
  16.       }
  17.     }
  18.   );
  19.   jf.setVisible(true);
  20. }
Данный способ плох тем, что мы вынуждены пересчитывать местоположение и размеры элементов, выполняя типовые расчеты. Можем забыть расположить какой-нибудь элемент. Также, мы рассредотачиваем код в нескольких местах, теряя контроль за ним. Рекомендуемым способ является использование менеджеров раскладки. Это класс, который присоединяется к некоторому классу контейнеру и реагирует на изменение его размеров, затем переупорядочивает элементы. Концепция менеджеров основана на том, что каждый компонент может предоставить ему информацию о своих размерах с помощью методов:
  1. final JLabel lab = new JLabel("Label Object"){
  2.    public Dimension getPreferredSize() {
  3.      return new Dimension (200 , 25);
  4.    }
  5.  
  6.    public Dimension getMinimumSize() {
  7.      return new Dimension (100 , 10);
  8.    }
  9.  
  10.    public Dimension getMaximumSize() {
  11.      return new Dimension (300 , 100);
  12.    }
  13. };
Например, созданная с помощью такого наследования текстовая надпись не может быть уменьшена менее чем (100*10), не может быть более чем (300*100) и, если есть возможность задать размер без ограничений, то ее размер будет (200*100).

Примечание: данные методы носят характер совещательных. Менеджеры раскладок должны принимать их во внимание, но если что-то не получается, то менеджер раскладки может их и не учитывать.

Элементы управления обычно сами определяют свои предпочтимые и предельные размеры на основании размещенной внутри этих компонентов информации, но иногда возможно устанавливать эти параметры жестко, например, так:
  1. lab.setMaximumSize(new Dimension (200 , 400));
  2.  lab.setMinimumSize(new Dimension (100 , 200));
  3.  lab.setPreferredSize(new Dimension (150 , 250));
Наиболее известными менеджерами расладок являются: BorderLayout, GridLayout
 <strong>BorderLayout</strong>
Данная раскладка предназначена для расположения пяти элементов согласно частям света.
  1. JFrame jf = new JFrame("BorderLayout Example");
  2.  int hgap = 10, vgap = 20;
  3.  jf.getContentPane().setLayout(new BorderLayout (hgap , vgap));
  4.  jf.getContentPane().add (new JLabel ("North") , BorderLayout.NORTH);
  5.  jf.getContentPane().add (new JLabel ("South") , BorderLayout.SOUTH);
  6.  jf.getContentPane().add (new JLabel ("West") , BorderLayout.WEST);
  7.  jf.getContentPane().add (new JLabel ("East") , BorderLayout.EAST);
  8.  jf.getContentPane().add (new JLabel ("Center") , BorderLayout.CENTER);
  9.  Component cmps [] =  jf.getContentPane().getComponents();
  10.  for (int i = 0; i < cmps.length; i++) {
  11.    JComponent cmp = (JComponent)cmps[i];
  12.    cmp.setBorder(new TitledBorder ("Border: "+ i));
  13.  }
  14.  jf.pack ();
  15.  jf.setVisible(true);

 <strong>GridLayout</strong>
Данная раскладка размещает произвольное количество элементов, располагая их в прямоугольнике размеров X строк на Y столбцов. Размеры всех элементов идентичны.
  1. JFrame jf = new JFrame("GridLayout Example");
  2. jf.getContentPane().setLayout(new GridLayout(/*rows*/4 , /*cols*/3));
  3. Random r = new Random();
  4. for (int i  =  0; i < 4; i++)
  5.    for (int j  =  0; j < 3; j++){
  6.      JLabel lab = new JLabel (i + ", " + j);
  7.      lab.setBorder(new TitledBorder(""+ (char)('A' + Math.abs(r.nextInt())%26) ) );
  8.      Color fg = new Color (0 , Math.abs(r.nextInt()) % 255 , Math.abs(r.nextInt()) % 255);
  9.      lab.setForeground(fg);
  10.      jf.getContentPane().add (lab);
  11.    }
  12.    jf.pack ();
  13.    jf.setVisible(true);


Элементы управления список и падающий список



Класс Jlist представляет собой группу элементов отображаемых в одну или несколько колонок. Т.к. элементов может быть много, то одновременно со список можно использовать компоненты JscrollPane. Дело в том, что размер Jlist создается именно таким, чтобы полностью уместить все элементы. Так что, если вы не поместите список внутрь области прокрутки - он будет слишком велик и может не уместиться на экране.

При создании списка можно использовать несколько видов конструторов: в простейшем случае вы можете сразу при создании списка указать все элементы:
  1. JFrame.setDefaultLookAndFeelDecorated(true);
  2. JFrame jf = new JFrame("D&D 2");
  3. jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  4.  
  5. Vector vec = new Vector();
  6. vec.add("Apple");
  7. vec.add("Orange");
  8. vec.add("Grapes");
  9. JList list1 = new JList(vec);
  10. JList list2 = new JList(new String[]{"Apple (0)", "Orange (0)", "Grapes (0)"});
  11.  
  12. class MegaFruit {
  13.     String fruitName;
  14.  
  15.     public MegaFruit(String fruitName) {
  16.         this.fruitName = fruitName;
  17.     }
  18.  
  19.     public String toString() {
  20.         return "MegaFruit: " + fruitName;
  21.     }
  22. }
  23. JList list3 = new JList(new MegaFruit[]{new MegaFruit("Apple (M)"),
  24.         new MegaFruit("Orange (M)"), new MegaFruit("Grapes (M)")});
  25. jf.getContentPane().add(list1, BorderLayout.WEST);
  26. jf.getContentPane().add(list2, BorderLayout.CENTER);
  27. jf.getContentPane().add(list3, BorderLayout.EAST);
  28. jf.pack();
  29. jf.setVisible(true);
Обратите внимание, на то что в третьем случае в качестве хранимых элементов в списке помещаются не строки (как в двух предыдущих), а произвольные объекты. Всмомните, что для swing ключевой является концепция MVC: когда мы отделяем данные от их представления. В простейшем случае, в списке хранятся не строки, а объекты представляющие часть предметной области. Другое дело, что нужен способ их отображать. Для этого любой класс помещаемый в список должен иметь переопределенный метод toString, результат вызова которого и отображается в списке.

При размещении элементов можно управлять их порядком (в примере они располагаются друг за другом вертикально), хотя такое поведение можно поведение изменить с помощью вызова:
  1. list1.setLayoutOrientation (JList.VERTICAL); //  стиль размещения элементов в списке - вертикальный
  2. list1.setVisibleRowCount(2); // управлением количества видимых строк в списке (влияет на предпочтимый размер списка)
  3.  
  4. list2.setLayoutOrientation (JList.VERTICAL_WRAP);// тоже вертикальный, но уже с переносом
  5. list2.setVisibleRowCount(2);// управлением количества видимых строк в списке
  6.  
  7. list3.setLayoutOrientation (JList.HORIZONTAL_WRAP); // стиль размещения элементов - горизонтальный
  8. list3.setVisibleRowCount(1);// управлением количества видимых строк в списке


Теперь самое интересное: последний вариант конструктора Jlist получает в качестве параметра ссылку на объект ListModel. Каждый элемент управления, отображающий данные, имеет связанный с ним объект МОДЕЛИ_ДАННЫХ. Для изменения отображаемой информации следует обращаться не к элементу управления, а к его модели. Класс ListModel содержит множество абстрактных методов (для добавления, удаления, изменения и прочее элементов). В практике удобно использовать так называемые модели по умолчанию, они есть практически для всех элементов управления и моделей, для списка этот класс DefaultListModel.

В качестве примера я создаю окно, содержащее список; затем добавляю обработчик события щелчка по списку. И в зависимости от того, какой кнопкой был выполнен щелчок выполняю некоторую работу. Так если клик был выполнен левой кнопой мыши, то в список добавляется новый элемент, в котором содержится координаты точки нажатия. При нажатии на правую кнопку элемент, по которому был выполнен щелчок, удаляется.


  1. JFrame.setDefaultLookAndFeelDecorated(true);
  2. JFrame jf = new JFrame("D&D 2");
  3. jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  4.  
  5. final DefaultListModel dataModel = new DefaultListModel ();
  6. final JList fruits = new JList(dataModel);
  7. fruits.addMouseListener(
  8.   new MouseAdapter() {
  9.     public void mouseClicked(MouseEvent e) {
  10.      if (e.getButton() == MouseEvent.BUTTON1)
  11.        dataModel.addElement("ListClick: " + e.getPoint());
  12.      else
  13.        for (int i = 0; i < dataModel.getSize(); i++){
  14.          Rectangle r = fruits.getCellBounds(i, i);
  15.          if (r.contains(e.getPoint())){
  16.            dataModel.removeElementAt(i);
  17.            return;
  18.          }
  19.        }
  20.      }
  21.   }
  22. );
  23. jf.getContentPane().add(fruits, BorderLayout.CENTER);
  24.  
  25. jf.pack();
  26. jf.setVisible(true);
Примечание: обратите внимание на то, как опеределяется то, по какому элементу был выполнен правый щелчок. Для этого я обращаясь к модели данных. Затем узнаю количестов элементов в модели, в цикле прохожу их все и определяю координаты, которые занимает на экране каждый конкретный элемент. Когда меня интересую данные - я использую модель. Когда же мне нужна визуализация (например, положение элемента) - обращаюсь к списку. Именно в таком распределении обязанностей в библиотеке swing прослеживается использование модели MVC.

После создания списка мы хотим определить то, какие элементы были выбраны. Снова обращайте внимание на разделение обязанностей. Так для управления и получения информации о выделении используется следующие методы:
  1. fruits.getSelectedIndex() // получаем номер одного выбранного элемента
  2. fruits.getSelectedIndices() // получаем массив целых чисел, в которых хранятся номера
  3.                             // тех элементов, которые были выбраны
  4. fruits.getSelectedValue() // возвращается не индекс, а, именно, ОБЪЕКТ
  5. fruits.getSelectedValues() // в случае выделения множества элементов возвращается массив ОБЪЕКТОВ
  6.  
  7. fruits.getSelectionMode() // здесь возращается константа, кодирующая доступные режимы выделения элементов.
  8. fruits.getSelectionModel ()  // возвращается модель выделения.
Для изменения выделения можно использовать методы:
  1. fruits.setSelectedIndex() // в качестве параметра будет передан номер выбираемого элемента
  2. fruits.setSelectedIndices() // то же, но не один элемент, а несколько номеров
  3.  
  4. fruits.setSelectedValue() // установить выбранный ОБЪЕКТ (входой параметр)
  5. fruits.setSelectionMode() // изменить стиль выделения элементов
  6.  
  7. fruits.setSelectionModel () // установить новую модель выделения.
Для задания стиля выделения можно использовать константы:
 SINGLE_SELECTION - только один элемент может быть выбран.
 SINGLE_INTERVAL_SELECTION - можно выбрать несколько элементов но они должны образовывать один сплошной интервал.
 MULTIPLE_INTERVAL_SELECTION - можно выбирать несколько элементов в любой комбинации.
Фактически, все операции связанные с выделением элементов списка можно выполнить используя модель выделения. В примере ниже, создается список из 20 элементов, затем очищается текущее выделение, затем устанавливается режим <любое выделение>, выделяются два интервала 0-2 и 3-4. Последним шагом затем из получившегося выделения исключается отрезок с 1 - до 3. В результате остается выделеными только элементы номер 0 и 4.
  1. for (int i = 0; i < 20; i++)
  2.   dataModel.addElement("Element # " + i);
  3.  
  4. ListSelectionModel selmodel = fruits.getSelectionModel();
  5. // получили ссылку на модель выделения элементов
  6. selmodel.clearSelection();
  7. // очистили выделение
  8. System.out.println (" (0) is selected " + selmodel.isSelectedIndex(0));
  9. // определяем выделен ли сейчас элемент списка под номером 0
  10. // изменяем стиль выделения на МНОЖЕСТВО ЭЛЕМЕНТОВ В ПРОИЗВОЛЬНОМ ПОРЯДКЕ
  11. selmodel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  12. // выделяем интервал 0-2
  13. selmodel.addSelectionInterval(0 , 2);
  14. // выделяем второй интервал 3-4
  15. selmodel.addSelectionInterval(3 , 4);
  16. // удаляем выделение в отрезке  1-3
  17. selmodel.removeSelectionInterval(1,3);
Следующий шаг: настройка того, как будут отображаться элементы в списке. Модель MVC требует, чтобы приложение могло управлять внешним видом элементов спискаю В модели Список-Модель_Данных есть еще третий элемент - renderer. Класс, который должен сформировать картинку отображаемого элемента. В следующем примере я получаю список всех доступных для java-приложения шрифтов, затем помещаю в список имена этих шрифтов. При наведении мыши на элемент списка появляется всплывающая подсказка оформленная с использованием данного шрифта.



Примечание: обратите внимание на способ каким я изменяю шрифт. Хотя в составе каждого визуального компонента еще со времен AWT есть метод setFont (для установки шрифта). Но сейчас пользоваться им становится несколько опасно (часто это приводит к непонятным ошибкам). Наилучшее решение - задавать оформление элементов (в том числе и шрифтовое) используя HTML код.

Для того, чтобы вывести увеличенную версию шрифта на который наведен указатель мыши, я использую стандартный метод в составе классов, которые представляют сложные элементы управления состоящие из нескольких частей. Ведь для каждой из этих частей может соответствовать различная подсказка. Интересно, что все swing элементы управления - по определению сложные элементы, следовательно для любого элемента (не только списка, но и текстовой надписи, кнопки) мы можем создать контекстно-зависимую подсказку. Ранее мы уже изменяли подсказку используя метод setTooltipText, но данный способ был слишком "глобальным".

Падающий список



Для создания падающего списка используется класс JcomboBox. Пользователь может выбирать один из нескольких вариантов либо ввести собственное значение в поле редактирования.

Использование JcomboBox практически идентично обычному списку Jlist. Вы также должны создать модель данных, в которой будут находиться элементы данных. По желанию вы добавляете рендерер. В качестве примера создается форма, на которой расположен падающий список с именами HTML файлов находящихя в текущем каталоге. При разветывании списка создается для каждого элемента небольшая картинка preview содержимого этой html-страницы и после выбора ее полное содержимое отображается в элементе Jlabel.


  1. JFrame.setDefaultLookAndFeelDecorated(true);
  2.  final JFrame jf = new JFrame("D&D 2");
  3.  jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  4.  
  5.  DefaultComboBoxModel dataModel = new DefaultComboBoxModel ();
  6.  // создаем модель данных для списка и заполняем ее всеми файлами в некотором каталоге
  7.  // файлами имеющими расширение htm, или html
  8.  final JComboBox fruits = new JComboBox(dataModel);
  9.  File [] fls = new File ("C:\\tmp\\html").listFiles(
  10.          new FilenameFilter() {
  11.              public boolean accept(File dir, String name) {
  12.                  return name.toLowerCase().endsWith("html") || name.toLowerCase().endsWith("htm");
  13.              }
  14.          }
  15.  );
  16.  for (int i = 0; i < fls.length; i++) {
  17.      dataModel.addElement(fls[i]);
  18.  }
  19.  jf.getContentPane().add (fruits, BorderLayout.NORTH);
  20.  fruits.setRenderer(
  21.          // создаем Reenderer-выполняющий настройку внешнего вида элемента списка
  22.          new ListCellRenderer() {
  23.              HashMap< Object, JComponent> mappings = new HashMap<Object, JComponent>();
  24.  
  25.              public Component getListCellRendererComponent(
  26.                      JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
  27.                  try {
  28.                      if (value == null) return new JLabel ("Выберите элемент из списка");
  29.                      // конечно каждый раз заново выполнять чтение содержимого файла и конструирование глупо,
  30.                      //поэтому создаю кэш
  31.                      if (mappings.containsKey(value))return mappings.get (value);
  32.  
  33.                      BufferedReader brin = new BufferedReader (new FileReader((File) value));
  34.                      char [] data = new char [(int) ((File) value).length()];
  35.                      int cnt = brin.read(data);
  36.                      String html = new String (data, 0 , cnt).trim();
  37.                      JLabel lab = new JLabel(html);
  38.                      lab.setBorder(new TitledBorder("Документ: " + ((File)value).getCanonicalPath() ));
  39.                      final JScrollPane pane = new JScrollPane(lab);
  40.                      mappings.put (value, pane);
  41.                      return pane;
  42.  
  43.                  } catch (IOException e) {
  44.                      e.printStackTrace();
  45.                  }
  46.                  return new JLabel ("Error. cannot renderer html file ");
  47.              }
  48.          }
  49.  );
  50.  fruits.setMaximumRowCount(5);
  51.  fruits.setPreferredSize(new Dimension(200 , 50));
  52.  
  53.  final JLabel lab_info = new JLabel("Выберите из списка файл и увидите его содержимое");
  54.  jf.getContentPane().add (new JScrollPane(lab_info), BorderLayout.CENTER);
  55.  
  56.  
  57.  fruits.addActionListener(
  58.          // обработчик события - был выбран элемент списка
  59.          // значит нужно прочитать файл и отобразить его внизу после падающего списка
  60.          new ActionListener() {
  61.              public void actionPerformed(ActionEvent e) {
  62.                  try {
  63.                      File f = (File) fruits.getSelectedItem();
  64.                      BufferedReader brin = new BufferedReader (new FileReader(f));
  65.                      char [] data = new char [(int) f.length()];
  66.                      int cnt = brin.read(data);
  67.                      String html = new String (data, 0 , cnt).trim();
  68.                      lab_info.setText (html);
  69.                      jf.pack ();
  70.                  } catch (IOException e1) {
  71.                      e1.printStackTrace();
  72.                  }
  73.  
  74.              }
  75.          }
  76.  );
  77.  
  78.  jf.pack ();
  79.  jf.setVisible(true);

Редакторы



Концепция редакторов основана на том, что информация, которую мы отображаем с помощью некоторого элемента управления должна редактироваться. Часто, при редактировании, ввод информации довольно сложен, требует проверок, организации интерфейса зависимого от действий пользователя. Например, в списке отображаются файлы и вы хотите изменить некоторый файл. Вы выполняете двойной клик на элементе списка и элемент изменяется, например, появляется панель, с переключателями: выбрать файл на диске или создать новый пустой файл на основании буфера обмена и т.д. В общем, когда нужно редактировать отображаемую информацию в элементе управления, то этот элемент создает класс <редактор> и отображает его. В качестве примера далее приводится форма, в которой есть список элементов (кодирующих цвет) при клике на кнопке <изменить цвет> появляется панель выбора цвета.


  1. JFrame.setDefaultLookAndFeelDecorated(true);
  2. final JFrame jf = new JFrame("D&D 2");
  3. jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE
  4. );
  5. // создаем модель данных, в которой будут находиться цвета.
  6. final DefaultComboBoxModel dataModel = new DefaultComboBoxModel();
  7. // создаем падающий список на базе данной модели
  8. final JComboBox fruits = new JComboBox(dataModel);
  9. // объект Random служит для генерации случайных чисел
  10. Random r = new Random();
  11. for (int i = 0; i < 10; i++) {
  12.     // заполняем модель случайными цветами
  13.     dataModel.addElement(new Color(Math.abs(r.nextInt()) % 255,
  14.             Math.abs(r.nextInt() % 255),
  15.             Math.abs(r.nextInt() % 255)));
  16. }
  17. fruits.setRenderer(new ListCellRenderer() {
  18.     // устанавливаем объект рендерер
  19.     JLabel jLabel = new JLabel();
  20.  
  21.     // естественно, что необходимо организовать
  22.     // повторное использование компонента JLabel
  23.     public Component getListCellRendererComponent(
  24.             JList list, Object value, int index, boolean isSelected, boolean cellHasFocus
  25.     ) {
  26.         // когда нас просят вернуть графическое представление объекта - цвета,
  27.         // то возвращается объект Jlabel с надписью кодирующей цвет
  28.         // и цветом фона установленным в значение объекта
  29.         Color c = (Color) value;
  30.         jLabel.setText("red:" + c.getRed() + "; green: " + c.getGreen() + "; blue: " + c.getBlue());
  31.         // здесь устанавливаем цвет
  32.         jLabel.setForeground(c);
  33.         return jLabel;
  34.     }
  35. });
  36. // здесь мы разрешаем редактировать содержимое списка
  37. fruits.setEditable(true);
  38. fruits.setEditor(new ComboBoxEditor() {
  39.     // добавляем новый класс редактор
  40.     Color c = Color.WHITE;
  41.     JColorChooser jch = new JColorChooser(c);
  42.     // класс панели для выбора цвета
  43.  
  44.     public Component getEditorComponent() {
  45.         // данный метод вызывается, когда необходимо создать компонент, который будет играть роль редактора
  46.         final JButton btn_act = new JButton("Изменить цвет");
  47.         // создаем кнопку, при нажатии на которую сработает слушатель события
  48.         // и будет показано всплывающее диалоговое окно выбора цвета
  49.  
  50.         btn_act.addActionListener(new ActionListener() {
  51.             public void actionPerformed(ActionEvent e) {
  52.                 // при нажатии на кнопку мы получаем текущий цвет c.
  53.                 // далее в методах setItem
  54.                 jch.getSelectionModel().setSelectedColor(c);
  55.                 // обращаемся к классу панели выбора цвета и устанавливаем данный цвет как текущий
  56.                 JColorChooser.createDialog(btn_act,
  57.                         // создаем диалоговое окно с панелью выбора цвета
  58.                         "Новый цвет", true, jch,
  59.                         new ActionListener() {
  60.                             // важно здесь создается обработчки события OK
  61.  
  62.                             public void actionPerformed(ActionEvent e) {
  63.                                 int p = dataModel.getIndexOf(c);
  64.                                 dataModel.removeElementAt(p);
  65.                                 c = jch.getSelectionModel().getSelectedColor();
  66.                                 dataModel.insertElementAt(c, p);
  67.                             }
  68.                         }, /* Здесь можно было бы задать обработчик события CANCEL*/ null)
  69.                         .setVisible(true);
  70.                 // и коказываем данное окно
  71.  
  72.             }
  73.         });
  74.         return btn_act;
  75.     }
  76.  
  77.     public void setItem(Object anObject) {
  78.         c = (Color) anObject;
  79.     }
  80.  
  81.     public Object getItem() {
  82.         return c;
  83.     }
  84.  
  85.     public void selectAll() {}
  86.  
  87.     public void addActionListener(ActionListener l) {}
  88.  
  89.     public void removeActionListener(ActionListener l) {}
  90. });
  91. fruits.setPreferredSize(new Dimension(200, 50));
  92. jf.getContentPane().add(fruits);
  93. jf.pack();
  94. jf.setVisible(true);
Здесь мы столкнулись с объектом JcolorChooser. Данный компонент предназначен для создания панели выбора цвета. Данная панель может быть встроена в любое диалоговое окно как его составная часть. Вы можете создать окно, на котором расположите любое количество этих окон выбора цвета, или же вы можете создать диалоговое окно для выбора цвета.

Итак, существую три способа использования окна выбора цвета:

1. Используя статический вызов метода вы отображаете модальное диалоговое окно. В качестве результата вызова функции вам возвращается выбранный пользователем цвет.

2. Снова используя статический метод вы создаете диалоговое окно как и в предыдущем случае. Но здесь окно более активно и вы можете установить обработчики событий для нажатия на кнопки OK и CANCEL.

3. Создается компоненет JcolorChooser, помещается внутрь некоторой панели и устанавливается обработчик события для реакции на то, когда пользователь будет изменять цвет в компоненте.
  1. public static void main(String[] args) {
  2.     Color nova_color = JColorChooser.showDialog(null, "Выберите цвет", Color.RED);
  3.     // первый способ -  самый простой
  4.     System.out.println("Был выбран цвет : " + nova_color);
  5.  
  6.     final JColorChooser chooser = new JColorChooser();
  7.     // эта переменная потребуется для двух последующих способов
  8.     ActionListener Act_CANCEL = new ActionListener() {
  9.         // создаем обработчики событий для кнопки OK
  10.         public void actionPerformed(ActionEvent e) {
  11.             System.out.println("Пользователь нажал отмена");
  12.         }
  13.     };
  14.     ActionListener Act_OK = new ActionListener() {
  15.         // тоже обработчик события но для нажатия кнопки CANCEL
  16.         public void actionPerformed(ActionEvent e) {
  17.             System.out.println("Пользователь нажал OK и выбрал цвет: " + chooser.getColor());
  18.         }
  19.     };
  20.  
  21.     JColorChooser.createDialog(null,
  22.             "Выберит цвет",
  23.             true,
  24.             chooser,
  25.             Act_OK,
  26.             Act_CANCEL).setVisible(true);
  27.     // создаем диалоговое окно и сразу же его показываем
  28.  
  29.     chooser.getSelectionModel().addChangeListener(
  30.             // добавляе слушателя событий для модели
  31.             new ChangeListener() {
  32.                 // данный метод будет вызываться при любом изменении текущего цвета
  33.                 public void stateChanged(ChangeEvent e) {
  34.                     System.out.println("Был выбран новый цвет: " + chooser.getColor());
  35.                 }
  36.             }
  37.     );
  38.     JFrame jf = new JFrame("Выбери цвет");
  39.     jf.getContentPane().add(chooser);
  40.     // добавляем на форму панель выбора цвета
  41.     jf.setVisible(true);
  42.     // и показываем форму
  43. }

Окна сообщений



Для того чтобы вывести на экран диалоговое окно с сообщением следует использовать статические методы класса JoptionPane:



Примечание. Для того, чтобы создать окно сообщения используйте метод showMessageDialog. Для создания окна подтверждения некоторого утверждения - showConfirmDialog. И если вам нужно окно для выбора или ввода значения используйте showInputDialog.

Все эти методы получают в качестве первого параметра ссылку на компонент, который будет являтья владельцем появляющегося окна (в примере я его не задавал). Затем идет сообщение, заголовок, обычно указание на то, какие кнопки будут доступны, затем стиль окна (внешний вид иконки, в отдельных ситациях возможно задать пользовательскую иконку).
  1. public static void main(String[] args) {
  2.     JOptionPane.showMessageDialog(
  3.             null,
  4.             "Текущее время : " + new Date(),
  5.             "Система извещений о текущем времени",
  6.             JOptionPane.YES_NO_CANCEL_OPTION,
  7.             // разумеется раз это окно сообщения,
  8.             //то варианты доступных кнопок для закрытия окна не могут отличаться от MB_OK
  9.             new ImageIcon("C:\\tmp\\comp.png")
  10.     );
  11.  
  12.     SimpleDateFormat sdf = new SimpleDateFormat("EEEE", new Locale("RU"));
  13.     int r = JOptionPane.showConfirmDialog(
  14.             null,
  15.             "Вы точно уверены, что сегодня " + sdf.format(new Date()),
  16.             "Подтвердение сведений о текущем дне недели",
  17.             JOptionPane.YES_NO_CANCEL_OPTION,
  18.             JOptionPane.INFORMATION_MESSAGE
  19.     );
  20.     if (r == JOptionPane.YES_OPTION)
  21.         JOptionPane.showMessageDialog(null, "Подтверждение получено");
  22.  
  23.  
  24.     Object ob = JOptionPane.showInputDialog(
  25.             null, "Введите четное число", "Окно для ввода четных чисел",
  26.             JOptionPane.INFORMATION_MESSAGE, new ImageIcon("C:\\tmp\\spic2.jpg"),
  27.             new Integer[]{new Integer(12), new Integer(13), new Integer(14)},
  28.             new Integer(12));
  29.     JOptionPane.showMessageDialog(null, ob);
  30. }
Возможные значения опций кнопок закрытия окна Возможные опции стиля иконки. В последнем случае без иконки.
  1. JOptionPane.YES_OPTION;
  2. JOptionPane.YES_NO_OPTION;
  3. JOptionPane.YES_NO_CANCEL_OPTION;
  4. JOptionPane.NO_OPTION;
  5. JOptionPane.CANCEL_OPTION;
  1. JOptionPane.INFORMATION_MESSAGE;
  2. JOptionPane.ERROR_MESSAGE;
  3. JOptionPane.QUESTION_MESSAGE;
  4. JOptionPane.PLAIN_MESSAGE;

Элементы управления для работы с текстом



Для представления текста в swing существует несколько компонентов разделяющихся по возможностям. От простейшего однострочного редактора, до текстовой области, в которую можно вводить текст с форматированием и т.д.
Компонент Назначение
JTextField Однострочная область ввода текста. При завершении ввода и нажатии на генерируется события для слушателя.
JFormattedTextField Текстовое поле, при вводе в которое можно задать маску или шаблон, которому должны соответствовать вводимые символы.
JPasswordField Текстовая область для ввода пароля. Вводимые символы закрываются символами подстановки.
В примере дальше создается форма для ввода имени пользователя, пароля и номера телефона по которому он хочет позвонить.

Особое внимание обратите на то, как я создаю объект JformattedTextField. В качестве параметра для его конструктора я передаю ссылку на объект, выполняющий форматирование и контроль за корректностью ввода значения в поле. Фактически, вы можете создать собственный класс, расширяющий интерфейс AbstractFormatter, и реализовать любую сколь угодно сложную проверку. Также вы можете воспользоваться специальными классами-адаптерами. Одним из которых является Formatter. Этот класс получает при создании маску для ограничения ввода значений согласно шаблону. Шаблон состоит из спец. символов согласно нижеприведенной таблице.
Символ Назначение
# Любой символ, представляющий цифру
' Данный символ является Escape-символом и служит для отмены специального значения других символов форматирования.
U Любой символ представляющий букву (с преобразованием к верхнему регистру)
L То же, что и выше, но преобразование идет к нижнему регистру
A Любой символ, представляющий собой букву или цифру
? Любой символ
* Все, что угодно.
H Любой hex символ (0-9, a-f или A-F)
  1. JFrame jf = new JFrame("Nova ATS");
  2. // это обычное текстовое поле без каких либо ограничений на ввод информации
  3. final JTextField uname = new JTextField("укажите имя пользователя");
  4.  
  5. // это поле для ввода паролей, все вводимые символы будут закрыты значком '@'
  6. JPasswordField upass = new JPasswordField();
  7. upass.setEchoChar('@');
  8.  
  9. // ввод телефон ограничен маской
  10. MaskFormatter mf;
  11. final JFormattedTextField phone = new JFormattedTextField(
  12.         mf = new MaskFormatter("(###)-###-##-## (UUU)")
  13. );
  14. mf.setPlaceholderCharacter('_');
  15.  
  16. JPanel jp_top = new JPanel(new GridLayout(3, 2));
  17. jp_top.add(new JLabel("Укажите имя пользователя"));
  18. jp_top.add(uname);
  19. jp_top.add(new JLabel("Укажите пароль"));
  20. jp_top.add(upass);
  21. jp_top.add(new JLabel("Укажите телефон"));
  22. jp_top.add(phone);
  23.  
  24. phone.addActionListener(
  25.         new ActionListener() {
  26.             public void actionPerformed(ActionEvent e) {
  27.                 JOptionPane.showMessageDialog(null, "Пользователь: " + uname.getText() +
  28.                         " Звонит по номеру : " + phone.getText());
  29.             }
  30.         }
  31. );
  32.  
  33. jf.getContentPane().add(jp_top, BorderLayout.CENTER);
  34. jf.pack();
  35. jf.setVisible(true);


Categories: Java