| « Про java swing - часть 1 | Внедрение flash-видео и swf-файлов внутрь medawiki сайта » |
Про java swing - часть 2
Обработка событий
Любой элемент управления генерирует события при воздействии на него пользователя или при наступлении внутренних изменений. В обработке событий иделогия java прошла через три стадии развития. Если кратко представить первые две модели, то они подобны тем, которые приняты в библиотеке VCL. Т.е. мы создаваем класс-наследник от некоторого элемента управления, а затем перекрываем методы обработчики событий. Например, так в старой модели выглядело бы закрытие формы.
Самая первая модель
public static void main(String[] args) {
new JFrame("Old Events Model") {
public boolean handleEvent(Event evt) {
if (evt.id == Event.WINDOW_DESTROY) {
System.exit(0);
return true;
} else
return super.handleEvent(evt);
}}.setVisible(true);
}
Вторая, но тоже устаревшая, модель
new JFrame("Old Events Model") {
protected void processWindowEvent(WindowEvent evt) {
if (evt.getID() == Event.WINDOW_DESTROY) {
System.exit(0);
}}}.setVisible(true);
// есть класс, который умеет складывать числа (накапливать их в какой-то внутренней переменной)class Summator {
ArrayList listeners = new ArrayList();
// некий массив, список, коллекция в которой хранится множество объектов,// поддерживающих интерфейс слушателя (надо было бы здесь использовать generics)// метод добавляющий объект-слушательpublic void addSumEventListener (ISumReachedListener l){
// перед добавлением слушателя очень неплохо проверить, что такого объекта// еще нет в списке добавленных слушателейif (!listeners.contains(l))
listeners.add (l);
}// метод удаляющий из очереди подписчиков объект-слушательpublic void removeSumEventListener (ISumReachedListener l){
listeners.remove (l);
}// переменная нужная для работы сумматора. В ней накапливатсяint current_sum = 0;
// получаем значение текущей суммы - в лучших традициях,// доступ к внутренним полям класса должен быть закрытpublic int getCurrentSum() {
return current_sum;
}// добавляем к сумме очередное число, и если лимит в 1000 был превышен, то генерируем событиеpublic void addNumber (int num){
current_sum+=num;
if (current_sum > 1000)
fireSumWasReachedEvent ();
}// служебная функция выполняющая отправку всем находящимся в коллекции// объектам-подписчикам извещения об наступлении событияprivate void fireSumWasReachedEvent() {
for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
ISumReachedListener iSumReachedListener = (ISumReachedListener) iterator.next();
iSumReachedListener.onSumWasReached(this);
}}}// интерфейс, который должны поддерживать все объекты, заинтересованные в получении извещенийinterface ISumReachedListener {
void onSumWasReached (Summator sum);
}// пример кода тестирующего класс сумматорpublic class TestF {
public static void main(String[] args) {
// я создаю анонимный класс реализующий интерфейс 'слушающего' ISumReachedListenerISumReachedListener l1 = new ISumReachedListener() {
public void onSumWasReached(Summator sum) {
// когда наступает событие-достигнута сумма в 1000 $System.out.println ("Sum Was Reached: Listener # 1: " + sum.getCurrentSum());
}};
// количество классов подписчиков является неограниченнымISumReachedListener l2 = new ISumReachedListener() {
public void onSumWasReached(Summator sum) {
System.out.println ("Sum Was Reached: Listener # 2 "+ sum.getCurrentSum());
}};
// создали класс сумматор, генерирующий событияSummator s = new Summator();
// привязали к нему два заинтересованных подписчикаs.addSumEventListener(l1);
s.addSumEventListener(l2);
// один из подписчиков решил что ему более не нужны извещения об работе сумматораs.removeSumEventListener(l1);
// тест на повторное добавление объекта-слушателяs.addSumEventListener(l2);
// затем выполняется генерация в цикле случайных чисел и после этогоRandom random = new Random();
for (int i = 0; i < 100; i++) {
int num = Math.abs(random.nextInt()) % 100;
s.addNumber(num);
}}}
Теперь пример того как можно реагировать на события изменения размеров и закрытия окна.
public class TWNow {
public static void main(String[] args) {
// создаем окноJFrame jf = new JFrame("Nova Events Model");
Toolkit tk = jf.getToolkit();
// определяем поддерживает ли оконный менеджер (ну java типа работает под разными OС,и типа разными оконными менеджерами)if (!(tk.isFrameStateSupported(Frame.ICONIFIED))) {
System.out.println("Your window manager doesn't support ICONIFIED.");
}if (!(tk.isFrameStateSupported(Frame.MAXIMIZED_VERT))) {
System.out.println("Your window manager doesn't support MAXIMIZED_VERT.");
}if (!(tk.isFrameStateSupported(Frame.MAXIMIZED_HORIZ))) {
System.out.println("Your window manager doesn't support MAXIMIZED_HORIZ.");
}if (!(tk.isFrameStateSupported(Frame.MAXIMIZED_BOTH))) {
System.out.println("Your window manager doesn't support MAXIMIZED_BOTH.");
} else {
System.out.println("Your window manager supports MAXIMIZED_BOTH.");
}// добавляем обработчик события - класс который слушает интерфейс WindowStateListener// будет получать извещения о том, что окно было минимизировано, максимизировано, ...// для этого вызывается функция windowStateChanged, во входной параметре которой передается вся// дополнительная информация что же случилосьjf.addWindowStateListener(new WindowStateListener() {
String convertStateToString(int state) {
if (state == Frame.NORMAL) {
return "NORMAL";
}if ((state & Frame.ICONIFIED) != 0) {
return "ICONIFIED";
}if ((state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) {
return "MAXIMIZED_BOTH";
}if ((state & Frame.MAXIMIZED_VERT) != 0) {
return "MAXIMIZED_VERT";
}if ((state & Frame.MAXIMIZED_HORIZ) != 0) {
return "MAXIMIZED_HORIZ";
}return "UNKNOWN";
}public void windowStateChanged(WindowEvent e) {
int state = e.getNewState();
int oldState = e.getOldState();
String msg = "New state: "
+ convertStateToString(state) + "\n"+
"Old state: " + convertStateToString(oldState);
System.out.println(msg);
}});
jf.setVisible(true);// это конечно не самый лучший способ показать окно и далее я скажу почему
}}
<h3> Обработка событий изменения состояния окна </h3>
jf.addWindowListener(
new WindowListener() {
public void windowOpened(WindowEvent e) {}
public void windowClosing(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
});
Обработка события получения окном фокуса или его потери
jf.addWindowFocusListener(
new WindowFocusListener() {
public void windowGainedFocus(WindowEvent e) {}
public void windowLostFocus(WindowEvent e) {}
});
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.out.println ("Close Frame: " + e);
System.exit (0);
}});
public class BooRamka {
// создаем и запускам экземляр класса строящего графический интерфейсpublic static void main(String[] args) {
new BooRamka().go ();
}// признак того что сейчас рисуется рамкаboolean is_started = false;
// начальная точка (левый-верхний угол) рамкиPoint start_point = new Point(0,0);
Point old_point = new Point();
// функия создающая окно и добавляющая к нему функцию рисования рамки и реакцию на перемещение мышиprivate void go() {
final JFrame jf = new JFrame("Selection Example"){
public void paint(Graphics g) {
super.paint(g);
// рисование очень простое - рисуем прямоугольники с рамкой синего цветаg.setColor(Color.BLUE);
for (int i = 0; i < 10; i++)
g.drawRect(25*i , 25*i , 50 , 50);
}};
// добавляем обработчик события для нажатия и освобождения кнопки мышиjf.addMouseListener(
new MouseAdapter() {
// когда кнопку нажали, то очевидно, что начато рисование зеленого прямоугольника// рамки выделенияpublic void mousePressed(MouseEvent e) {
is_started = true;
start_point = e.getPoint();
old_point = start_point;
}// рисование рамки завершено, тогда когда кнопка мыши была отпущенаpublic void mouseReleased(MouseEvent e) {
is_started = false;
}});
// добавляем обработку событий для перемещения мышиjf.addMouseMotionListener(
new MouseMotionAdapter() {
// Dragged - значит что указатель мыши перетаскивают, кнопка нажата и следовательно нужно выполнять рисованиеpublic void mouseDragged(MouseEvent e) {
// откровенно говоря, показанный здесь способ рисования ужасен - рисовать следует внутри метода paint,// а отсюда следовало бы выполнить отправку запроса на вызов этого метода -// ни в коем случае нельзя метод paint вызывать явноGraphics g = jf.getGraphics();
Point now = e.getPoint();
g.setXORMode(Color.BLUE);
g.setColor(Color.RED);
g.drawRect((int)start_point.getX() , (int)start_point.getY(),
(int)(old_point.getX() - start_point.getX()),
(int)(old_point.getY() - start_point.getY())
);
g.drawRect((int)start_point.getX() , (int)start_point.getY(),
(int)(now.getX() - start_point.getX()),
(int)(now.getY() - start_point.getY())
);
old_point = e.getPoint();
}});
// показываем окно для рисованияjf.setVisible(true);
}}
Откровенно говоря, реализован пример просто ужасно: при изменении размеров или при попытке поместить окно приложения за другим окном возникают артефакты изображения. Также в свое первое появление окно имеет минимальный размер. Далее я раскажу как решить эту проблему.
Врезка: проектирование интерфейса в java может быть проведено с использованием всевозможных мастеров. Какие мастера и как их использовать вы должны смотреть в составе конкретного средства разработки. Один из лидеров интерфейсостроения borland jbuilder, для таких сред как eclipse или idea есть плагины. В последней версии idea дизайнер форм - является встроенным(4.5). Собственный опыт: создавать интерфейс без мастеров и визуальных построителей проще и удобнее, да и контроля больше. Собственный опыт будет меняться во времени, с появлением новых средств проектирования или изменения взглядов на жизнь.
Как грамотно показать окно, спрятать, изменить надпись на кнопке и т.д.
Для того чтобы отобразить созданное окно мы применяли метод setVisible (true), соответственно для того чтобы окно спрятатать setVisible (false). Этот метод применим и для любых других элементов управления (кнопки, текстовые поля и т.д.). Существует рекомендация как из кода (выполняющего некоторый расчет) выполнить изменение UI.
Предположим, что вы решили создать кнопку при нажатии на которую выполняется длительный расчет чего-угодно (например, числа PI с точностью до миллиона знаков после запятой), затем полученное значение помещается внутрь текстового поля.
Вы создали кнопку, создали обработчик события "click", и самой большой ошибкой будет написать внутри этого метода что-то вроде:
public void actionPerformed(ActionEvent e) {
// Расчет числа PI. Длительный расчет. Очень длительный.txt_result.setText (РезультатРасчета);
}
Поэтому в состав класса javax.swing.SwingUtilities были введены два метода: invokeLater и invokeAndWait. Каждому из них в качестве параметра передается ссылка на объект Runnable. Эти методы откладывают выполнение кода заключенного в методе run, до тех пор пока не будут выполнены все остальные запланированные действия подсистемой UI. Только затем выполняется код помещенный внутрь run и выполняется он в потоке EventDispatchThread. Рекомендуется весь код, который выполняет изменения интерфейса (прячет или показывает кнопки, меняет текстовые поля), заключать внутрь invoke*. Различие между invokeLater и invokeAndWait в том, что второй метод блокирует выполнение вашего кода до тех пор пока не отработает код внутри run. Ха, и если вы вызовите этот метод из потока EventDispatchThread, то получится чушь, и будет выброшено исключение. Первый метод может быть вызван из любого потока (в том числе и EventDispatchThread). Если вы не в состоянии определить какой поток выполняет ваш код, то используйте в составе класса SwingUtilites следующий метод:
public static boolean isEventDispatchThread()
public static void main(String[] args) {
JFrame f = new JFrame("hack gui");
JButton jp = new JButton ("click me");
f.setSize(400, 400);
// элементы UI созданы, и с этого момента мы не должны обращаться к ним извне пределов EventDispatchThread// фактически даже следующая строка является уже неправильной, т.к. выполняется в потоке mainf.setVisible(true);
}
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
jf.setVisible(true);
}});
Элементы управления
Начнем с простого: необходимо создать приложение наподобие калькулятора. Прежде всего, необходимо понимать, что все элементы управления делятся на две категории: элементы и контейнеры элементов. Контейнеры содержат список расположенных элементов и управляют их размещением, используя концепцию менеджеров раскладок. Менеджер раскладки – класс, который располагает элементы по правилам: например все друг за дружкой, а при нехватке места - переход на новую строку; или вся область контейнера разделяется на ячейки, в каждой из которых будет размещено по элементу управления. Возможно, эти клетки могут быть одинакового размера а возможно и нет. Разумно комбинируя контейнеры с разными менеджерами раскладок, можно создать практически любой интерфейс. Мы вернемся к менеджерам раскладки позже, пока же будем управлять размещением элементов вручную указывая для каждого координаты и размер.
Все контейнеры в мире awt (той самой древней библиотеке построения UI) производны от класса java.awt.Container. Впоследствии, все элементы управления в библиотеке swing были унаследованы от javax.swing.Jcomponent. Который, в свою очередь, является наследником от java.awt.Container. Следовательно, любой элемент управления может выступать в роли контейнера для других элементов. Правда при этом иногда приходится бороться с его желаниями. Но если вам заняться нечем - то можете попробовать. Класс окна Jframe является наследником от Frame, но является необычным контейнером. Прежде всего, на него нельзя напрямую помещать другие элементы управления, как было общепринято в библиотеке awt. Для доступа к контейнеру окна необходимо использовать вызов getContentPane. Другой вариант заключается в том, что вы создаете класс контейнера, например Jpanel. Затем на нее помещаете множество элементов управления и делаете вызов форма.setContentPane (панель).
Для демонстрации создается небольшое приложение состоящее из окна с кнопкой (за это отвечает компонент JButton), текстовой надписью (за это отвечает компонент JLabel). При нажатии на кнопку изменяется надпись на текстовой надписи, проходя через три стадии: одна надпись для количества нажатий от 0-5, 2-ая надпись – 6-10 нажатий, третья - более 10 нажатий. При наведении мыши на кнопку появляется всплывающая подсказка, в которой приводится таблица с перечислениями того, в какое время были выполнены эти "click"-и.
public static void main(String[] args) {
JFrame jf = new JFrame("Mega Buttons Test");
Container con = jf.getContentPane();
// получаем ссылку на контейнер для окна. именно на этот контейнер можно добавлять какие-либо элементы управленияcon.setLayout(null); // отказываемся от использования менеджера раскладок,
// это значит что я должен буду выполнять позиционирование элементов и указание их размеров сам, без// помощи менеджера раскладок// создаем кнопку, в качестве параметров конструктора указывается текстовая надпись для кнопки и, опционально,// изображение иконкиfinal JButton btn_next = new JButton("Click Me !", new ImageIcon("C:\\tmp\\end00000.jpg"));
// добавляем на окно (JFrame) созданную кнопкуcon.add(btn_next);
// указываем координаты кнопки и ее размер, первые два числа для конструктора Rectangle - координаты// левого верхнего укгла где будет размещена кнопка, остальные два числа - ее размерыbtn_next.setBounds(new Rectangle(50, 50, 150, 50));
// создается элемент управления - надпись, в качестве параметров конструктора следует// указать текстовую надпись для кнопки, горизонтальное выравнивание содержимого надписи, и иконкаfinal JLabel lab_status = new JLabel("Пока ничего не произошло",
new ImageIcon("C:\\tmp\\end00000.jpg"), JLabel.CENTER);
con.add(lab_status);
// добавляем на окно (JFrame) созданную текстовую надписьlab_status.setBounds(new Rectangle(50, 120, 300, 50));
// теперь выполним позиционирование созданной надписи// создаем обработчик события для нажатия на кнопкуbtn_next.addActionListener(
new ActionListener() {
// поле класса служит для подсчета того сколько раз// было выполнено нажатие на кнопку в течении срока работы программыint count_was_pressed = 0;
// а этот список служит для хранения тех дат-времени, когда именно было выполненоArrayList times = new ArrayList();
public void actionPerformed(ActionEvent e) {
// при нажатии на кнопку - увеличим переменную счетчик количества нажатияcount_was_pressed++;// также добавим в массив текущую дату-времяtimes.add(new java.util.Date());
// изменим текст надписи в завимимости от того сколько именно раз мы выполнили нажатие на кнопкуif (count_was_pressed < 5)
lab_status.setText("Количество нажатий менее чем 5");
else if (count_was_pressed < 10)
lab_status.setText("Количество нажатий в отрезке от 5 до 10");
elselab_status.setText("Количество нажатий превзошло 10");
// создается строка содержащая некоторый HTML-документ, обратите внимание на то,// что содержимое строки начинатся со слова <HTML>String mes = "<HTML>Количество нажатий: " + count_was_pressed + "<BR>";
mes += " <IMG SRC='C:\tmp\\end00000.jpg'>";
mes += "<TABLE BORDER='1'>";
for (int i = 0; i < times.size(); i++) {
mes += "<TR>";
mes += "<TD> <IMG SRC='C:\\tmp\\pic" + ((i) % 3 + 1) + ".jpg'> </TD>";
mes += "<TD>";
java.util.Date date = (java.util.Date) times.get(i);
java.text.SimpleDateFormat sdf = new SimpleDateFormat("MMMM-DD-yyyy hh:mm:ss");
mes += sdf.format(date) + "</TD>";
mes += "</TR>";
}mes += "</TABLE>";
// в html-тексте я использую таблицы, картинки// теперь получившуюся строку я хочу установить как значение tooltip-а (всплывающей// подсказки, возникающей при наведении мыши на кнопку)btn_next.setToolTipText(mes);
}});
jf.setVisible(true);
}
Для обработки событий "нажатия на кнопку" я добавил обработчик события в виде анонимного класса, реализующий интерфейс ActionListener. При вызове метода actionPerformed, передается в качестве параметра объект ActionEvent. В составе этого объекта его нет ничего особенно ценнного, за исключением ссылки на объект, который сгенерировал данное событие (это неплохо в том случае, если вы хотите привязать один и тот же обработчик к множеству кнопок, а внутри функции actionPerformed выполнить проверку с помощью if, switch какая же именно кнопка была активирована и что-то сделать). Это единая методика обработки событий, будут меняться лишь названия интерфейсов которые вам следует реализовать, имена методов в составе этого интерфейса и, естественно, будет изменься входной параметр этих методов. Так производные классы MouseEvent, KeyEvent и другие, приобретают специфические параметры содержащие дополнительные сведения о том, что же произошло с нашим UI.
Для хранения истории времени, когда было выполнено нажатие на кнопку используется массив ArrayList. Важно: при задании всплывающей подсказки tooltip я сформировал строку содержащую теги HTML. Это один из столпов swing – тесная интеграция с html. Практически любой элемент управления, который способен отображать текст, может принимать в качестве параметра документы HTML. Критически важно: это не internet explorer, который игнорирует ошибки: поэтому пишите теги корректно. К сожалению, использовать возможности стилей не возможно.
Поправка, это можно делать, но с большими ограничениями, так что эксперементируйте.
mes += "<TABLE BORDER='1' STYLE='color: green;' >";
Для задания размеров и местоположения элементов используется метод setBounds. В качестве входного параметра методу setBounds задается объект прямоугольник (Rectangle). Параметры его конструктора - координаты левого верхнего угла и размеры: ширина и высота.
События и класссы слушателей, которые к ним привязаны
| Событие, которое произошло | Тип класса слушателя |
| Пользователь нажимает на кнопку или что-то от нее производное: radio, check. Нажимает enter в текстовом поле или выбирает пункт меню. | ActionListener |
| Закрывает окно | WindowListener |
| Нажимает кнопку мыши или отпускает ее | MouseListener |
| Пермещает мышь над компонентом | MouseMotionListener |
| Компонент становится видимым или нет | ComponentListener |
| Компонент получает или теряет фокус | FocusListener |
| Таблица или список изменяют выделение | ListSelectionListener |
| Изменяется свойство компонента: например, текстовая надпись для Jlabel. | PropertyChangeListener |
Рамки
Для оформления и выделения созданных элементов управления удобно использовать рамки. Еще их удобно использовать для отладки: если вы создаете UI "ручками", и использете прием с чередование большого количества вложенных друг внутрь друга контейнеров, то неплохо каждому контейнеру назнаить свою, разноцветную, рамку. Например, так:
btn_next.setBorderPainted(true);
btn_next.setBorder(BorderFactory.createEtchedBorder(Color.BLUE , Color.GREEN));
BevelBorder - это типовая для графического интерфейса рамка, имитирующая либо выпуклость, либо вогнутость панели. Чтобы получить рамку типа BevelBorder, нужно вызвать метод createBevelBorder класса BorderFactory.
btn_next.setBorder(BorderFactory.createBevelBorder(
BevelBorder.LOWERED , Color.YELLOW , Color.GREEN , Color.WHITE , Color.MAGENTA
));
Достаточно похожа на BevelBorder по дизайну рамка SoftBevelBorder и EtchedBorder. Первая из них должна отображать рамку, похожую на BevelBorder, но со "смягченными" краями.
btn_next.setBorder(new SoftBevelBorder (BevelBorder.LOWERED, Color.RED , Color.BLUE));
Сложная рамка - EmptyBorder. Эта прозрачная рамка, которая не отображается на экране, однако занимает место вокруг контейнера. Особое ее значение необходимо при точном позиционировании элементов, или для того, чтобы создать зазор между контейнером и элементами.
Для того чтобы логически объединить элементы в окне или в панели используйте рамку TitledBorder.
В примере далее создается набор радиокнопок, для выбора различных видов “еды“. Варианты выбора помещаются внутрь рамки с надписью, при выборе какого-либо из пунктов появляется сообщения о том, что произошло.
JPanel jp_items = new JPanel();
jp_items.setBorder(new TitledBorder ("Выберите предпочтимое блюдо из списка ниже:"));
String foods [] = new String[]{"apple" , "orange" , "grapes" , "pizza"};
String imgs [] = new String[]{"C:\\tmp\\pic1.jpg" , "C:\\tmp\\pic2.jpg" , "C:\\tmp\\pic3.jpg" , "C:\\tmp\\pic4.jpg"};
String simgs [] = new String[]{"C:\\tmp\\spic1.jpg" , "C:\\tmp\\spic2.jpg" , "C:\\tmp\\spic3.jpg" , "C:\\tmp\\spic4.jpg"};
final ButtonGroup group_food = new ButtonGroup();
int [] keys = new int [] {KeyEvent.VK_F1, KeyEvent.VK_F2, KeyEvent.VK_F3, KeyEvent.VK_F4};
for (int i = 0; i < foods.length; i++) {
String food = foods[i];
final JRadioButton radio = new JRadioButton(food, new ImageIcon (imgs [i]));
radio.setBounds(new Rectangle(20 , 20 + 30*i , 200 , 25));
radio.setRolloverIcon(new ImageIcon (simgs [i]));
radio.setMnemonic(keys[i]);
radio.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println ("Was Selected : " + radio);
}});
jp_items.add (radio);
group_food.add(radio);
}con.add (jp_items);
jp_items.setBounds(new Rectangle(300 , 50 , 300 , 300)) ;
Для каждой из радио-кнопок создается мнемоника – комбинация клавиш для ее активации. Устанавливаются две иконки: состояние обычное и когда мышь находится над элементом. Также вы еще можете задать иконку для состояния "выделено":
radio.setSelectedIcon(new ImageIcon ("C:\\tmp\\end00000.jpg"));
Следующая рамка - MatteBorder. Она заполняет все отведенное ей пространство графическим изображением, которое берется из файла формата GIF или JPG. Если изображение слишком велико для окна, оно будет усечено. Если же окно больше изображения, то все отведенное под рамку пространство будет заполнено по принципу мозаики.
jp_items.setBorder(new MatteBorder (new Insets(10, 20 , 30, 40) , new ImageIcon ("C:\\tmp\\end00000.jpg")));
Последний вид рамки - CompoundBorder. Она служит для объединения двух заданных пользователем рамок в одну. При этом сначала создается первая рамка, а уже внутри нее отображается вторая.
JPanel jp_items = new JPanel();
MatteBorder mb = new MatteBorder (new Insets(10, 20 , 30, 40) , new ImageIcon ("C:\\tmp\\end00000.jpg"));
TitledBorder tb = new TitledBorder("Выберите предпочтимое блюдо из списка ниже:");
CompoundBorder cm = new CompoundBorder(/*outside border*/tb , /*inside border*/mb);
jp_items.setBorder(cm);
final JPanel jp_chks = new JPanel();
jp_chks.setBorder(new TitledBorder("Доступные места для сеанса:"));
jp_chks.setLayout(null);
final ArrayList all_chks = new ArrayList();
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++){
JCheckBox chk = new JCheckBox("Место : " + i + " Ряд :" + j);
jp_chks.add (chk);
all_chks.add (chk);
chk.setBorderPaintedFlat(true);
chk.setBounds(new Rectangle (20+i*70, 20+j*30, 65, 25));
chk.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
int c = 0;
String mes = "<HTML>";
for (Iterator iterator = all_chks.iterator(); iterator.hasNext();) {
JCheckBox jCheckBox = (JCheckBox) iterator.next();
if (jCheckBox.isSelected()){
mes += jCheckBox.getText() + ", ";
c++;if (c % 5 == 0)
mes += "<BR>";
}}jp_chks.setToolTipText(mes); // обратите внимание на декларацию переменной - final
}});
}con.add (jp_chks);
jp_chks.setBounds(new Rectangle(300 , 50 , 300 , 300)) ;
JPanel what_i_must_do = new JPanel(null);
JToggleButton tog_nothing = new JToggleButton( "Ничего", new ImageIcon("C:\\tmp\\spic2.jpg"));
JToggleButton tog_rest = new JToggleButton( "Отдыхать", new ImageIcon("C:\\tmp\\spic3.jpg"));
JToggleButton tog_think = new JToggleButton( "Размышлять", new ImageIcon("C:\\tmp\\spic4.jpg"));
what_i_must_do.add (tog_think);
what_i_must_do.add (tog_rest);
what_i_must_do.add (tog_nothing);
tog_think.setBounds(new Rectangle(10 , 10 , 220 , 30));
tog_rest.setBounds(new Rectangle(10 , 50 , 220 , 30));
tog_nothing.setBounds(new Rectangle(10 , 90 , 220 , 30));
con.add (what_i_must_do);
what_i_must_do.setBounds(new Rectangle(300 , 50 , 300 , 300)) ;
tog_think.setCursor(new Cursor (Cursor.CROSSHAIR_CURSOR));
tog_rest.setCursor(new Cursor (Cursor.WAIT_CURSOR));
tog_nothing.setCursor(new Cursor (Cursor.NW_RESIZE_CURSOR));
Курсор
В примере выше была показана возможность установить для некоторого элемента управления особый вид курсора (наводим мышь на элемент и видим не привычную стрелку - а, например, песочные часы или что-то еще). У каждого UI элемента есть метод setCursor, который и служит для того чтобы установить внешний вид курсора.
В составе класса java.awt.Cursor определен ряд констант - кодов курсоров.
JPanel pcurs = new JPanel();
Cursor [] curs = new Cursor [] {
Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR),
Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR),
Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.HAND_CURSOR),
Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR),
Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR),
Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)
};
for (int i = 0; i < curs.length; i++) {
Cursor cur = curs[i];
JButton comp = new JButton(cur.getName());
comp.setCursor(cur);
pcurs.add(comp);
}con.add(pcurs);
Есть возможность создавать свои собственные курсоры. Для этого в состав класса Toolkit введен метод создающий пользовательский курсор на основании указанного изображения, координат "горячей точки" и имени курсора.
final JButton btn_cust = new JButton("Custom Cursor");
con.add(btn_cust);
Cursor customCursor = Toolkit.getDefaultToolkit().createCustomCursor(
new ImageIcon("C:\\tmp\\cursora.jpg").getImage(), new Point(5, 5), "foo_cursor"
);
btn_cust.setCursor(customCursor);

| « Про java swing - часть 1 | Внедрение flash-видео и swf-файлов внутрь medawiki сайта » |