Проблема рендеринга Swing курсоров при использовании модальных диалогов в Linux - PullRequest
4 голосов
/ 19 августа 2011

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

Еще лучше, когда вы устанавливаете курсор занятости перед открытием диалога.Курсор занятости отображается, но при перемещении мыши внутри, а затем вне диалогового окна курсор занятости больше не отображается.

Обратите внимание, что я наблюдаю следующую ошибку только в Linux.В Mac OS X или Windows поведение является детерминированным и непротиворечивым.

Еще один совет, в первом случае примера кода, когда мышь НЕ входит в диалоговое окно и YES_OPTION выбирается с помощью клавиатуры, заняткурсор мыши всегда отображается.Также в этом случае метка «Пожалуйста, подождите ...» на стеклянной панели никогда не окрашивается.

Вот SSCCE, демонстрирующий эти ошибки:

import java.awt.event.*;
import javax.swing.*;

public class TestFrame extends JFrame {

private JPanel panel;
private JPanel glassPane;

public TestFrame() {
    final JButton button1 = new JButton(new AbstractAction("Start activity indicator after closing the dialog") {
        @Override
        public void actionPerformed(ActionEvent e) {
            doAction1();
        }
    });

    final JButton button2 = new JButton(new AbstractAction("Start activity indicator before opening the dialog") {
        @Override
        public void actionPerformed(ActionEvent e) {
            doAction2();
        }
    });

    panel = new JPanel();
    panel.add(button1);
    panel.add(button2);
    getContentPane().add(panel, BorderLayout.NORTH);

    glassPane = (JPanel) getGlassPane();
    glassPane.setLayout(new BorderLayout());
    glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    glassPane.add(new JLabel("Please Wait..."), BorderLayout.CENTER);

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(800, 600);
    setVisible(true);
}

public void doAction1() {
    System.out.println("IsStartingInEDT?: "+  SwingUtilities.isEventDispatchThread());
    final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");
    if (JOptionPane.YES_OPTION == response) {
        startActivity();
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        stopActivity();
    }
}

public void doAction2() {
    startActivity();
    System.out.println("IsStartingInEDT?: "+  SwingUtilities.isEventDispatchThread());
    final int response = JOptionPane.showConfirmDialog(this, "Move the mouse inside the dialog (me) and then outside, the busy indicator is not shown anymore");
    if (JOptionPane.YES_OPTION == response) {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    stopActivity();
}

public void startActivity() {
    System.out.println("TestFrame.startActivity()");
    glassPane.setVisible(true);
}

public void stopActivity() {
    System.out.println("TestFrame.stopActivity()");
    glassPane.setVisible(false);
}

/**
 * @param args
 */
public static void main(String[] args) {
    new TestFrame();
}

}

На данный момент я не сделалнайти любые связанные проблемы в параде JavaBug.Я буду искать дальше, прежде чем открывать новую.

Я также уже читал следующую статью, но она не очень удобна, поскольку создание хорошего модального диалога из немодального не является простым: http://www.javaspecialists.eu/archive/Issue065.html

Кто-нибудь может помочь?Заранее спасибо, Пьер

Ответы [ 2 ]

6 голосов
/ 19 августа 2011

У вас есть проблема с многопоточностью.

Является ли IsStartingInEDT верным?

Если да, вы делаете это неправильно, потому что:

  • Вы не должны спать в потоке пользовательского интерфейса.Это остановит обновление экрана.

Если нет, вы делаете это неправильно, потому что:

  • OptionPane.showConfirmDialog() должен вызываться из потока пользовательского интерфейса.1017 *

    вы должны сделать что-то вроде этого:

    public void doAction1() {
        if (!SwingUtilities.isEventDispatchThread())  {
             System.err.println("error, must be edt");
             return;
        }
    
        final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");
    
        if (JOptionPane.YES_OPTION == response) {
            startActivity();   // change glass panel in edt    
    
            // new thread for long standing task
            new Thread( new Runnable() { public void run() {    
               for (int i = 0; i < 5; i++) {
                   try {
                       Thread.sleep(200);
                   } catch (Exception e) {
                       e.printStackTrace();
                   }
               }
    
               SwingUtilities.invokeAndWait(new Runnable(){ public void run() {
                  // changing glass panel need edt
                  stopActivity();
               });
            }).start();
        }
    }
    
4 голосов
/ 19 августа 2011

первый.с помощью Tread.sleep(int) почти наверняка заблокируем EDT, со всеми описанными проблемами Параллельность в Swing

2.nd работает, потому что инициализация для JOptionPane создает новый EDT

здесьЭто простая демонстрация о ...., пожалуйста, это только пример и убедитесь, что это противоречит всем правилам Swing, но демострирует блокировку и разблокировку EDT с помощью Tread.sleep (int) во время EDT

enter image description here enter image description here

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class ShakeComponents1 {

    private JFrame frame = new JFrame();
    private final String items[] = {"One", "Two", "Three"};
    private Timer timer;
    private JPanel panel = new JPanel();
    private JPanel buttonPanel = new JPanel();
    private JButton button = new JButton("  Exit  ");
    private boolean repeats = true;
    private boolean runs = false;
    private Color clr[] = {Color.red, Color.blue, Color.magenta};
    private Insets initMargin;

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ShakeComponents1().makeUI();
            }
        });
    }

    public void makeUI() {
        buttonPanel = new JPanel();
        buttonPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
        buttonPanel.setLayout(new BorderLayout());
        button.setPreferredSize(new Dimension(100, 45));
        button.setForeground(Color.darkGray);
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent event) {
                Runnable doRun = new Runnable() {

                    @Override
                    public void run() {
                        System.exit(0);
                    }
                };
                SwingUtilities.invokeLater(doRun);
            }
        });
        button.addMouseListener(new java.awt.event.MouseListener() {

            @Override
            public void mouseClicked(MouseEvent e) {
            }

            @Override
            public void mousePressed(MouseEvent e) {
            }

            @Override
            public void mouseReleased(MouseEvent e) {
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                if (runs) {
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            runs = false;
                            timer.stop();
                            changePnlBorder(new EmptyBorder(5, 5, 5, 5));
                            changeBtnForegroung(Color.darkGray);
                        }
                    });
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {
                if (!runs) {
                    timer.start();
                    runs = true;
                }
            }
        });
        buttonPanel.add(button);
        final Insets margin = button.getMargin();
        panel.add(buttonPanel);
        for (int i = 0; i < 2; i++) {
            JComboBox combo = new JComboBox(items);
            combo.setMinimumSize(new Dimension(50, 25));
            combo.setMaximumSize(new Dimension(150, 25));
            combo.setPreferredSize(new Dimension(100, 25));
            combo.addActionListener(new ShakeAction());
            panel.add(combo);
        }
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setLocation(50, 50);
        frame.setVisible(true);
        timer = new Timer(500, new ShakeAction());
        timer.setRepeats(repeats);
        initMargin = button.getMargin();
    }

    private class ShakeAction extends AbstractAction {

        private static final long serialVersionUID = 1L;
        private int noColor = 0;
        private Border border;
        private int count = 0;

        @Override
        public void actionPerformed(ActionEvent e) {
            timer.start();
            if (count > 5) {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            Thread.sleep(500);
                            changeBtnForegroung(Color.darkGray);
                            Thread.sleep(500);
                            count = 0;
                            Thread.sleep(750);
                        } catch (Exception e) {
                            System.out.println(e);
                        }
                    }
                }).start();
            } else {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            runs = true;
                            if (noColor < 2) {
                                noColor++;
                                changeBtnForegroung(clr[noColor]);
                            } else {
                                noColor = 0;
                                changeBtnForegroung(clr[noColor]);
                            }
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left + 10, initMargin.bottom, initMargin.right - 10));
                            border = new EmptyBorder(0, 5, 10, 5);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left - 10, initMargin.bottom, initMargin.right + 10));
                            border = new EmptyBorder(0, 0, 10, 10);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left + 10, initMargin.bottom, initMargin.right - 10));
                            border = new EmptyBorder(5, 10, 5, 0);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left - 10, initMargin.bottom, initMargin.right + 10));
                            border = new EmptyBorder(10, 10, 0, 0);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            changeBtnMargin(new Insets(initMargin.top, initMargin.left, initMargin.bottom, initMargin.right));
                            border = new EmptyBorder(5, 5, 5, 5);
                            changePnlBorder(border);
                            Thread.sleep(100);
                            count++;
                        } catch (Exception e) {
                            System.out.println(e);
                        }
                    }
                }).start();
            }
        }
    }

    private void changePnlBorder(final Border b) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                buttonPanel.setBorder(b);
                buttonPanel.revalidate();
                buttonPanel.repaint();
            }
        });
    }

    private void changeBtnForegroung(final Color c) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.setForeground(c);
            }
        });
    }

    private void changeBtnMargin(final Insets margin) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.setMargin(margin);
            }
        });
    }
}

заключение -> вы можете создать новые Thread в качестве заданий BackGroung, заключенных в Runnable, если вы хотите имитировать задание LongRunning и с помощью Thread.sleep (int), возможно, ответьтена ваш вопрос здесь

наверняка правильным будет использование SwingWorker для этого, с Thread.sleep (int) тоже

...