?

Log in

Previous Entry | Next Entry

Очередная статья родилась. Времени на нее я потратил чуть не четыре месяца – было много работы, да и статья обширная, иллюстративных примеров много потребовалось. Статья посвящена внутреннему устройству GUI в Java, а точнее в Swing, ибо AWT, во-первых, устарел, во-вторых, устроен немного по-другому.

В общем, сама статья выложена: http://www.skipy.ru/technics/gui_int.html. Здесь будем ее обсуждать.

Comments

( 8 comments — Leave a comment )
(Anonymous)
Dec. 27th, 2010 11:05 pm (UTC)
расширение класса Highlighter
День добрый, Евгений.

Спасибо Вам за статью. Как всегда эксклюзивно и в "мемориз" :).

Теперь вопрос: предположим я хочу "подсвечивать" при наведении мыши узлы в дереве (JTree), строки в таблице (JTable) или вообще свой выдуманный компонент. Если не ошибаюсь, то необходимые мне компоненты являются JLabel. Стоит ли изменить класс Highlighter для работы с JLabel и подключить его как к JTree и JTable? Или лучше обрабатывать подсветку компонентов отдельно?

С уважением, Павел.

P.S. С наступающим! Желаю всего наилучшего и почаще пишите свои статьи. Нам очень интересно.
skipy_ru
Feb. 14th, 2011 01:34 pm (UTC)
Re: расширение класса Highlighter
Добрый день, Павел!

JLabel не имеет никакого отношения к отрисовке компонентов дерева и таблицы. Он берется исключительно для удобства, т.е. умеет отрисовывать изображения. Я могу нарисовать свой компонент и точно также использовать в качестве CellRenderer-а.

UI JLabel переопределять, имхо, не стоит. Дело в том, что он будет работать исключительно при отрисовке, а отслеживать перемещения мыши не будет по любому - нет соответствующей компоненты на форме. Перемещения мыши могут отслеживать только JTable/JTree. Причем алгоритм определения элемента под мышью будет принципиально отличаться.

Навскидку я бы писал CellRenderer-ы (разные!) на основе JLabel, слушающие на таблице или дереве перемещения мыши. По движению они должны отслеживать, какой элемент под мышью, при смене вызывать перерисовку. При перерисовке сравнивать элементы с запомненным активным, если совпал (по ссылке!) - отрисовывать выделенным.

P.S. Сорри за задержку с ответом - запарка на работе.
magicien
Aug. 11th, 2011 12:12 pm (UTC)
Re: расширение класса Highlighter
Евгений, спасибо за статьи. Помогите решить след:
Есть класс расш. DefaultTreeCellRenderer у которого есть метод getComponent, он возвращает JPanel, в котором два JLabel-а. При наведении мыши на узел дерева, надо определить, какой JLable находится под мышкой. Возможно ли такое сделать? Не получается Renderer-у слушать перемещение мыши.
miklegarin
Jan. 16th, 2011 07:19 pm (UTC)
Возможные уточнения/дополнения к статье
Хотел бы, сперва, сказать большое спасибо, ибо даже после 3ех лет плотной работы со Swing'ом нашел кое-что новое и интересное :)


Также хотел бы отметить несколько вещей из Вашей статьи, которые можно было бы уточнить/дополнить...


1) К части про "Области отсечения"

Конечно, оптимизация перерисовки отсечением ненужных частей может являться неотъемлимой частью оптимизации отрисовки, но все же, как мне кажется, clip реже используется в этих целях при написании своих компонентов.

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

По своему недавнему опыту могу сказать, что пара "неудачных" методов по вычислению места, где должна отрисоваться та или иная часть компонента, по времени исполнения перекрывают в сотни раз саму отрисовку.

Как мне кажется, clip чаще используется в двух следующих случаях:

- Стандартный clip, обрезающий отрисовку по краям компонента
В данном случае от разработчика ничего не требуется, кроме как передать управление super.paint() в начале отрисовки для установки оным стандартного clip'а

- Свой clip, необходимый для корректной отрисовки содержимого
Например если необходимо внутри компонента ограничивать отрисовки неких внутренних частей.
Главное при этом не забывать, что при отрисовке устанавливается всего 1 clip и можно нечаянно выйти за границы самого компонента, установив свой "внутренний" clip.

Но это, все же, сугубо мое личное мнение.


2) "DebugGraphics.BUFFERED_OPTION – создает отдельное окно и показывает в нем отрисовку, проводимую на буфере компоненты. Полезно при включенной буферизации. Если честно, мне не удалось включить этот режим."

Как-то давно тоже удивился, что данная опция вообще ничего не делает. Т.е. фактически - не работает.

Также на паре ресурсов по Java находил заметки на эту тему:

DebugGraphics.BUFFERED_OPTION: This is supposed to pop up a frame showing rendering as it occurs in the offscreen buffer if double-buffereing is enabled. As of the Java 2 FCS this option is not functional.


3) К части статьи - "Прозрачность компонент и смысл свойства opaque"

Я бы добавил, что opaque=true, конечно хорошо, но у него есть и обратная сторона медали.
Такая непрозрачность может сильно осложнить написание/отладку компонентов, которые могут динамически перерисовываться со временем, вне зависимости от действий пользователя.

Самым ярким примером (из доступных) являются 2 перекрывающие друг друга кнопки с установленным нативным Windows LnF в ОС WindowsVista/Windows7. Они имеют вредную привычку "моргать" при наличии фокуса.
Если их расположить друг поверх друга на панели (например) с null лэйаутом и сделать непрозрачными (setOpaque(true)), то после пары переключений/наведений минусы проявят себя во всей красе :)

Этот случай конечно немного наигранный, но он самый простой.
Я уже встречал и более сложные моменты, когда после нескольких часов мучений и гор "ужастного" кода все решалось простой установкой opaque в false (и далее, если надо, ручным отрисовыванием фона компонента).


P.S. Из того, что я отметил, вроде все :)
Было бы интересно интересно почитать схожие статьи непосредственно про производительность при отрисовке, а точнее, что-то вроде такого списка:
- Частые ошибки, приводящие к потере производительности при работе с Graphics/Graphics2D в Java
- Возможные пути оптимизации отрисовки
- Сравнения производительности разных методов Graphics/Graphics2D
- Производительность отрисовки Swing-компонентов
- Более подробное описание различий paint/paintAll/print/printAll
Возможно еще что-то в этом духе... Это то, что первое пришло в голову
miklegarin
Jan. 17th, 2011 10:08 am (UTC)
Re: Возможные уточнения/дополнения к статье
Да, тут вспомнилась еще 1 вещь...

4) "paintComponent – этот метод позволяет отрисовать саму компоненту. Т.е. делает то, что в AWT делал paint(Graphics). Именно этот метод необходимо переопределять для того, чтобы отрисовать что-то на компоненте. Можно, конечно, переопределить и paint(Graphics) – а те, кто начинал с AWT, часто этим грешат! – но тогда при использовании рамок (border) начнутся сложности."

Тут также важно понимать то, что метод paintComponent зачастую вызывается при частичных перерисовках компонента с разными указанными clip'ами. Также он может быть и вовсе не вызван, если перерисовываемая часть полностью закрыта opaque компонентами.
Метод же paint будет вызываться всегда при "возможной" необходимости перерисовки (будь то перекрытие другим компонентом, ресайз или что еще). Таким образом не всегда paintComponent лучше в использовании.
Тем более в сложном графическом компоненте использование его, а не paint может породить различные проблемы с перерисовкой частей.
Но для простых компонентов, когда нет смысла/времени заморачиваться с оптимизацией - да, paintCompomponent однозначно более удачный выбор.


5) Еще одно замечание к тому же paintComponent...

После части статьи про отрисовку и рамки создается немного двойственное ощущение, что использование метода paintComponent решает проблему с рамкой, т.е. (например) делает за вас отступы и подгоняет размеры (прямо, естественно об этом не говорится, но нет и обратного).
Даже лишний раз проверил реализацию метода paint/paintComponent, чтобы удостовериться, что я все еще в себе :)

Думаю, тут стоит еще раз напомнить, что замена paintComponent (вместо paint) решает проблему с отрисовкой бордера, а не с отступами, создаваемыми бордером.
skipy_ru
Feb. 14th, 2011 01:40 pm (UTC)
Re: Возможные уточнения/дополнения к статье
Добрый день!

Спасибо за комментарии, обстоятельно. В принципе, со всем согласен. Если соберусь пересматривать/дополнять, возможно, что-то добавлю в статью. А пока – Вы очень хорошо всё это изложили и без меня, а ссылка на это обсуждение в статье есть.

Касательно исследований производительности и т.п. - явно не сейчас. Мне на ответ найти времени - месяц понадобился.
miklegarin
Feb. 14th, 2011 02:18 pm (UTC)
Re: Возможные уточнения/дополнения к статье
Ну, с кое-чем из моих замечаний (думаю) можно было бы и поспорить, как мне уже сейчас кажется...

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

В любом случае еще раз спасибо за содержательную статью!
Буду ждать продолжения :)
Евгений Миркес
Sep. 7th, 2012 04:43 am (UTC)
Добрый день. Спасибо огромное за статью, очень помогла.
Единственное замечание от зануды. Согласно правилам русского языка компонент - мужского рода и только для математиков в отношении математических текстов разрешается использование в женском роде (я сам математик).
Еще раз спасибо за содержание.
Миркес.
( 8 comments — Leave a comment )