JPanel внутри проблемы рисования JScrollPane

Я поместил объект JPanel внутрь JScrollPane, и прокрутка работает, как и ожидалось. Переопределяя paintComponent(), я попытался выполнить пользовательское рисование в объекте JPanel. Однако, когда объект JPanel помещается в JScrollPane, JPanel больше не рисует должным образом (вместо этого показывает только цвет фона).

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

Следующие фрагменты кода показывают мой текущий проект:

a) paintComponent() из моей JPanel (этот метод был сокращен только до рисования, фактическая краска будет постоянно обновляться BufferedImage, поставляемым из другого потока, вместо этого большого розового статического поля):

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

    //Render Frame
    // 'RXDisplayCanvas' is the JPanel.
    Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();

    G2D.setColor(Color.PINK);
    //800 and 600 are arbitrary values for this example, real values are calculated at runtime. The value calculation code is verified to work (as its used elsewhere in a similar scenario)
    G2D.fillRect(0, 0, 800, 600);
    G2D.dispose();
}

б) Поток 'updater', который периодически перерисовывает кадр:

@Override
public void run() {
    long MaxFrameTime;
    long Time;

    while(isVisible()){
        // 'FPSLimit' is a integer value (default to 30)
        MaxFrameTime = Math.round(1000000000.0 / FPSLimit);
        Time = System.nanoTime();

        try{
            SwingUtilities.invokeAndWait(new Runnable(){
                @Override
                public void run() {
                    // 'RXDisplayCanvas' is the JPanel.
                    RXDisplayCanvas.repaint(); //When using this, the JPanel does not display correctly.

                    //RXDisplayCanvas.paintImmediately(0, 0, RXDisplayCanvas.getWidth(), RXDisplayCanvas.getHeight()); When using this, the JPanel renders correctly but flickers.
                }
            });
        }catch(InterruptedException | InvocationTargetException e){}

        Time = System.nanoTime() - Time;
        if(Time < MaxFrameTime){
            try{
                Thread.sleep(Math.round((MaxFrameTime - Time)/1000000.0));
            }catch(InterruptedException ex){}
        }
    }
}

Я принял во внимание, что repaint() не вызывает немедленной перерисовки экрана, а проблема заключается в неправильном рендеринге экрана. Когда программа остается одна, она просто отображает цвет фона JPanel до тех пор, пока JScrollPane не будет прокручен, в котором он правильно отображает один кадр, прежде чем следующий вызов repaint() рисует неправильное отображение.

При переключении repaint() на paintImmediately() (в отрывке b) кадр отображался правильно, но присутствовало сильное мерцание, когда он постоянно чередовался между отрисовкой цвета фона и отрисовкой розового прямоугольника. Я пытался добавлять и удалять менеджеры компоновки, отключать менеджер перерисовки, а также включать и отключать флаг «двойной буферизации» для обоих компонентов, в которых все приводило к одному из двух вариантов поведения, упомянутых выше (рендеринг только фона или мерцание).

Может ли кто-нибудь помочь мне в этом вопросе?

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


person initramfs    schedule 25.06.2013    source источник
comment
Как вы синхронизируете доступ к общим данным?   -  person trashgod    schedule 25.06.2013
comment
Больше не актуально, но это делается с помощью другого потока, копирующего растр BufferedImage в активный буфер дисплея. Дисплей постоянно отображает текущий BufferedImage, в то время как другой поток обновляет его (все делается с помощью синхронизированных блоков).   -  person initramfs    schedule 25.06.2013


Ответы (1)


1) Я не уверен в этом:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    // 'RXDisplayCanvas' is the JPanel.
    Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();
    ..
    G2D.dispose();
}

Я бы рекомендовал сделать:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D G2D = (Graphics2D)g;
    G2D.setColor(Color.PINK);
    G2D.fillRect(0, 0, 800, 600);
}

Обратите внимание, что я опустил getGraphics и использовал текущий, переданный в графическом контексте paintComponent.

Также обратите внимание, что я не вызываю g2d.dipose(), так как это вызывает проблемы, это должно быть сделано только для Graphic, которые вы создаете Component.getGraphics(), но в вашем случае вы даже не должны создавать контекст Graphics, поскольку он уже был создан и передан методу paintComponent. (см. this аналогичный вопрос)

2) Нет необходимости в блоке SwingUtilities.invokeXXX для repaint(), так как он потокобезопасен. Но особенно нет необходимости в SwingUtilities.invokeAndWait (поскольку это блокирующий вызов и ожидание, пока все ожидающие события AWT будут обработаны и завершится метод run()), что не очень хорошо и может также добавлять визуальные артефакты на экране, которые вы видите.

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

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

person David Kroukamp    schedule 25.06.2013
comment
Боже мой... Как я мог это пропустить? Graphics2D G2D = (Graphics2D)g; Вместо этого: (Graphics2D)RXDisplayCanvas.getGraphics(); Это действительно решает мою проблему, и я очень благодарен вам за то, что вы заметили эту крошечную ошибку, которую я не замечал в течение 3 дней... - person initramfs; 25.06.2013
comment
@CPUTerminator не проблема, иногда нам просто нужна вторая пара глаз :) - person David Kroukamp; 25.06.2013