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

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

400

ГЛАВА 13

Особенности реализации класса JViewport

Прокрутка часто становится «узким местом» графических программ, особенно если прокручиваются большие сложные компоненты, прорисовка которых целиком стоит дорого и отнимает немало ресурсов. В компоненте JViewport для смены изображения используется блиттинг (blitting) — побитовое копирование изображения. Вместо перерисовки всего изображения та часть его, которая после смены изображения все равно остается на экране (а такая часть почти всегда есть, особенно при постепенной прокрутке) копируется со старого изображения (посредством блиттинга), а новые части изображения прорисовываются заново. Так удается намного ускорить производительность прокрутки, и требования к памяти практически не повышаются: мы помним, что прорисовка компонентов Swing происходит с двойной буферизацией, и класс RepaintManager хранит общее внеэкранное изображение всего интерфейса. Из этого изображения JViewport и копирует старые, но все еще необходимые, части.

Компонент, часть которого отображает класс JViewport (вид), является его единственным потомком. Чтобы избежать вмешательства базовой системы прорисовки в свои действия, класс JViewport переопределяет два метода. Метод isOptimizingDrawingEnabled() возвращает false, а это означает, что механизмы прорисовки не будут бездумно вызывать прорисовку всех потомков компонента JViewport, и возложат их прорисовку на метод paint(). Переопределяется и метод paint(): вместо перерисовки всего «видоискателя» он копирует все еще необходимые части (применяя блиттинг), а остальное дополняет, рисуя новые части заново. Правда, из-за переопределения метода paint() класс JViewport не может обладать рамкой — метод paintBorder() больше не вызывается основным рисующим методом paint(). Так что если вы попытаетесь сменить рамку «видоискателя» методом setBorder(), то получите исключение. Но мы уже знаем способ, как обойти эту проблему: в классе JScrollPane имеется свойство viewportBorder, именно оно позволит вам задать рамку для «видоискателя» вашей панели прокрутки. Если же вы используете класс JViewport сам по себе, то рамку можно задать для вспомогательной панели, в которой затем разместится «видоискатель».

Для программиста-клиента особая настройка системы рисования компонента JViewport остается за кадром, главное — подходящая производительность. Правда, у вас есть возможность повлиять на выбор метода прокрутки: как мы уже видели, по умолчанию используется блиттинг, но есть и еще два средства: специальное внеэкранное изображение для всего вида (это средство может работать быстрее, но в некоторых случаях, если компонент велик, ведет к неоправданным затратам памяти) или отказ от оптимизации и полная перерисовка всего компонента при прокрутке (вряд ли стоит полагаться на этот режим, иначе вы рискуете значительно снизить производительность своего приложения). Выбрать алгоритм прокрутки позволяет метод setScrollMode() класса JViewport.

Заголовки и уголки панели прокрутки JScrollPane

Панель прокрутки JScrollPane позволяет не только организовать прокрутку компонента, но при необходимости дополнить ее чрезвычайно полезными возможностями. Заголовки (headers) панели прокрутки дополняют прокручиваемый компонент сверху и слева, предоставляя пользователю вспомогательную информацию о том содержимом, что он прокручивает. Как правило, заголовки используются в качестве навигационных или информационных дополнений: линеек или координатных осей, которые помогают быстро определить размеры прокручиваемой области, текущую позицию или получить иную вспомогательную информацию. Интересно, что заголовки находятся в отдельных «видоискателях» JViewport, так же как и основное содержимое панели прокрутки. Это позволяет им иметь большой размер, как правило,

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

401

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

Уголки (corners) панели прокрутки реже применяются в приложениях. Это обычные компоненты, которые занимают пустые места по углам панели прокрутки. Интересно, что этих пустых мест может и не быть — они появляются только при наличии полос прокрутки и заголовков. В уголки обычно добавляются компоненты, которые выполняют некоторое масштабирование прокручиваемого изображения, переход на позицию по некоторому признаку или вызов контекстного меню с подходящим списком команд. Заголовки и уголки располагаются так, как показано на рис. 13.1.

Заголовки

Уголок

Полосы

прокрутки

Рис. 13.1. Заголовки и уголки панели прокрутки

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

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

//HeadersAndCorners.java

//Заголовки и "уголки" панели прокрутки JScrollPane import javax.swing.*;

import java.awt.*;

402

ГЛАВА 13

public class HeadersAndCorners extends JFrame { // надпись с большим изображением

private JLabel label = new JLabel( new ImageIcon("image.jpg"));

public HeadersAndCorners() { super("HeadersAndCorners"); setDefaultCloseOperation(EXIT_ON_CLOSE); // создаем панель прокрутки

JScrollPane scroll = new JScrollPane(label);

//устанавливаем заголовки scroll.setColumnHeaderView(new XHeader()); scroll.setRowHeaderView(new YHeader());

//устанавливаем левый верхний "уголок" scroll.setCorner(JScrollPane.UPPER_LEFT_CORNER,

new JButton(new ImageIcon("Print16.gif")));

//выводим окно на экран

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

}

// заголовок по оси X

class XHeader extends JPanel { // размер заголовка

public Dimension getPreferredSize() { return new Dimension(

label.getPreferredSize().width, 20);

}

// прорисовываем линейку

public void paintComponent(Graphics g) { super.paintComponent(g);

int width = getWidth();

for (int i=0; i<width; i+=50) { g.drawString("" + i, i, 15);

}

}

}

// заголовок по оси Y

class YHeader extends JPanel { // размер заголовка

public Dimension getPreferredSize() { return new Dimension(

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

403

20, label.getPreferredSize().height);

}

// прорисовываем линейку

public void paintComponent(Graphics g) { super.paintComponent(g);

int height = getHeight();

for (int i=0; i<height; i+=50) { g.drawString("" + i, 0, i);

}

}

}

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

new Runnable() {

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

}

}

Мы создаем небольшое окно, в центре панели содержимого которого разместится панель прокрутки JScrollPane. «Прокручивать» мы будем надпись JLabel с изображением большого размера, которое загружается из файла. В дополнение мы добавляем к панели прокрутки пару заголовков и один уголок.

Задать заголовки позволяют свойства columnHeaderView и rowHeaderView7 (верхний и левый заголовки, соответственно, или, что то же самое, заголовки для столбцов и строк). В качестве заголовков будут использованы специальные компоненты, написанные нами в виде внутренних классов. Они унаследованы от панелей JPanel и прорисовывают координатную сетку по оси X и Y с шагом в 50 пикселов. Обратите внимание на то, какой размер возвращается методами getPreferredSize(): по одной из осей он постоянен, а по второй зависит от компонента, находящегося в панели прокрутки. При создании заголовков чаще всего приходится поступать именно так: размеры заголовков должны совпадать с размерами области прокрутки, иначе они будут не в состоянии сообщить информацию обо всей прокручиваемой области.

Далее мы задаем левый верхний уголок панели прокрутки, добавляя в него кнопку с маленьким значком печати8 (вполне удобно для пользователя, поскольку заголовки с координатами позволят ему оценить размеры изображения, а щелкнув на кнопке, он мгновенно получит печатную копию и, несомненно, оценит качество вашего приложения). Для задания уголков служит метод setCorner(), принимающий два параметра: идентификатор уголка (все четыре уголка описаны соответствующими константами класса JScrollPane) и собственно компонент, который будет уголком.

7 Здесь мы видим все ту же схему именования, используемую в классе JScrollPane — основное название принадлежит «видоискателю» (например, viewport для основного «видоискателя» панели прокрутки и columnHeader для «видоискателя» верхнего заголовка), а компоненты, которые в этих «видоискателях» располагаются, управляются свойствами с суффиксом «View», например viewportView.

8 Этот значок взят из коллекции Java Look And Feel Graphics Repository (java.sun.com).

404

ГЛАВА 13

Запустив программу с примером, вы увидите, как заголовки помогают моментально оценить размеры изображения, а кнопка печати недвусмысленно показывает возможность быстрой печати изображения. В качестве заголовков можно использовать компоненты с любой информацией об области прокрутки, хотя, без сомнения, линейки с разнообразными размерами встречаются чаще всего. Интересно, что на заголовки панели прокрутки JScrollPane полагается таблица JTable9: она помещает заголовки своих столбцов именно в верхнем заголовке панели прокрутки, так что если вы располагаете таблицу вне панели прокрутки, то ее заголовка вообще не увидите.

Полосы прокрутки JScrollBar

Пара полос прокрутки (вертикальная и горизонтальная) играет важную роль в панели прокрутки JScrollPane, поскольку именно полосы прокрутки предоставляют пользователю возможность перемещаться по области прокрутки, различными способами: щелчками на кнопках полос, щелчками на полосе или просто перетаскиванием бегунка. Полосы прокрутки в Swing реализованы классом JScrollBar. Это самый настоящий компонент, который вы можете добавлять в контейнеры и использовать в своих программах, тем не менее отдельно от панели прокрутки он применяется редко.

Полоса прокрутки JScrollBar чрезвычайно похожа на исследованный нами в главе 12 ползунок JSlider: выполнять прокрутку можно, перетаскивая бегунок (в ползунке — регулятор) или щелкая мышью на полосе (шкале). Более того, оба компонента используют модель ограниченного диапазона данных BoundedRangeModel. Правда, есть и отличия: полоса прокрутки обладает кнопками, а ползунок — нет, кроме того, по-разному обрабатывается значение внутреннего диапазона, хранимое в модели. Ползунки им не пользуются, а полоса прокрутки на основании этого значения прорисовывает свою видимую часть. Собственно, эти несколько отличий и объясняют различное назначение двух схожих компонентов: ползунок используется как самостоятельный компонент, а полоса прокрутки — в составе панели прокрутки, как вспомогательный компонент.

Применять полосы прокрутки JScrollBar очень легко, отличий от ползунков практически нет. Вы настраиваете модель (как правило, используя стандартную реализацию DefaultBoundedRangeModel), передаете ее в конструктор и добавляете к модели слушателя изменений значений ChangeListener. Столь же легко задать или сменить ориентацию полосы: она может быть горизонтальной или вертикальной. Именно таким образом на-

9 Мы подробно обсудим таблицы в главе 17.

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