350 |
ГЛАВА 12 |
позволяя создавать ползунки самых причудливых форм и раскрасок. Далее у вас есть возможность включить для ползунка особое клиентского свойство, делающее его «наполняемым» по мере перемещения регулятора, то есть похожим на индикатор процесса (см. следующий раздел). Иногда такой визуальный эффект помогает лучше определить роль ползунка в приложении. Рассмотрим пример:
//SliderAdditionalFeatures.java
//Дополнительные возможности ползунков import javax.swing.*;
import java.util.*; import java.awt.*;
public class SliderAdditionalFeatures extends JFrame { public SliderAdditionalFeatures() {
super("SliderAdditionalFeatures"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//используем стандартную модель
BoundedRangeModel model =
new DefaultBoundedRangeModel(10, 0, 0, 60);
//таблица с надписями
Dictionary labels = new Hashtable(); labels.put(0, new JLabel(
"<html><font color=red size=4>Ноль")); labels.put(10, new JLabel(
"<html><font color=green size=3>Двадцать")); labels.put(50, new JLabel(
"<html><font color=yellow size=5>Много")); labels.put(60, new JLabel(
new ImageIcon("caution.gif"))); // настройка первого ползунка
JSlider slider1 = new JSlider(JSlider.VERTICAL); slider1.setModel(model); slider1.setLabelTable(labels); slider1.setPaintLabels(true);
//"наполняемый" инвертированный ползунок
JSlider slider2 = new JSlider(model); slider2.putClientProperty(
"JSlider.isFilled", Boolean.TRUE); slider2.setInverted(true); slider2.setPaintTicks(true); slider2.setMajorTickSpacing(10);
//добавляем компоненты в окно setLayout(new FlowLayout());
Диапазоны значений |
351 |
add(slider1);
add(slider2);
// выводим окно на экран setSize(300, 300); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new SliderAdditionalFeatures(); } });
}
}
Пример демонстрирует все дополнительные возможности ползунков JSlider. Данные мы будем хранить в стандартной модели DefaultBoundedRangeModel. Минимальное значение равно нулю, максимальное — шестидесяти, а текущее значение при запуске программы — десяти. Созданную модель мы используем для двух ползунков, которые и продемонстрируют вам дополнительные возможности. У первого ползунка мы создаем собственные надписи для наиболее важных значений, их необходимо поместить в любой подходящий подкласс абстрактной таблицы Dictionary2, для примера была выбрана таблица Hashtable. Целым значениям, автоматически преобразованным в объекты Integer, можно сопоставлять любые компоненты, унаследованные от базового класса JComponent (в примере использованы прекрасно знакомые нам надписи JLabel, которые способны не только выводить текст и значки, но и поддерживают язык HTML). В первых трех метках мы применили надписи с красочным HTML-текстом, а последнее (максимальное) деление шкалы пометили особым значком. Заметьте, что создавать надписи можно для любых значений, и это позволяет получать весьма эффектные ползунки. После подготовки таблицы с надписями ее нужно передать в метод setLabelTable(). Первый ползунок является вертикальным, обратите внимание, несмотря на наличие нестандартных надписей их все равно приходится включать методом setPaintLabels().
Второй ползунок демонстрирует еще кое-что из арсенала класса JSlider. Он является «наполняемым»: по мере передвижения регулятора полоса (шкала), по которой он движется, заполняется специальным цветом. «Наполняемые» ползунки довольно эффектны — пользователь воочию видит, как выбирается все большее значение; особенно удобны такие ползунки, когда нужно показать объем или размер каких-либо объектов. Чтобы сделать ползунок «наполняемым», нужно изменить клиентское свойство с названием JSlider.isFilled, сопоставив ему объект Boolean со значением true. Правда, имейте в виду, что на данный момент «наполняемые» ползунки поддерживаются только внешним видом Metal, остальные поставляемые вместе с JDK внешние виды (Nimbus, Windows, Motif) это свойство игнорируют3. Помимо того что второй ползунок является «наполняемым», он еще и инвертированный, то есть значения в нем отсчитываются справа налево. Такое поведение чаще всего требуется, чтобы локализовать программу
2 Начиная еще с покрытого древностью пакета JDK 1.2, класс Dictionary объявлен устаревшим, вместо него рекомендуется использовать карты Map. Но ползунки были разработаны раньше, и менять уже устоявшийся программный интерфейс (что привело бы к неработоспособности уже написанного кода) создатели Swing не стали, а нам остается лишь смириться с этим и использовать устаревший класс. Дополнительных методов за все это время также не добавилось.
3 С другой стороны, почти все внешние виды от сторонних производителей свойство «наполняемости» поддерживают.
352 |
ГЛАВА 12 |
для языков с письмом справа налево, но можно использовать инвертированные ползунки и в обычных программах, в некоторых ситуациях это может внести в интерфейс разнообразие.
Оба ползунка добавляются в панель содержимого окна, которое после этого выводится на экран. Запустив программу с примером, вы сможете оценить описанные в этом разделе возможности ползунков JSlider и при желании применить их в своих пользовательских интерфейсах.
Индикаторы процесса JProgressBar
Индикаторы процесса JProgressBar наглядно демонстрируют пользователю, на каком этапе выполнения находится некий процесс, так что пользователь сможет понять, как долго остается ждать его завершения. Такие индикаторы выводятся на экран в виде непрерывно меняющейся полосы, чаще всего с текстом, который показывает (обычно в процентах), насколько выполнен процесс, или каким-либо другим образом поясняет, что в данный момент происходит. Индикаторы процесса — незаменимы для создания качественного пользовательского интерфейса. Время пользователя следует ценить, а благодаря индикаторам процесса он увидит, сколько этого времени ему понадобится для завершения какого-либо действия (и решит, стоит ли ждать завершения этого действия вообще).
Индикаторы процесса JProgressBar — чрезвычайно незатейливые компоненты, одни из самых простых в Swing. Данные они черпают из модели BoundedRangeModel, предназначенной для хранения ограниченного диапазона чисел. Модель BoundedRangeModel уже знакома нам по ползункам JSlider. Как вы помните, модель BoundedRangeModel хранит четыре целых (int) числа: максимальное, минимальное, текущее, а также внутренний диапазон. Индикаторам процесса JProgressBar требуются лишь три из этих значений: максимальное, минимальное и текущее. Именно на основе трех чисел и прорисовывается индикатор JProgressBar. Как правило, минимальное и максимальное значения задаются сразу же (вы знаете либо промежуток времени, который займет процесс, либо диапазон данных, которыми он оперирует), а текущее значение постоянно обновляется по мере выполнения процесса. Писать свою реализацию модели BoundedRangeModel вряд ли имеет смысл, она слишком проста: со всеми возможными случаями ее использования прекрасно справится стандартная реализация, описанная в знакомом нам по тем же ползункам классе DefaultBoundedRangeModel.
Диапазоны значений |
353 |
Процесс, выполнение которого наглядно выводит на экран индикатор JProgressBar, необходимо поместить в отдельный поток выполнения (Thread) или использовать инструмент SwingWorker, чтобы остальной интерфейс программы не страдал, если процесс окажется требовательным к загрузке процессора и других ресурсов, а значение индикатора (и при необходимости его текст) можно было непрерывно обновлять. Все это мы подробно обсуждали в главе 3. Рассмотрим теперь несложный пример, в котором попробуем применить самые полезные возможности индикатора процесса JProgressBar:
//UsingProgressBars.java
//Использование основных возможностей
//компонента JProgressBar
import javax.swing.*; import java.awt.*;
public class UsingProgressBars extends JFrame {
//максимальное значение (100%) private int MAX = 100;
//будем использовать общую модель private BoundedRangeModel model; public UsingProgressBars() {
super("UsingProgressBars"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//создаем стандартную модель
model = new DefaultBoundedRangeModel(5, 0, 0, MAX); // горизонтальный индикатор
JProgressBar progress1 = new JProgressBar(model); progress1.setStringPainted(true);
//вертикальный индикатор
JProgressBar progress2 =
new JProgressBar(JProgressBar.VERTICAL); progress2.setModel(model); progress2.setStringPainted(true); progress2.setString("Немного терпения...");
//добавляем индикаторы в окно setLayout(new FlowLayout()); add(progress1); add(progress2);
//выводим окно на экран
setSize(400, 300); setVisible(true);
// запускаем "процесс"
new LongProcess().execute();
}
354 |
ГЛАВА 12 |
// эмуляция |
долгого процесса |
class LongProcess extends SwingWorker<String,Integer> { |
|
// работаем до завершения процесса |
|
protected |
String doInBackground() throws Exception { |
int current = 5; |
|
while ( |
current <= MAX ) { |
//обновляем внешний вид publish(current++);
//случайная задержка
Thread.sleep((int)(Math.random()*1000));
}
return "Готово";
}
// выполняется в потоке рассылки событий
public void process(java.util.List<Integer> chunks) { // увеличиваем текущее значение model.setValue(chunks.get(0));
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new UsingProgressBars(); } });
}
}
Итак, мы создаем небольшое окно, в котором разместятся два индикатора процесса. Прежде всего необходимо создать поставщика данных для индикаторов — модель BoundedRangeModel. Мы задействуем стандартную модель, задав в конструкторе текущее значение равным пяти, внутренний диапазон — нулю (он нам не понадобится), минимальное значение — также равным нулю, а максимальное значение — ста. Полученная модель потребуется для обоих индикаторов, так что они будут синхронно показывать ход процесса. С другой стороны, как это принято в Swing, вы можете делать вид, что модели нет, задавая минимальное и максимальное значения в особом конструкторе и при необходимости меняя их или текущее значение методами класса JProgressBar. Но это не так элегантно.
Далее мы создаем сами индикаторы процесса JProgressBar. Индикаторы могут быть вертикальными и горизонтальными, управляет этим свойство orientation, но задать ориентацию индикатора можно и в конструкторе. Если вы не указываете ориентацию индикатора процесса явно, он считается горизонтальным. Первый индикатор создается конструктором, принимающим в качестве параметра нашу модель BoundedRangeModel. По умолчанию никакого текста индикатор прорисовывать не будет, чтобы он начал это делать, следует вызвать метод setStringPainted(true). Стандартно прорисовывается строка, показывающая, сколько
впроцентах от максимального занимает текущее значение, то есть какая часть процесса уже позади (значения эти хранятся в модели). Второй индикатор создается конструктором,
вкотором мы указываем его ориентацию: он будет вертикальным. Далее мы присоединяем к нему нашу модель методом setModel(), включаем прорисовку текста уже известным мето-