
Хочу поделиться недавним опытом разработки апплетов на 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<Object> 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<MyButtonListener> listener = new ArrayList<MyButtonListener>();
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<Void, MyObject>{
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<MyObject> chunks) {
//Все обращения к визульном компоненте нужно делать в process!
}
}
...
new MyWorker(data).execute(); //запускаем посредника
...
}
10. И наконец фишка про SwingWorker. В один момент времени может выполняться всего десять SwingWorker потоков, связано это с тем, что пул потоков обработчика SwingWorker имеет максимальный размер десять, так что старайтесь чтобы задачи были не большие. Пример из жизни: в IE можно открыть 10 страниц с апплетами, а 11 будет уже ждать(«висеть») пока осводобится место в ThreadPoolExecutor для обработки данных полученных с сервера в другом потоке.
Послесловие
В целом разработка под swing парадовала обилием документации и примеров на все случаи жизни. Если интересно могу еще в том же духе поподробней рассказать про фишки JTable.