
Хочу поделиться недавним опытом разработки апплетов на 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.
