Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Списки

315

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

Можно вспомнить, что в современных приложениях очень часто выбор (в том числе и в списках) легко сделать двойным щелчком мыши. Пользователь может быстро показать, что ему нужно, дважды щелкнув на некотором элементе списка. Список JList по умолчанию не поддерживает двойные щелчки мыши и не предоставляет средства по их определению, но поддержать такое поведение самостоятельно не составляет труда. Нам помогут два полезных метода списка JList: locationToIndex() и indexToLocation(). Они позволяют узнать, какому элементу списка принадлежит некоторая точка на экране, или наоборот выяснить, в каком месте экрана находится тот или иной элемент списка. С их помощью (для нашего примера понадобится первый метод) нам будет просто обеспечить поддержку двойных щелчков мыши:

//DoubleClickList.java

//Получение элемента списка по точке экрана import javax.swing.*;

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

public class DoubleClickList extends JFrame { // данные списка

private String[] data = { "Стейк", "Лобстер", "Борщ", "Севрюга"};

private JList list;

public DoubleClickList() { super("DoubleClickList"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создаем список

list = new JList(data); list.setSelectionMode(

ListSelectionModel.SINGLE_SELECTION); // добавим cлушателя событий от мыши list.addMouseListener(new MouseAdapter() {

public void mousePressed(MouseEvent e) { if ( e.getClickCount() == 2 ) {

// получаем элемент и покажем его int pos =

list.locationToIndex(e.getPoint());

JOptionPane.showMessageDialog( list, "Уже готовится: " +

list.getModel().getElementAt(pos));

}

316

ГЛАВА 11

}

}); // выведем окно на экран

add(new JScrollPane(list)); setSize(300, 200); setVisible(true);

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new DoubleClickList(); } });

}

}

Мы создаем небольшой «кулинарный» список (пусть это будет меню небольшого ресторана фьюжн-кухни) и добавляем его в центр нашего окна. К списку присоединяется слушатель событий от мыши (конечно, в виде адаптера: к чему нам остальные методы слушателя), в котором мы следим за щелчками мыши на списке. Когда пользователь щелкает на списке дважды, мы получаем позицию элемента, на котором произошло это событие (как раз здесь нам и пригодится метод locationToIndex()), и сообщаем пользователю, что его выбор был получен и обработан. Поддержка двойных щелчков мыши интуитивно понятна пользователю («сильный», то есть двойной, щелчок равносилен выбору) и особенно полезна там, где выбирать можно только один элемент списка. В таких ситуациях всегда добавляйте поддержку двойных щелчков мыши: это уже становится нормой. Напоследок стоит сказать о том, что метод locationToIndex() работает в координатах списка JList. В нашем примере это не важно, потому что мы следили за щелчками мыши непосредственно в списке, но если вы присоединили слушатель мыши к другому компоненту, в котором находится список, не забудьте преобразовать позицию указателя мыши в координаты списка с помощью метода convertMouseEvent() класса SwingUtilities.

Список JList делает все, чтобы с ним можно было без проблем работать на самом низком уровне: при обработке событий от мыши и клавиатуры или в других подобных ситуациях. Он позволяет определить, какие элементы списка в данный момент видны на экране, прокрутить свое содержимое так, чтобы на экране появился определенный элемент, узнать размеры любого элемента и выяснить, на каких позициях экрана он находится. Вы можете смело манипулировать списком на всех уровнях: и посредством моделей, и посредством низкоуровневых событий. Вся необходимая информация у вас в руках.

Список из флажков

В современных приложениях довольно часто встречаются списки, в качестве своих элементов показывающие флажки. Флажки позволяют упорядочить набор альтернатив и более наглядно представить глазам пользователя сделанный выбор. В качестве примера можно вспомнить разнообразные программы установки, в которых вы выбираете, какие компоненты устанавливаемой системы будут копироваться на вашу машину и сколько пространства на жестком диске в итоге потребуется, или всяческие «корзины покупателя», в которых аналогичным образом можно увидеть общую стоимость выбранных товаров и при необходимости от некоторых товаров тут же отказаться. Одним словом, наличие списка с флажками в вашем арсенале лишним не будет, и попробовать создать его стоит, заодно закрепив все полученные нами знания о списках JList.

Списки

317

Сразу ясно, что нам понадобится особый класс для хранения данных об элементе списка (включен или выключен элемент, а также строка с надписью) и специальный элемент ListCellRenderer, который будет отображать элементы списка в надлежащем виде, а именно как флажки JCheckBox. Но на этом работа не заканчивается: мы хорошо знаем, что список не помещает создаваемые объектом ListCellRenderer компоненты на экран, он всего лишь использует их как кисти для прорисовки, так что автоматически флажки устанавливаться или сбрасываться не будут (они не будут получать событий). Придется присоединить к списку слушателя событий от мыши и при щелчке сменять состояние элемента (включенное на выключенное и наоборот), чтобы флажок правильно отображал его. Давайте приступим. Сначала самое простое — класс с данными элементов нашего будущего списка:

//com/porty/swing/CheckBoxListElement.java

//Данные элемента списка с флажками package com.porty.swing;

public class CheckBoxListElement {

//данные элемента private boolean selected; private String text;

//удобный конструктор

public CheckBoxListElement(boolean selected, String text){ this.selected = selected;

this.text = text;

}

// методы для доступа к значку и тексту

public boolean isSelected() { return selected; } public String getText() { return text; }

public void setSelected(boolean selected) { this.selected = selected;

}

public void setText(String text) { this.text = text; }

}

В классе CheckBoxListElement (мы поместили его в библиотечный пакет com.porty. swing) находятся данные для списка с флажками: признак выделения элемента и строка с текстом, которая этот элемент будет описывать. Для удобства был добавлен конструктор, который позволяет быстро задать нужные значения элемента. Теперь нужно написать отображающий объект ListCellRenderer, который на основе этого класса должен создавать подходящий флажок. Мы не станем писать его в виде отдельного класса (вряд ли он будет использоваться где-то еще помимо списка с флажками, потому что ему требуется только определенный тип данных), а сделаем частью класса нашего нового списка с флажками (опишем его в виде статического внутреннего класса). Вот что получится:

//CheckBoxList.java

//Список с флажками package com.porty.swing;

318

ГЛАВА 11

import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Vector;

public class CheckBoxList extends JList { // сохраняем все конструкторы

public CheckBoxList(ListModel model) { super(model);

initList();

}

public CheckBoxList(Object[] data) { super(data);

initList();

}

public CheckBoxList(Vector data) { super(data);

initList();

}

// специальная настройка списка private void initList() {

setCellRenderer(new CheckBoxCellRenderer()); setSelectionMode(

ListSelectionModel.SINGLE_SELECTION); addMouseListener(new MouseAdapter() {

public void mousePressed(MouseEvent e) { // следим за щелчками

if ( e.getClickCount() == 1 && SwingUtilities.isLeftMouseButton(e) ) {

//нужный нам щелчок int pos =

locationToIndex(e.getPoint()); CheckBoxListElement cbel =

(CheckBoxListElement)getModel(). getElementAt(pos);

cbel.setSelected(! cbel.isSelected());

//заново рисуем только эту ячейку списка repaint(getCellBounds(pos, pos));

}

}

});

Списки

319

}

// отображающий флажки объект

public static class CheckBoxCellRenderer

extends JCheckBox implements ListCellRenderer { public Component getListCellRendererComponent(

JList list, Object data, int idx, boolean isSelected, boolean hasFocus) {

//полагаем, что данные всегда нужного типа

CheckBoxListElement

cbel = (CheckBoxListElement)data;

//настраиваем флажок

if ( isSelected ) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground());

} else { setBackground(list.getBackground()); setForeground(list.getForeground());

}

setSelected(cbel.isSelected());

setText(cbel.getText()); return this;

}

}

}

Мы создаем список с флажками CheckBoxList на основе обычного списка JList, наследуя от него, так что все возможности обычных списков остаются с вами. Прежде всего обратите внимание на реализацию специального рисующего объекта CheckBoxCellRenderer. Он унаследован от флажка JCheckBox и реализует необходимый для всех рисующих объектов списка интерфейс ListCellRenderer. Для отображения элементов списка требуется только один флажок — собственно сам рисующий объект. Он приводит нуждающийся в прорисовке элемент списка к нужному типу CheckBoxListElement (мы полагаем, что в списке с флажками будут элементы только такого типа, в противном случае возникнет исключение при приведении типов) и производит настройку флажка. Так как в этот раз нам приходится создавать рисующий объект «с нуля», нужно самостоятельно позаботится о соответствующих цветах элемента списка (которые могут меняться пользователем или текущим менеджером внешнего вида и поведения). Далее, используя специальные данные класса CheckBoxListElement, мы устанавливаем состояние флажка и текст для него.

Теперь остается только сам список с флажками. Он почти не отличается от обычного списка JList, за исключением того, что отдельно следит за нажатиями мыши, сразу же устанавливает специальный рисующий объект CheckBoxCellRenderer и по умолчанию задействует режим одиночного выделения элементов (это удобно для пользователя — он может сосредоточиться на смене состояния нужных ему элементов, а смена состояния возможна только для одного флажка одновременно). Мы сохранили все три конструктора обычного списка JList, так что вы не заметите разницы между функционированием обычного и нового списков CheckBoxList. Все что необходимо присоединяется в методе

Источник: https://studfile.net/preview/15935977/