Хочу поделиться недавним опытом разработки апплетов на Swing и рассказать про подводные камни, приемы найденные и использованные в процессе работы.
Если вы уже имели дело с библиотекой Swing, то можете сразу переходить ко второй главе.
Три шага для быстрого старта
Для начала внимательно изучаем документацию по различным Layout менеджерам, без таких базовых знаний будет сложно добиться желаемого отображения.
Затем внимательно изучаем базовые визуальные компоненты java look and feel или windows look and feel. Как и что использовать в работе можно подсмотреть в учебнике.
Ставим Eclipse и создаем java проект, создаем апплет и ...«дальше напильником»(с).
Для быстрого старта и построения простых интерфейсов этих знаний будет вполне достаточно.
Теперь переходим к самому интересному.
Десять полезных простых вещей
1. Для того чтобы выставить look and feel как в системе (например соответствующий теме windows):
javax.swing.UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
2. Меняем шрифт заданный по умолчанию:
FontUIResource f = new FontUIResource(new Font("Verdana", 0, 12);
Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof FontUIResource) {
FontUIResource orig = (FontUIResource) value;
Font font = new Font(f.getFontName(), orig.getStyle(), f.getSize());
UIManager.put(key, new FontUIResource(font));
}
}
3. Простой вариант модального диалога:
int reply = JOptionPane.showConfirmDialog(null, "Is Hello world?", "Title", JOptionPane.YES_NO_OPTION);
if (reply == JOptionPane.YES_OPTION){
//do something
}
Если требуется более продвинутый диалог, есть вариант с возможностью добавления JComponent:
JComponent[] inputs //Массив отображаемых компонет
int reply = JOptionPane.showConfirmDialog(null, inputs, "Заголовок", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
if (reply == JOptionPane.OK_OPTION) {
...
4. Для быстрой локализации диалогов:
UIManager.put("OptionPane.yesButtonText", "Да");
UIManager.put("OptionPane.noButtonText", "Нет");
UIManager.put("OptionPane.cancelButtonText", "Отмена");
UIManager.put("OptionPane.okButtonText", "Готово");
5. Для фильтрации элементов в колонках таблицы JTable есть очень хорошая открытая библиотека Swing Bits с помощью которой можно сделать фильтр «как в Экселе».
6. Для того, чтобы дать пользователю возможность выбирать даты есть отличный компонент jcalendar
7. Для проигрывания звука упакованного в jar архив:
myapplet.getAudioClip(MyPlayerClass.class.getResource("путь и имя ресурса для проигрывания").play();
Теперь немного нетривиальных фишек:
8. Для того чтобы встроить в строку таблицы кнопку (с правильным рендером клика):
public class MyTable extends JTable {
public class MyButtonRenderer extends JButton implements TableCellRenderer, TableCellEditor{
private int selectedRow;
private int selectedColumn;
private final List listener = new ArrayList();
public MyButtonRenderer() {
super();
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (MyButtonListener l : listener) {
l.tableButtonClicked(selectedRow, selectedColumn);
//сообщаем всем слушателям о событии
}
}
});
}
public Component getTableCellRendererComponent(JTable a, Object b, boolean c, boolean d, int e, int f) {
setFocusable(false);
return this;
}
public boolean stopCellEditing() {return true;}
public Object getCellEditorValue() { return null;}
public boolean isCellEditable(EventObject anEvent) { return true;}
public boolean shouldSelectCell(EventObject anEvent) {return true;}
public Component getTableCellEditorComponent(JTable a, Object b, boolean c, int d, int e) {
selectedRow = row;
selectedColumn = column;
setFocusable(false);
return this;
}
public void addTableButtonListener(ITableButtonListener l) {listener.add(l);}
public void removeTableButtonListener(ITableButtonListener l) {listener.remove(l);}
}
public MyTable(){
MyButtonRenderer button = new MyButtonRenderer();
button.addTableButtonListener(new MyButtonListenerImpl());
//Задаем рендер "кнопка"для первой колонки таблицы. Важно сделать 2 разных рендера для просмотра и редактирования
this.getColumnModel().getColumn(0).setCellRenderer(new MyButtonRenderer());
this.getColumnModel().getColumn(0).setCellEditor(button);
}
public TableCellRenderer getCellRenderer(int row, int column) {
TableCellRenderer result = super.getCellRenderer(row, column);
if (result instanceof MyButtonRenderer)
return result;
return ;
}
}
9. Для того, чтобы изменить компоненты swing из другого потока нужно использовать посредника:
class MyWorker extends SwingWorker{
private IProxyObject action;
protected SwingWorkerReal(MyObject data){
this.action = data;
}
protected Void doInBackground() throws Exception {
if (action != null)
publish(action);
return null;
}
protected void process(List chunks) {
//Все обращения к визульном компоненте нужно делать в process!
}
}
...
new MyWorker(data).execute(); //запускаем посредника
...
}
10. И наконец фишка про SwingWorker. В один момент времени может выполняться всего десять SwingWorker потоков, связано это с тем, что пул потоков обработчика SwingWorker имеет максимальный размер десять, так что старайтесь чтобы задачи были не большие. Пример из жизни: в IE можно открыть 10 страниц с апплетами, а 11 будет уже ждать(«висеть») пока осводобится место в ThreadPoolExecutor для обработки данных полученных с сервера в другом потоке.
Послесловие
В целом разработка под swing парадовала обилием документации и примеров на все случаи жизни. Если интересно могу еще в том же духе поподробней рассказать про фишки JTable.