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

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

380

ГЛАВА 13

ши (как правило, Alt) и клавиши латинского символа (проблемы с русскими символами мы уже обсудили), при нажатии которых подходящая вкладка мгновенно активизируется. Мнемоники позволяют максимально ускорить процесс работы с приложением для опытных пользователей, позволяя перемещаться по интерфейсу молниеносными комбинациями клавиш. Однако, как мы уже знаем, в Swing из-за проблем с символами других языков число мнемоник довольно ограничено (приходится укладываться в 26 символов латинского алфавита). Впрочем, панель с вкладками чаще всего встречается в диалоговых окнах, а в отдельных окнах все сочетания клавиш собственные, так что за нехватку символов, как правило, можно не опасаться.

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

Рассмотрим небольшой пример использования описанных возможностей панели

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

//TabAdditionalFeatures.java

//Дополнительные возможности панелей с вкладками import javax.swing.*;

import java.awt.*;

public class TabAdditionalFeatures extends JFrame { public TabAdditionalFeatures() {

super("TabAdditionalFeatures"); setDefaultCloseOperation(EXIT_ON_CLOSE); // панель с вкладками

JTabbedPane tabs = new JTabbedPane(); tabs.addTab("Первая вкладка", new JPanel()); tabs.addTab("Вторая вкладка (S)", new JPanel()); tabs.addTab("Интересная вкладка", new JPanel());

//зададаем мнемоники tabs.setMnemonicAt(0, 'П'); tabs.setMnemonicAt(1, 'S'); tabs.setMnemonicAt(2, 'И');

//активизируем последнюю вкладку tabs.setEnabledAt(2, false);

//выводим окно на экран add(tabs);

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

}

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

Управление пространством

381

new Runnable() {

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

}

}

Мы наследуем наш класс от окна с рамкой JFrame и добавляем в панель содержимого панель с вкладками JTabbedPane. У нас будет три вкладки, для простоты мы сопоставляем им пустые панели JPanel. Для каждой из вкладок мы устанавливаем мнемоники методом setMnemonicAt(); этому методу помимо символа мнемоники нужно указать индекс вкладки, для которой меняется мнемоника. Для двух вкладок мы передали в качестве мнемоник русские символы (и вы увидите их подчеркнутыми на экране, только вот работать они, увы, не будут; подробно причины и возможные решения этой проблемы мы обсуждали в главе 9), а для третьей использовали не слишком эстетичный, но зато надежный способ, включив в мнемонику латинский символ, который мы к тому же добавили к русской надписи в скобках, так что пользователь сможет увидеть мнемонику вкладки. Помимо мнемоник пример демонстрирует и отключение вкладок: методом setEnabledAt() мы отключили последнюю (судя по надписи, самую интересную) вкладку, так что перейти на нее пользователь не сможет.

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

Размещение компонентов во вкладках

До сих пор рассматриваемые нами вкладки были полностью информационными компонентами — мы могли размещать на них текст и значки, а компонент JTabbedPane сам отслеживал способы их переключения и решал, как рисовать на них. Однако в последнее время вкладки наделяются все большими полномочиями, в качестве самого популярного расширения можно вспомнить маленькие кнопки на вкладках, закрывающие их, если интерфейс приложения подразумевает такое поведение (вспомните все популярные браузеры). Библиотека Swing также не осталась в стороне и позволяет тонко настроить внешний вид вкладок.

Вспомнив другие компоненты библиотеки Swing, можно было бы подумать, что компонент JTabbedPane предлагает рисовать вкладки отдельному рисующему объекту (renderer), как мы это уже видели в списках и еще не раз увидим. Однако в случае с вкладками все одновременно и проще и сложнее. Как таковой «прорисовки» нет, вы просто можете добавить в то место, где рисуется вкладка, свой собственный компонент, будь то простая кнопка или плотно упакованная компонентами сложная составная панель. Казалось бы, нет ничего проще, чем добавить свой компонент и нарисовать в нем все, что нам нужно, однако здесь есть свои весьма острые подводные камни. Давайте сразу же

382

ГЛАВА 13

рассмотрим несложный пример, в котором попробуем добавить кнопку для закрытия вкладки и флажок, а затем обсудим все его тонкости:

//TabComponents.java

//Размещение компонентов во вкладках import javax.swing.*;

import java.awt.*;

import java.awt.event.ActionEvent;

public class TabComponents extends JFrame { public TabComponents() {

super("TabComponents"); setDefaultCloseOperation(EXIT_ON_CLOSE); // панель с вкладками

final JTabbedPane tabs = new JTabbedPane(); tabs.addTab(null, new JPanel()); tabs.addTab(null, new JPanel());

// флажок во вкладке

JCheckBox checkBox = new JCheckBox("Флажок!"); checkBox.setOpaque(false); tabs.setTabComponentAt(0, checkBox);

// вкладка с надписью и кнопкой закрытия

final JPanel panel = new JPanel();

JLabel label = new JLabel("Можно закрыть!");

JButton closeButton = new JButton(new AbstractAction() {

{

putValue(SMALL_ICON, new ImageIcon("close.png"));

}

public void actionPerformed(ActionEvent e) {

// нужно определить вкладку, в которой находится кнопка tabs.removeTabAt(

tabs.indexOfTabComponent(panel));

}

});

closeButton.setBorderPainted(false);

closeButton.setContentAreaFilled(false);

panel.setOpaque(false);

panel.add(label);

panel.add(closeButton); tabs.setTabComponentAt(1, panel); // выводим окно на экран add(tabs);

Управление пространством

383

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

}

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

new Runnable() {

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

}

}

Пример очень прост — мы создаем всего лишь одну панель со вкладками JTabbedPane и добавляем ее в центр окна. В ней будут находиться две вкладки, содержащие две пустые панели. Так как мы собираемся отдать отображение вкладок отдельным компонентам, вместо названия вкладок в метод addTab() первым параметром передаются пустые ссылки null.

Далее настает черед настроить компоненты. Первой вкладкой будет заниматься флажок JCheckBox. Мы создаем его и устанавливаем свойство непрозрачности (opaque) в false, чтобы он своим цветом фона не перекрывал фон и внешний вид вкладки, в противном случае картина получится не слишком привлекательная. Не во всех внешних видах Swing флажки непрозрачны, однако во внешнем виде по умолчанию это именно так, и на всякий случай стоит всегда менять это свойство. После этого нам остается указать, что для первой вкладки мы будем использовать флажок методом setTabComponentAt(), требующим в качестве параметров номер вкладки и ссылку на компонент.

Для второй вкладки настраивается отдельная панель, которая будет отображать название вкладки (с помощью надписи) и хранить кнопку для закрытия вкладки. Если с надписью все очевидно, то кнопка создается на основе команды Action, что позволяет нам «одним махом» указать и значок кнопки, и описать, что будет происходить при нажатии кнопки1. Закрыть вкладку оказывается не совсем просто — как нам узнать, на какой вкладке пользовать нажал кнопку? Следить за курсором мыши в этом случае не слишком-то рационально — в разных внешних видах и операционных системах кнопку можно нажать по-разному. В примере нам помогает метод indexOfTabComponent() класса JTabbedPane, который позволяет узнать, на какой вкладке находится данный компонент. Так как наша кнопка находится во вспомогательной панели, нам приходится передавать ее в этот метод, чтобы узнать, в какой же вкладке мы находимся, и наконец удалять ее. Если бы у нас было много вкладок с кнопками, нам бы пришлось хранить в слушателе нажатия кнопки ссылку на панель, которая размещается во вкладке.

1 Мы подробно обсуждали команды Action в главе 9, посвященной кнопкам.

384

ГЛАВА 13

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

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

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

Мы задаем текст для флажка и надписи при их создании, но что будет, если надпись для вкладки поменяется динамически во время работы программы? Опятьтаки необходимо следить за этим вручную, неплохим вариантом может быть слежение за привязанным свойством «indexForTitle», с помощью метода JavaBeans addPropertyChangeListener(), доступного в каждом компоненте Swing.

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

Разделяемая панель JSplitPane

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

Использовать разделяемую панель особенно удобно там, где есть требовательные к пространству контейнера компоненты, одновременная работа с которыми маловероятна. Работая с одним компонентом, пользователь сможет для удобства увеличить его, а при переключении на другой компонент увеличить размер последнего. В качестве примера можно вспомнить разнообразные среды разработки программ, использующие разделяемые панели для размещения редактора кода или форм и дерева проекта. При работе с проектом вам нужно больше места под его дерево и таблицу свойств, а при написании кода место требуется уже под редактор и диагностические сообщения компилятора. Разделяемая панель в этом случае случает удачной альтернативой многооконному интерфейсу (MDI), который в последнее время потерял свои позиции при проектировании интерфейсов.

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