Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Списки

325

Прежде всего стоит отметить, что модель ComboBoxModel, как и модель обычного списка ListModel, очень проста. Раскрывающийся список имеет много общего со списком обычным (он также хранит некоторую последовательность элементов, правда, в отличие от обычного списка, выбирать можно только один элемент), так что во избежание лишней работы создатели раскрывающегося списка решили унаследовать модель ComboBoxModel от модели обычного списка ListModel. Если вспомнить модель ListModel, то у нас в распоряжении оказывается четыре унаследованных метода: два для добавления и удаления заинтересованных в изменениях модели слушателей, один для получения элемента на некоторой позиции и еще один для получения количества хранящихся в списке элементов. Кроме того, в интерфейсе ComboBoxModel появилось пара новых методов: getSelectedItem() — для получения выбранного в данный момент элемента иsetSelectedItem() — для смены выбранного элемента. У раскрывающегося списка JComboBox нет отдельной модели выделения (было бы слишком расточительно и сложно использовать отдельную модель для единственного выбранного элемента), так что информацию о выделенном элементе раскрывающийся список с помощью двух новых методов сохраняет в модели ComboBoxModel вместе с информацией обо всех своих элементах.

Создавать модель вы можете не только с помощью интерфейса ComboBoxModel, помимо него раскрывающийся список может использовать модель MutableComboBoxModel. Она унаследована от обычной модели ComboBoxModel и дополнительно описывает четыре метода, позволяющие динамически изменять содержимое раскрывающегося списка. С помощью этих методов вы сможете добавлять новые элементы (к концу списка или на произвольные позиции) или удалять уже имеющиеся элементы7.

Прежде чем начать обдумывать, как реализовать собственную модель для раскрывающегося списка, вы можете обратить внимание на стандартную модель DefaultComboBoxModel. Она унаследована от вспомогательного класса AbstractListModel, позволяющего упростить создание моделей для обычных списков JList, и реализует интерфейс MutableComboBoxModel. В качестве хранилища для элементов списка в ней используется динамический массив (вектор) Vector8. Стандартная модель предоставляет вам соответствующий набор методов, так что вы практически не заметите разницы между работой с ней и с динамическим массивом, а все достоинства модели останутся между тем с вами. Давайте рассмотрим небольшой пример, в котором применим стандартную модель раскрывающегося списка:

//UsingComboBoxModel.java

//Использование стандартной модели раскрывающихся списков import javax.swing.*;

import java.awt.event.*; import java.awt.*;

public class UsingComboBoxModel extends JFrame {

7Появление для раскрывающегося списка отдельной «изменяемой» модели довольно странно

иконтрастирует с обычным списком JList, который обладает лишь одной «неизменяемой» моделью. Логичнее было бы предположить, что свойство «изменяемости» находится во власти конкретной модели, как это и было со списком JList. Похоже, что классы JComboBox и JList писали разные

люди, и это впечатление только усиливается при взгляде на методы этих классов. Нам же от этого только сложнее знакомиться с очень похожими классами, работа с которыми «по идее» должна быть почти одинаковой.

8 Да-да, библиотека Swing так до сих пор и не удосужилась перейти на новую библиотеку коллекций, появившуюся в Java 2, и повсюду все еще используют часто становившийся объектом для нападок и споров довольно медленный динамический массив Vector. К счастью, это один из немногих очевидных просчетов создателей Swing.

326

ГЛАВА 11

// наша стандартная модель private DefaultComboBoxModel cbm; public UsingComboBoxModel() {

super("UsingComboBoxModel"); setDefaultCloseOperation(EXIT_ON_CLOSE);

//создаем стандартную модель и наполняем ее данными cbm = new DefaultComboBoxModel();

for (int i=0; i<10; i++) { cbm.addElement("Элемент списка №: " + i);

}

//сменяем выбранный элемент cbm.setSelectedItem(cbm.getElementAt(4));

//список на основе нашей модели

JComboBox combo = new JComboBox(cbm); combo.setMaximumRowCount(5);

//стандартная модель позволяет

//динамически манипулировать данными

JButton add = new JButton("Добавить"); add.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) { // случайно выбираем позицию

int pos = (int) (Math.random()*cbm.getSize()); cbm.insertElementAt("Новинка!", pos);

}

});

//добавляем список и кнопку в панель setLayout(new FlowLayout()); add(combo);

add(add);

//выводим окно на экран

setSize(400, 200); setVisible(true);

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new UsingComboBoxModel(); } });

}

}

Вприведенном примере в окно добавляется раскрывающийся список JComboBox

ипростая кнопка JButton. Раскрывающийся список создается на основе стандартной модели DefaultComboBoxModel, которую мы создаем и наполняем данными еще до при-

Списки

327

соединения к раскрывающемуся списку. Как вы помните, это общее правило для всех стандартных моделей (чаще всего использующих для хранения данных динамические массивы Vector): основную часть данных следует добавлять в модель до присоединения к виду (или нескольким видам), потому что после присоединения модель начинает оповещать вид о каждом изменении в своих данных, что может значительно замедлить работу приложения, особенно если вид уже выведен на экран. Итак, мы наполняем модель элементами в цикле, используя для этого метод addElement() — полный аналог одноименного метода класса Vector. Заметьте, как модель позволяет сменить выбранный элемент с помощью метода setSelectedItem(). Правда, использовать его нужно немного необычно: метод требует передавать ему не позицию, а сам элемент (это необходимо для поддержки редактируемых списков, выбранным элементом в которых может быть любое значение). Поэтому нам приходится сначала получить элемент на нужной позиции (с помощью все той же модели), а затем только выбрать его. После этого мы присоединяем модель к списку JComboBox, передавая ее в конструктор (то же самое можно было бы сделать методом setModel()). Для удобства работы со списком (длинные выпадающие меню довольно неудобны, особенно если им не хватает места на экране) мы ограничили количество видимых на экране элементов меню пятью, изменив уже известное нам свойство maximumRowCount.

Для демонстрации динамической работы с нашей стандартной моделью мы добавили в окно кнопку add, слушатель ActionListener которой при щелчке на кнопке вводит

вмодель новый элемент, причем на случайную позицию, так что вы точно будете знать, что изменения происходят динамически. Запустив приложение, вы сможете увидеть, как стандартная модель поставляет данные раскрывающемуся списку и при этом позволяет без особых трудностей динамически изменять его содержимое. Созданную модель вы сможете разделять между несколькими видами и отдельно обрабатывать хранящиеся

вней данные.

Раскрывающийся список хранит довольно простые данные, и к тому же ему не нужна отдельная модель выделения (выделенный элемент хранится вместе с остальными элементами списка), так что талантов стандартной модели вполне хватает для большинства ситуаций. Создавать собственную модель стоит, только когда данные хранятся в особом источнике, требующем особого подхода, например, когда возможна некоторая оптимизация при получении и обработке данных. Мы уже создали для обычного списка JList модель, работающую с базами данных JDBC, давайте сделаем то же самое и для раскрывающегося списка JComboBox. Такая модель на самом деле будет очень полезна и может стать многократно использующимся элементом любого приложения: как только вам понадобится наполнить раскрывающийся список элементами из базы данных, вы сможете сделать это буквально парой строк кода. Удивительно, но создатели раскрывающегося списка не удосужились обеспечить нас абстрактным классом с поддержкой слушателей вроде AbstractComboBoxModel, хотя такие классы есть для всех остальных компонентов

328

ГЛАВА 11

с моделями. Создавать «модель» с нуля излишне сложно, вместо этого унаследуем ее от стандартной модели DefaultComboBoxModel, определив нужные нам методы. (Еще одним вариантом, если вы не хотите использовать стандартную модель, может быть наследование от базового класса модели списка AbstractListModel вместе с реализацией интерфейса модели выпадающего списка.) Давайте попробуем:

//com/porty/swing/DatabaseComboBoxModel.java

//Модель раскрывающегося списка для работы с базами данных package com.porty.swing;

import javax.swing.*; import java.sql.*;

public class DatabaseComboBoxModel extends DefaultComboBoxModel {

// получение данных из запроса ResultSet

public void setDataSource(ResultSet rs, int column) throws SQLException {

//очистим список removeAllElements();

//добавим новые элементы из базы данных while ( rs.next() ) {

//получаем строки из столбца column

addElement(rs.getString(column));

}

}

}

Здесь мы пошли по пути наименьшего сопротивления, максимально опираясь на уже имеющиеся возможности стандартной модели DefaultComboBoxModel. Мы не стали менять хранилище для данных и переопределять методы модели, а просто добавили новый метод setDataSource(), который получает результат запроса к базе данных ResultSet, удаляет все имеющиеся в модели элементы и заполняет ее новыми элементами из базы данных, получая их из столбца с определенным номером (его нужно указать в качестве второго параметра того же метода setDataSource()). Благодаря максимальному использованию механизмов стандартной модели, все делается за нас: при добавлении новых элементов методом addElement() модель будет оповещать об этом присоединенных слушателей, так что пользователь сможет своими глазами увидеть, как происходит заполнение раскрывающегося списка. Заметьте, что класс новой модели мы разместили в пакете com.porty. swing, откуда его проще извлечь.

Проверим работу нашей новой модели. Давайте напишем небольшое приложение, которое присоединяется к некоторой базе данных JDBC и полученные из нее данные показывает в раскрывающемся списке JComboBox. Вот что получается:

//DatabaseComboBox.java

//Пример использования модели DatabaseComboBoxModel import javax.swing.*;

import com.porty.swing.DatabaseComboBoxModel;

Списки

329

import java.sql.*; import java.awt.*;

public class DatabaseComboBox extends JFrame {

//параметры подключения к базе данных private static String

dsn = "jdbc:odbc:Library", uid = "",

pwd = "";

//наша модель

private DatabaseComboBoxModel cbm; public DatabaseComboBox() {

super("DatabaseComboBox"); setDefaultCloseOperation(EXIT_ON_CLOSE); // настраиваем соединение с базой данных

Connection conn = null; try {

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // объект-соединение с базой данных

conn = DriverManager.getConnection(dsn, uid, pwd); Statement st = conn.createStatement();

ResultSet rs = st.executeQuery( "select * from readers.csv");

// передаем данные в модель

cbm = new DatabaseComboBoxModel(); cbm.setDataSource(rs, 2); rs.close();

}catch (Exception ex) {

throw new RuntimeException(ex);

}

//присоединяем модель к списку setLayout(new FlowLayout()); add(new JComboBox(cbm));

//выводим окно на экран setSize(300, 200); setVisible(true);

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new DatabaseComboBox(); } });

Источник: https://studfile.net/preview/15935977/