Почему paintComponent() непрерывно и асинхронно вызывается без явного вызова repaint()?

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

У меня есть JFrame с JPanel и несколько child JPanels с 3 JButtons на каждом. Я также создал JComponent с именем glassPanel для JFrame (то есть myJFrame.setGlassPane(glassPanel)), что позволяет мне закрашивать JPanels и кнопки.

(1) По сути, запускается нажатием всех 3 кнопок на JPanel, glassPanel устанавливается на Visible (что, по-видимому, затем вызывает paintComponent()). Это относится к первому вопросу.

(2) В paintComponent() я рисую и раскрашиваю прямоугольники и изображения, используя двойной буфер на glassPanel. Это относится ко второму вопросу.

Вот мой соответствующий код класса GlassPanel (это не SSCCE, потому что пока это абстрактный вопрос):

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;


public class GlassPanel extends JComponent {

     @Override
     protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        setDoubleBuffered(true);
        Graphics2D g2 = (Graphics2D) g;

        g2.drawRect(x,y,width,height);
        g2.fillRect(x,y,width,height);

        g2.drawImage(img, x, y, this);
    }
}

Поместив оператор System.out.print в метод paintComponent(), я мог сказать, что он вызывается непрерывно, а также асинхронно. О том, как, по моему мнению, делается вызов, см. (1). Кроме того, скажем, я абсолютно уверен, что нигде в коде нет вызова repaint() (я проверял много-много раз). Это основа первого вопроса.

Первый раз нажимаю 3 кнопки, все идет гладко. Прямоугольник и изображения рисуются сразу. Однако, когда я нажимаю следующие 3 кнопки (в этот момент glassPanel уже было setVisible(true), а первый прямоугольник и изображение все еще на экране, закрашенные первыми 3 кнопками), второй прямоугольник и изображение загружаются только частично . Когда я перехожу от JFrame к окну Eclipse, из которого я запускаю программу, количество вызовов paintComponent() каждый раз быстро увеличивается на одну и ту же величину И частично загруженные изображения и прямоугольники ) немедленно и полностью отображаются на заднем плане JFrame. Когда я возвращаюсь к JFrame, количество вызовов снова увеличивается на точную сумму). Это основа второго вопроса.

ОБНОВЛЕНИЕ: Вот кое-что, что я прочитал:

Кроме того, когда GUI закрывается другим окном, а затем становится открытым, система рисования вызывает метод paintComponent с областью рисования, равной только что открытой области.

Мои вопросы:

(1) почему paintComponent() может так часто называться без repaint()? Или аналогичный вопрос, что может вызывать paintComponent()?

ОБНОВЛЕНИЕ: немного подсчитав, я твердо верю, что он постоянно вызывается каждым компонентом (всеми кнопками и панелями). Но все равно нет вызова repaint()...

(2) Почему изображения загружаются частично, пока я не сфокусируюсь на окне JFrame?

Обратите внимание, что я пробовал много вещей: (а) создать свой собственный двойной буфер и не использовать двойной буфер (я знаю, что это в основном для анимации), (б) переопределить и не переопределить paintComponent(), (в) рисовать, а не рисовать изображение (прямоугольник по-прежнему требуется время для загрузки), (d) абсолютно убедиться, что repaint() не было, (e) использовать и не использовать SwingUtilities.invokeLater(new Runnable() { public void run() { //stuff});, (f) выполнить оператор if только для setVisible(true) один раз.

Я могу попробовать пройти SSCCE, если нужно, но я думаю, что они более абстрактны. Спасибо!


person Alex Silverman    schedule 11.07.2013    source источник
comment
Прости, Эндрю. Я знаю, что было бы намного проще помочь с кодом (работающим) перед вами. Я знаю, что не упрощаю. Но я надеюсь, что у кого-то была похожая проблема, и он может ответить на нее, не видя никакого кода.   -  person Alex Silverman    schedule 11.07.2013
comment
добавить Mouse, KeyListener в Glasspane/JComponent с помощью System.out.print, а paintComponent вызывается из методов, реализованных в API   -  person mKorbel    schedule 11.07.2013
comment
Что бы я сделал со слушателями?   -  person Alex Silverman    schedule 11.07.2013


Ответы (1)


Что ж, думаю, я ответил на оба вопроса. Во-первых, почему paintComponent() вызывалась постоянно, было то, что на самом деле она не вызывалась постоянно. Он вызывался всеми компонентами, когда он впервые отображал графический интерфейс. Когда окно Eclipse закрывает его, а затем открывает, оно вызывается больше раз.

Во-вторых, это связано с clipBounds объекта/вещи Graphics2D. Я узнал, как clipBounds менялись для каждого вызова рисования, поэтому, когда я устанавливаю клип в начале метода paintComponent(), изображения появляются сразу. (Кстати, это выглядит ОТЛИЧНО!).

С завихрением: после отображения изображения каждое нажатие кнопки что-то делает с изображением. Хотя я так и не понял, что именно. Это почти похоже на перерисовку одного и того же изображения поверх старых изображений.

Поэтому мне нужно выяснить, как сохранить старые изображения, но рисовать новые, когда это необходимо, и только рисовать/добавлять новые на glassPanel.

ОБНОВЛЕНИЕ: вызов repaint() сразу после нажатия каждой кнопки немного помогает. Но это по-прежнему заставляет изображение немного мерцать, как будто добавляется еще один слой, когда кнопка нажата, а затем оно возвращается в нормальное состояние, когда пользователь отпускает.

person Alex Silverman    schedule 11.07.2013
comment
Рассмотрите возможность рисования на JPanel вместо JComponent, поскольку между ними существует небольшая разница, как указано в этом ответе: - ) - person nIcE cOw; 11.07.2013
comment
Проблема с использованием JPanel заключается в том, что я не могу делать много других вещей, которые хочу: проверить, виден ли он, автоматический двойной буфер, стеклянная панель и т. д. - person Alex Silverman; 11.07.2013
comment
Вы что, шутите?!, Swing по умолчанию использует двойную буферизацию во многих своих компонентах (JPanel, без сомнения, подпадает под это), посмотрите, что Учебники по Java должны сказать об этом :-) - person nIcE cOw; 11.07.2013
comment
а что со стеклопакетом? Итак, setDoubleBuffer(true) бесполезен, если я не укажу иное? Кроме того, не думаете ли вы, что проблема может заключаться в том, что буферизованное изображение перерисовывается поверх самого себя, так что было бы полезно снова создать мой собственный двойной буфер? - person Alex Silverman; 11.07.2013
comment
Я предполагаю, что частично это зависит от маршрута, по которому вы рисуете артефакты на экране, поэтому, я думаю, по этой причине нужно видеть код, если уж на то пошло. - person nIcE cOw; 11.07.2013
comment
JPanel является подклассом JComponent. В любом случае вам не следует менять вызов setDoubleBuffered внутри paintComponent, так как это может вызвать другую перерисовку. Вам также следует избегать игры с clipBounds, они устанавливаются родительским контейнером до того, как дочерний элемент будет отрисован, чтобы соответствовать требованиям границ дочернего компонента. Дополнительные сведения см. в разделе Рисование в AWT и Swing. - person MadProgrammer; 12.07.2013