Событие AdjustmentEvent

Это событие возникает для полосы прокрутки Scrollbar или JScrollBar при всяком изменении ее бегунка и отмечается идентификатором adjustment_value_changed.

Соответствующий интерфейс описывает один метод:

public interface AdjustmentListener extends EventListener{ public void adjustmentValueChanged(AdjustmentEvent e);

}

Аргумент e этого метода предоставляет ссылку на источник события методом e. getAdj ustable (), текущее значение положения движка полосы прокрутки методом e.getValue ( ) и способ изменения его значения методом e.getAdjustmentType(), возвращающим следующие значения:

? unit_increment — увеличение на одну единицу;

? unit_decrement — уменьшение на одну единицу;

? block_increment — увеличение на один блок;

? block_decrement — уменьшение на один блок;

? track — процесс передвижения бегунка полосы прокрутки.

"Оживим" программу создания цвета, приведенную в листинге 10.4, добавив необходимые действия. Результат этого представлен в листинге 15.6.

Листинг 15.6. Программа создания цвета

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

class ScrollTest1 extends Frame{ private Scrollbar

sbRed = new Scrollbar(Scrollbar.VERTICAL, 127, 16, 0, 271), sbGreen = new Scrollbar(Scrollbar.VERTICAL, 127, 16, 0, 271), sbBlue = new Scrollbar(Scrollbar.VERTICAL, 127, 16, 0, 271);

private Color c = new Color(127, 127, 127); private Label lm = new Label();

private Button

b1 = new Button("Применить"), b2 = new Button("Отменить");

ScrollTest1(String s){ super(s);

setLayout(null);

setFont(new Font("Serif", Font.BOLD, 15));

Panel p = new Panel(); p.setLayout(null);

p.setBounds(10,50, 150, 260); add(p);

Label lc = new Label("Подберите цвет"); lc.setBounds(20, 0, 120, 30); p.add(lc);

Label lmin = new Label("0", Label.RIGHT); lmin.setBounds(0, 30, 30, 30); p.add(lmin);

Label lmiddle = new Label("127", Label.RIGHT); lmiddle.setBounds(0, 120, 30, 30); p.add(lmiddle); Label lmax = new Label("255", Label.RIGHT); lmax.setBounds(0, 200, 30, 30); p.add(lmax);

sbRed.setBackground(Color.red); sbRed.setBounds(40, 30, 20, 200); p.add(sbRed); sbRed.addAdjustmentListener(new ChColor());

sbGreen.setBackground(Color.green); sbGreen.setBounds(70, 30, 20, 200); p.add(sbGreen) sbGreen.addAdjustmentListener(new ChColor());

sbBlue.setBackground(Color.blue); sbBlue.setBounds(100, 30, 20, 200); p.add(sbBlue); sbBlue.addAdjustmentListener(new ChColor());

Label lp = new Label("Образец:"); lp.setBounds(250, 50, 120, 30); add(lp);

lm.setBackground(new Color(127, 127, 127)); lm.setBounds(220, 80, 120, 80); add(lm);

b1.setBounds(240, 200, 100, 30); add(b1); b1.addActionListener(new ApplyColor());

b2.setBounds(240, 240, 100, 30); add(b2); b2.addActionListener(new CancelColor());

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

}

class ChColor implements AdjustmentListener{

e) {

= c.getBlue(); getValue(); getValue(); getValue();

public void adjustmentValueChanged(AdjustmentEvent int red = c.getRed(), green = c.getGreen(), blue if (e.getAdjustable() == sbRed) red = e

else if (e.getAdjustable() == sbGreen) green = e else if (e.getAdjustable() == sbBlue) blue = e c = new Color(red, green, blue); lm.setBackground(c);

}

class ApplyColor implements ActionListener{

public void actionPerformed(ActionEvent ae){ setBackground(c);

}

}

class CancelColor implements ActionListener{ public void actionPerformed(ActionEvent ae){ c = new Color(127, 127, 127); sbRed.setValue(127); sbGreen.setValue(127) ; sbBlue.setValue(127); lm.setBackground(c); setBackground(Color.white);

}

}

public static void main(String[] args){

Frame f = new ScrollTest1(" Выбор цвета"); f.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){ System.exit(0);

}

});

}

}

Несколько слушателей одного источника

В начале этой главы, в листингах 15.1—15.3, мы привели пример класса TextMove, слушающего сразу два компонента: поле ввода tf типа JTextField и кнопку b типа JButton.

Чаще встречается обратная ситуация — несколько слушателей следят за одним компонентом. В том же примере кнопка b в ответ на щелчок по ней кнопки мыши совершала еще и собственные действия — она "вдавливалась", а при отпускании кнопки мыши становилась "выпуклой". В классе Button эти действия осуществляет peer-объект.

В классе FlowerButton листинга 10.7 такие же действия выполняет метод paint ( ) этого класса.

В данной модели реализован design pattern под названием Observer.

К каждому компоненту можно присоединить сколько угодно слушателей одного и того же события или разных типов событий. Однако при этом не гарантируется какой-либо определенный порядок их вызова, хотя чаще всего слушатели вызываются в порядке написания методов addXxxListener ().

Если нужно задать определенный порядок вызовов слушателей для обработки события, то придется обращаться к ним друг из друга или создавать объект, вызывающий слушателей в нужном порядке.

Ссылки на присоединенные методами addXxxListener( ) слушатели можно было бы хранить в любом классе-коллекции, например Vector, но в пакет java.awt специально для этого введен класс AWTEventMulticaster. Он реализует все одиннадцать интерфейсов XxxListener, значит, сам является слушателем любого события. Основу класса составляют своеобразные статические методы add (), написанные для каждого типа событий, например:

add(ActionListener a, ActionListener b);

Своеобразие этих методов двоякое: они возвращают ссылку на тот же интерфейс, в данном случае ActionListener, и присоединяют объект a к объекту b, создавая совокупность слушателей одного и того же типа. Это позволяет использовать их наподобие операций a += b.

Заглянув в исходный текст класса Button, вы увидите, что метод addActionListener() очень прост:

public synchronized void addActionListener(ActionListener l){ if (l == null){ return; }

actionListener = AWTEventMulticaster.add(actionListener, l); newEventsOnly = true;

}

Он добавляет к совокупности слушателей actionListener нового слушателя l.

Для событий типа InputEvent, а именно KeyEvent и MouseEvent, есть возможность прекратить дальнейшую обработку события методом consume (). Если записать вызов этого метода в класс-слушатель, то ни peer-объекты, ни следующие слушатели не будут обрабатывать событие. Таким способом обычно пользуются, чтобы отменить стандартные действия компонента, например "вдавливание" кнопки.

Диспетчеризация событий

Если вам понадобится обработать просто действие мыши, не важно, нажатие это, перемещение или еще что-нибудь, то придется включать эту обработку во все семь методов двух классов-слушателей событий мыши.

Задачу можно облегчить, выполнив обработку не в слушателе, а на более ранней стадии. Дело в том, что прежде чем событие дойдет до слушателя, оно обрабатывается несколькими методами.

Чтобы в компоненте произошло событие AWT, должно быть выполнено хотя бы одно из двух условий: к компоненту присоединен слушатель или в конструкторе компонента определена возможность появления события методом enableEvents (). В аргументе этого метода через операцию побитового сложения перечисляются константы класса AWTEvent, задающие события, которые могут произойти в компоненте, например:

enableEvents (AWTEvent.MOUSE_MOTION_EVENT_MASK |

AWTEvent.MOUSE_EVENT_MASK | AWTEvent. KEY_EVENT_MASK)

При появлении события создается объект соответствующего класса XxxEvent. Метод dispatchEvent () определяет, где появилось событие — в компоненте или одном из его

подкомпонентов,- и передает объект-событие методу processEvent () компонента-

источника.

Метод processEvent () определяет тип события и передает его специализированному методу processXxxEvent (). Вот начало этого метода:

protected void processEvent(AWTEvent e){ if (e instanceof FocusEvent){

processFocusEvent((FocusEvent)e);

}else if (e instanceof MouseEvent){ switch(e.getID()){

case MouseEvent.MOUSE_PRESSED: case MouseEvent.MOUSE RELEASED: case MouseEvent.MOUSE_CLICKED: case MouseEvent.MOUSE ENTERED: case MouseEvent.MOUSE EXITED:

processMouseEvent((MouseEvent)e); break;

case MouseEvent.MOUSE MOVED: case MouseEvent.MOUSE_DRAGGED:

processMouseMotionEvent((MouseEvent)e); break;

}

}else if (e instanceof KeyEvent){ processKeyEvent((KeyEvent)e);

}

// ...

Затем в дело вступает специализированный метод, например processKeyEvent(). Он-то и передает объект-событие слушателю. Вот исходный текст этого метода:

protected void processKeyEvent(KeyEvent e){

KeyListener listener = keyListener; if (listener != null){ int id = e.getID(); switch(id){

case KeyEvent.KEY TYPED: listener.keyTyped(e); break;

case KeyEvent.KEY PRESSED: listener.keyPressed(e); break;

case KeyEvent.KEY RELEASED: listener.keyReleased(e); break;

}

}

}

Из этого описания видно, что если вы хотите обработать любое событие типа AWTEvent, то вам надо переопределить метод processEvent(), а если более конкретное событие, например событие клавиатуры, — переопределить более конкретный метод processKeyEvent (). Если вы не переопределяете весь метод целиком, то не забудьте в конце обратиться к методу суперкласса, например:

super.processKeyEvent(e);

Замечание

Не забывайте обращаться к методу processXxxEvent () суперкласса.

В главе 10 мы уже применили такое переопределение в листинге 10.10 для вызова всплывающего меню.

Создание собственного события

Вы можете создать собственное событие и определить источник и условия его возникновения.

В листинге 15.7 приведен пример создания события MyEvent, любезно предоставленный Вячеславом Педаком.

Событие MyEvent говорит о начале работы программы (start) и окончании ее работы

(stop).

Листинг 15.7. Создание собственного события

// 1. Создаем свой класс события:

public class MyEvent extends java.util.EventObject{ protected int id;

public static final int START = 0, STOP = 1; public MyEvent(Object source, int id){ super(source); this.id = id;

}

public int getID(){ return id; }

}

// 2. Описываем Listener:

public interface MyListener extends java.util.EventListener{ public void start(MyEvent e); public void stop(MyEvent e);

}

// 3. В теле нужного класса создаем метод fireEvent(): protected Vector listeners = new Vector(); public void fireEvent( MyEvent e){

Vector list = (Vector) listeners.clone(); for(int i = 0; i < list.size(); i++){

MyListener listener = (MyListener)list.elementAt(i); switch(e.getID()){

case MyEvent.START: listener.start(e); break; case MyEvent.STOP: listener.stop(e); break;

}

}

}

Все, теперь при запуске программы делаем

fireEvent(this, MyEvent.START);

а при окончании

fireEvent(this, MyEvent.STOP);

При этом все зарегистрированные слушатели получат экземпляры событий.

Вопросы для самопроверки

1. Какая модель обработки событий выбрана в AWT?

2. Что нового добавила в обработку событий библиотека Swing?

3. Какие компоненты отслеживают события мыши?

4. Какие компоненты отслеживают события клавиатуры?

5. Может ли одно и то же событие возникнуть сразу в нескольких компонентах?

6. Может ли одно действие вызвать сразу несколько событий?

7. Можно ли сделать обработку нескольких событий одним методом?

8. Можно ли обработать одно событие сразу несколькими методами?

9. Как в AWT осуществляется диспетчеризация событий?

ГЛАВА 16