Подтвердить что ты не робот

Как можно изменить размер Swing JWindow без мерцания?

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

При запуске кода я испытываю мерцание при изменении размера окна в отрицательных направлениях x и y, то есть вверх и влево. Похоже, что происходит, что окно изменяется и изменяется до обновления компонентов. Ниже приведена упрощенная версия кода. При запуске верхняя панель может использоваться для изменения размера окна вверх и влево. Фон окна установлен на зеленый, чтобы очистить его, где пиксели, которые я не хочу показывать.

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

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Area;

import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;

import com.sun.awt.AWTUtilities;

public class FlickerWindow extends JWindow implements MouseListener, MouseMotionListener{

    JPanel controlPanel;
    JPanel outlinePanel;
    int mouseX, mouseY;
    Rectangle windowRect;
    Rectangle cutoutRect;
    Area windowArea;

    public static void main(String[] args) {
        FlickerWindow fw = new FlickerWindow();
    }

    public FlickerWindow() {
        super();
        setLayout(new BorderLayout());
        setBounds(500, 500, 200, 200);
        setBackground(Color.GREEN);

        controlPanel = new JPanel();
        controlPanel.setBackground(Color.GRAY);
        controlPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
        controlPanel.addMouseListener(this);
        controlPanel.addMouseMotionListener(this);

        outlinePanel = new JPanel();
        outlinePanel.setBackground(Color.BLUE);
        outlinePanel.setBorder(new CompoundBorder(new EmptyBorder(2,2,2,2), new LineBorder(Color.RED, 1)));

        add(outlinePanel, BorderLayout.CENTER);
        add(controlPanel, BorderLayout.NORTH);
        add(new JButton("Dummy button"), BorderLayout.SOUTH);
        setVisible(true);
        setShape();

        addComponentListener(new ComponentAdapter() {           
            @Override
            public void componentResized(ComponentEvent e) {
                setShape();
            }});
    }


    public void paint(Graphics g) {
        // un-comment or breakpoint here to see window updates more clearly
        //try {Thread.sleep(10);} catch (Exception e) {}
        super.paint(g);
    }

    public void setShape() {
        Rectangle bounds = getBounds();
        Rectangle outlineBounds = outlinePanel.getBounds();
        Area newShape = new Area (new Rectangle(0, 0, bounds.width, bounds.height));
        newShape.subtract(new Area(new Rectangle(3, outlineBounds.y + 3, outlineBounds.width - 6, outlineBounds.height - 6)));
        setSize(bounds.width, bounds.height);
        AWTUtilities.setWindowShape(this, newShape);
    }

    public void mouseDragged(MouseEvent e) {
        int dx = e.getXOnScreen() - mouseX;
        int dy = e.getYOnScreen() - mouseY;

        Rectangle newBounds = getBounds();
        newBounds.translate(dx, dy);
        newBounds.width -= dx;
        newBounds.height -= dy;

        mouseX = e.getXOnScreen();
        mouseY = e.getYOnScreen();

        setBounds(newBounds);
    }

    public void mousePressed(MouseEvent e) {
        mouseX = e.getXOnScreen();
        mouseY = e.getYOnScreen();
    }

    public void mouseMoved(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
}

В качестве точки останова можно использовать переопределенный метод paint() или Thread.sleep() может быть раскомментирован, чтобы обеспечить более четкое представление об обновлении по мере его возникновения.

Моя проблема, похоже, связана с методом setBounds(), заставляя окно окрашиваться на экран перед его выкладкой.


Окно перед изменением размера, как и должно выглядеть:

alt text


Окно при изменении размера больше (вверх и влево), как показано в точке останова при переопределенном методе paint()):

alt text


Окно при изменении размера меньше (вниз и вправо), как показано в точке останова при переопределенном методе paint()):

alt text


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

Зеленая область с уменьшением размера до более крупного снимка экрана показывает новый фон, который нарисован до того, как будет выполнена какая-либо маска/макет, похоже, это происходит в базовом ComponentPeer или собственном оконном менеджере. Синяя область на снимке "изменить размер до меньшего" показывает фон JPanel, который отображается в данный момент, но теперь устарел. Это происходит под Linux (Ubuntu) и Windows XP.

Кто-нибудь нашел способ изменить размер Window или JWindow на задний буфер до того, как будут сделаны какие-либо изменения на экране и, таким образом, избежать этого мерцающего эффекта? Возможно, есть системное свойство java.awt...., которое можно установить, чтобы избежать этого, но я не смог его найти.


Изменить # 2: Прокомментировать вызов AWTUtilities.setWindowShape() (и, возможно, раскомментировать строку Thread.sleep(10) в paint()), а затем перетащить верхнюю панель вокруг агрессивно, чтобы четко увидеть природу мерцания.

Изменить # 3: Кто-нибудь может протестировать это поведение в Sun Java в Windows 7 или Mac OSX?

4b9b3361

Ответ 1

Я признаю, что это не очень полезный ответ, но понимание того, что именно может представлять Swing, может помочь.

См., Swing делает все свои собственные работы, а не получает немного места, чтобы опираться на ОС. Все чертежи, виджеты и т.д. - это код Java. Это не так много, что это работает медленно, так как это работает без использования ускорения 2D-графики и трюков рендеринга ОС.

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

С Swing, поскольку он управляет всем своим пространством, ОС не может выполнять какие-либо из этих трюков рендеринга, чтобы помочь вам.

Кто-то может придумать некоторую оптимизацию, которая устраняет вашу проблему на вашем компьютере, но я обеспокоен тем, что на самом деле это не будет исправлять базовую проблему. Swing работает медленно и не может ускориться.

Вы должны изучить встроенные инструментальные средства. AWT в порядке, но отсутствует много виджетов/и т.д. Он родной и встроенный, поэтому он должен быть достаточно быстрым, если это все, что вам нужно. Я частично отношусь к SWT, что и использует Eclipse, Vuze (среди прочих). Он сочетает в себе природу AWT с легкостью и особенностями Swing и, конечно же, работает везде.

РЕДАКТИРОВАТЬ: довольно ясно после прочтения некоторых ваших комментариев, что вы абсолютно понимаете, как происходит окно, - я не хочу оторваться от снисхождения. Не только это, но вас больше интересует изменение размера, которое у моего комментария не так много общего. Я бы по-прежнему рекомендовал SWT, потому что это собственный код и быстрее, но это другой ответ, чем тот, который был выше.

Ответ 2

Я только что попробовал ваш пример. Когда я изменил размер окна, я увидел небольшое щелчок. Я попытался заменить фрагмент кода, начиная с setBounds() и заканчивая на AWTUtilities.setWindowShape(this, newShape); на

setSize(newBounds.width, newBounds.height);

и увидел, что щелчок исчез. Поэтому я предлагаю вам это решение, если у вас нет особых причин использовать setBounds.

Ответ 3

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

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