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

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

Списки

305

extends DefaultListSelectionModel { // добавление интервала выделения

public void addSelectionInterval(int idx0, int idx1) { super.addSelectionInterval(idx0, idx1); adjustSelection(idx0);

}

// установка интервала выделения

public void setSelectionInterval(int idx0, int idx1) { super.setSelectionInterval(idx0, idx1); adjustSelection(idx0);

}

// общий метод "подкрутки" выделения private void adjustSelection(int idx) {

if ( idx == 0 ) addSelectionInterval(2, 2);

if ( idx == 1 ) addSelectionInterval(3, 3);

}

}

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

new Runnable() {

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

}

}

Вэтом примере мы добавляем в центр окна список с элементами массива data.

Всписке нет ничего экстраординарного, за исключением специальной модели выделения CustomListSelectionModel, реализованной в виде внутреннего класса. Мы унаследовали эту модель от стандартной модели DefaultListSelectionModel, и переопределили два самых полезных для нас метода: метод addSelectionInterval() добавляет к выделению новый интервал, а метод setSelectionInterval() заменяет текущий интервал

выделения новым (хотя на самом деле выделение меняет UI-представитель списка, когда пользователь щелкает мышью или нажимает клавиши выделения, но сейчас это не так важно). При вызове списком JList данных методов мы, во-первых, вызываем их базовые версии, так что выделение элементов идет обычным порядком, во-вторых, проверяем, какой именно элемент выбирается, в специальном методе adjustSelection(). Если это нужные нам первый или второй элемент списка, мы дополнительно к ним выделяем (программно, методом addSelectionInterval()) третий и четвертый элементы списка, соответственно. Это намек пользователю на оптимальное сочетание — мороженное должно быть холодным, а курица горячей. Хотя если ему больше по нраву холодная курица, он может и не согласиться с нашим вторым мнением и убрать не понравившийся ему выбранный элемент. В этом наша специальная модель выделения ему не мешает. Запустив программу с примером, вы увидите все своими глазами.

306

ГЛАВА 11

Подобное поведение можно было бы реализовать и более простым и интуитивно понятным способом: присоединением к модели выделения слушателя и выделением

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

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

Кроме того что модель выделения без труда настраивается и имеет широкие возможности, она позволяет легко получить выделенные в данный момент элементы списка. Для этого можно воспользоваться методами самой модели выделения или, как это обычно делается в Swing, аналогами этих методов, определенными в непосредственно списке JList (вы выбираете тот подход, который вам удобнее и лучше вписывается в концепцию вашего кода). Также в списке определены несколько очень удобных методов, позволяющих получить массив выбранных элементов или массив позиций этих элементов (табл. 11.2).

Таблица 11.2. Методы для получения информации о выделенных элементах

Метод

Назначение

 

 

isSelectedIndex()

Метод модели выделения, который позволяет узнать, выбран ли неко-

 

торый элемент списка

getSelectedIndex(),

Эти методы определены в списке JList и позволяют получить позицию

getSelectedIndices()

первого выделенного элемента (если ничего не выделено, возвращает-

 

ся —1) или массив позиций выделенных элементов

getSelectedValue(),

Эти методы также определены в списке JList, с их помощью получают

getSelectedValues()

сам выбранный элемент списка или массив, составленный из всех вы-

 

бранных элементов списка

 

 

Используя описанные методы, вы сможете получить полную информацию о выборе пользователя.

Внешний вид списка

До сих пор мы выясняли, какие данные показывают списки и как эти данные подготавливать и использовать, а затем получать информацию о выделенных элементах. Теперь мы можем посмотреть, что позволяют изменить списки в своем внешнем виде. Оказывается, изменить можно практически все.

Списки

307

Как и у всех компонентов Swing, унаследованных от базового класса JComponent, у списка JList есть возможность менять цвет фона (список является непрозрачным, его свойство opaque равно true, и как мы знаем из главы 4, это означает, что занимаемая им площадь закрашивается цветом фона), цвет шрифта и сам шрифт. Чтобы не ограничивать вашу фантазию, список также позволяет при необходимости изменить цвет выделенного элемента и сам цвет выделения. Свойства, позволяющие это сделать, кратко описаны в табл. 11.3.

Таблица 11.3. Свойства, меняющие внешний вид списка

Свойство

Описание

(и методы get/set)

 

background, foreground, font

Стандартные свойства любого компонента Swing. Позволяют

 

задать фон, цвет шрифта и сам шрифт, соответственно

selectedBackground

Позволяет изменить цвет, которым прорисовывается фон выде-

 

ленных элементов

selectedForeground

Определяет цвет шрифта выделенного элемента

 

 

С помощью этих свойств вы сможете легко окрасить списки в любимые цвета, однако увлекаться этим занятием не стоит. Приложение чаще всего должно иметь выдержанный в едином стиле внешний вид, и по умолчанию UI-представители рисуют списки теми цветами, что больше всего соответствуют используемому менеджеру внешнего вида. Если вам нужен новый вид списков, вы можете написать собственного UI-представителя списков или переопределить «рисующий» метод paintComponent(), что позволит со вкусом приукрасить списки, к примеру, градиентными заливками. Однако чаще всего нужно по-особому нарисовать элементы списка.

Оказывается, список рисует содержащиеся в нем элементы не сам, он делегирует эту работу специальному объекту, реализующему интерфейс ListCellRenderer. Если вы вспомните методы модели ListModel, поставляющей списку данные для вывода на экран, то поймете, что списку элементы передаются в виде ссылок на базовый класс Object, то есть список способен «показывать» объекты любого типа. Как он показывает эти объекты, определяет его помощник по рисованию элементов ListCellRenderer. По умолчанию в списке используется стандартный рисующий объект DefaultListCellRenderer, который на самом деле представляет собой подкласс хорошо знакомой нам надписи JLabel. Объект DefaultListCellRenderer способен отображать значки Icon, а все остальные объекты, поставляемые моделью, он отображает в виде строк (вызывая для них метод toString()). При этом прорисовка элементов списка максимально оптимизируется, а все «ненужные» механизмы надписи, такие как оповещения об изменении текста и значка (привязанные свойства JavaBeans), автоматическая проверка корректности и другие, отключаются4.

Реализовать прорисовывающий элементы списка объект несложно — в интерфейсе

ListCellRenderer описан всего один метод getListCellRendererComponent(). Этот метод требу-

4 Интересно, что на этом оптимизация для рисующих объектов ListCellRenderer не заканчивается. Список JList (а также, например, таблицы и деревья, которые делегируют работу по прорисовке элементов другим объектам) прорисовывает их на специальном «холсте» — особом компоненте CellRendererPane. Этот компонент отключает дополнительные возможности рисования (например, двойную буферизацию Swing) и встроенную проверку корректности компонентов. Создатели Swing выяснили, что элементы в списках (а также таблицах и деревьях) рисуются так часто, что обычный способ прорисовки приводит к значительному снижению быстродействия, и встроили в библиотеку особые механизмы работы с такими элементами.

308

ГЛАВА 11

ет от вас вернуть для определенного элемента списка (для которого еще известны его позиция в списке, а также наличие фокуса и его выделения) графический компонент Component — он и будет использован для прорисовки элемента в списке. Прежде чем пробовать свои силы в написании собственного отображающего объекта, можно до конца исследовать возможности стандартного объекта DefaultListCellRenderer. Нам известно, что это надпись JLabel, а с надписями мы работать умеем, и в том числе знаем о поддержке ими HTML. Иногда этого, вкупе с поддержкой значков, может быть достаточно:

//HTMLForList.java

//Использование стандартного объекта

//DefaultListCellRenderer

import javax.swing.*; import java.awt.*;

public class HTMLForList { // данные списка

private static Object[] data = { "<html><font size=4 color=red>Первый", new ImageIcon("bullet.gif"), "<html><h2><font color=yellow>Большой"

};

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

new Runnable() { public void run() {

// создаем список

JList list = new JList(data); // помещаем его в окно

JFrame frame = new JFrame("HTMLForList"); frame.setDefaultCloseOperation(

JFrame.EXIT_ON_CLOSE); frame.setSize(200, 200); frame.add(new JScrollPane(list)); frame.setVisible(true);

} });

}

}

Здесь мы просто создаем окно со списком, который наполняем данными из массива объектов. Отличительная особенность примера — в самих данных, которые должен будет отображать список. Вы видите, что для двух элементов мы используем некоторые возможности языка разметки HTML (и не забывайте о все тех же «волшебных» символах <html> в начале строки), а еще один элемент задаем в виде значка ImageIcon, получающего изображение из файла. Запустив программу с примером, вы увидите, что отображением элементов занимается на самом деле надпись: и HTML-текст, и значок выводятся правильно и в тоже время являются элементами списка. Продемонстрированных возмож-

Списки

309

ностей объекта DefaultListCellRenderer обычно вполне хватает для создания эффектных списков в вашем приложении. Единственное нарекание к внешнему виду — все же списки нам привычнее, когда все их элементы имеют одинаковую высоту. Чтобы исправить это, мы можем использовать уже знакомое нам свойство fixedCellHeight, и указать единую высоту для всех элементов, достаточную для их корректного отображения.

Тем не менее, создать собственный объект для отображения элементов списка также несложно и иногда удобно. Главное, что нужно при этом помнить, — список JList не размещает создаваемые вашим рисующим объектом компоненты на экране, он использует их как «кисти», прорисовывая их содержимое на положенном месте списка. Ни событий от пользователя, ни перехода в видимое состояние нет и не будет: просто вызывается метод paint() вашего компонента. Стандартный объект DefaultListCellRenderer использует этот факт: для всех элементов списка, какие бы данные они не содержали, имеется только один экземпляр надписи JLabel, который заново настраивается (меняется текст или значок) для каждого отображаемого элемента списка5.

Несмотря на всю прелесть поддержки надписями языка HTML, мы еще в главе 8 узнали, что загружать с его помощью изображения неудобно. Давайте создадим библиотечный класс для отображения элементов списка, который будет способен отображать одновременно значок и текст (в том числе и HTML-текст). Чтобы не писать все «с нуля», а использовать достоинства стандартного класса DefaultListCellRenderer (он хорошо оптимизирован), унаследуем от него. Ну, а прежде всего, необходимо создать класс, который будет хранить значок и текст для нового рисующего объекта:

//com/porty/swing/ImageListElement.java

//Класс, хранящий значок и текст элемента package com.porty.swing;

import javax.swing.*;

public class ImageListElement { // значок и текст

private Icon icon;

5 Такое использование надписи JLabel хорошо описывается шаблоном проектирования приспособленец (flyweight). Суть этого шаблона проста: некоторых объектов (надписей) нужно много (множество элементов списка), а это дорого и расточительно, поэтому вместо множества объектов остается один, состояние которого меняется в зависимости от контекста (отображаемого элемента), в котором он используется.

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