Проблема отображения курсора ожидания Java

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

Я прилагаю SSCE, чтобы точно объяснить мою проблему.

public class BusyCursorTest extends javax.swing.JFrame {

public BusyCursorTest() {

    javax.swing.JMenuBar menuBar = new javax.swing.JMenuBar();
    javax.swing.JMenu menu = new javax.swing.JMenu("Menu");
    javax.swing.JMenuItem wait1 = new javax.swing.JMenuItem("Wait 100 ms");
    javax.swing.JMenuItem wait2 = new javax.swing.JMenuItem("Wait 250 ms");
    javax.swing.JMenuItem wait3 = new javax.swing.JMenuItem("Wait 500 ms");
    javax.swing.JMenuItem wait4 = new javax.swing.JMenuItem("Wait 1000 ms");
    menu.add(wait1);
    menu.add(wait2);
    menu.add(wait3);
    menu.add(wait4);
    menuBar.add(menu);
    setJMenuBar(menuBar);
    wait1.addActionListener(getActionListener(this, delayActionListener(100)));
    wait2.addActionListener(getActionListener(this, delayActionListener(250)));
    wait3.addActionListener(getActionListener(this, delayActionListener(500)));
    wait4.addActionListener(getActionListener(this, delayActionListener(1000)));

    cursorPanel = new javax.swing.JPanel();
    cursorPanel.addMouseListener(new java.awt.event.MouseAdapter() {

        public void mouseEntered(java.awt.event.MouseEvent e) {
            cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR));
        }

        public void mouseExited(java.awt.event.MouseEvent e) {
            cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        }

    });

    javax.swing.JTabbedPane tabbedPane = new javax.swing.JTabbedPane();
    tabbedPane.addTab("Default", new javax.swing.JPanel());
    tabbedPane.addTab("Cursor change", cursorPanel);
    getContentPane().add(tabbedPane);

    setTitle("Cursor test");
    setSize(400, 400);
    setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
    setVisible(true);
}

private java.awt.event.ActionListener delayActionListener(final int delay) {
    java.awt.event.ActionListener listener = new java.awt.event.ActionListener() {

        public void actionPerformed(java.awt.event.ActionEvent ae) {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
            }
        }

    };
    return listener;
}

public static void main(String[] args) {
    new BusyCursorTest();
}

public static java.awt.event.ActionListener getActionListener(final java.awt.Component component,
    final java.awt.event.ActionListener originalActionListener) {

    java.awt.event.ActionListener actionListener = new java.awt.event.ActionListener() {

        public void actionPerformed(final java.awt.event.ActionEvent e) {

            java.util.TimerTask timerTask = new java.util.TimerTask() {

                public void run() {
                    originalCursor = component.getCursor();
                    component.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
                }

            };
            java.util.Timer timer = new java.util.Timer();

            try {
                timer.schedule(timerTask, DELAY_MS);
                originalActionListener.actionPerformed(e);
            } finally {
                timer.cancel();
                component.setCursor(originalCursor);
            }
        }

    };
    return actionListener;
}

private javax.swing.JPanel cursorPanel = null;

public static java.awt.Cursor originalCursor = null;

public static final int DELAY_MS = 250;
}

Запустите прилагаемую SSCE.

Когда выбрана первая вкладка ("По умолчанию"), нажатие на пункт меню 1000 мс покажет занятый курсор.

Когда выбрана вторая вкладка ("Смена курсора"), нажатие на пункт меню 1000 мс не показывает занятый курсор.

Как мне решить эту проблему?

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

Какой обходной путь рекомендуется, чтобы я мог изменить поведение в контейнере верхнего уровня?

4 ответа

Решение

После поиска в интернете я нашел ответ на свой вопрос.

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

Подожди, Курсор, Подожди!

Автоматический курсор ожидания: WaitCursorEventQueue

Я изменил мой SSCE, чтобы он работал для случая, когда компоненты внутри фрейма устанавливают свой собственный курсор. Вот модифицированная СБСЕ.

public class BusyCursorTest extends javax.swing.JFrame {

private javax.swing.JPanel cursorPanel = null;

public BusyCursorTest() {

    javax.swing.JMenuBar menuBar = new javax.swing.JMenuBar();
    javax.swing.JMenu menu = new javax.swing.JMenu("Menu");
    javax.swing.JMenuItem wait1 = new javax.swing.JMenuItem("Wait 100 ms");
    javax.swing.JMenuItem wait2 = new javax.swing.JMenuItem("Wait 250 ms");
    javax.swing.JMenuItem wait3 = new javax.swing.JMenuItem("Wait 500 ms");
    javax.swing.JMenuItem wait4 = new javax.swing.JMenuItem("Wait 1000 ms");
    menu.add(wait1);
    menu.add(wait2);
    menu.add(wait3);
    menu.add(wait4);
    menuBar.add(menu);
    setJMenuBar(menuBar);
    wait1.addActionListener(getActionListener(this, delayActionListener(100)));
    wait2.addActionListener(getActionListener(this, delayActionListener(250)));
    wait3.addActionListener(getActionListener(this, delayActionListener(500)));
    wait4.addActionListener(getActionListener(this, delayActionListener(1000)));

    cursorPanel = new javax.swing.JPanel();
    cursorPanel.addMouseListener(new java.awt.event.MouseAdapter() {

        public void mouseEntered(java.awt.event.MouseEvent e) {
            cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR));
        }

        public void mouseExited(java.awt.event.MouseEvent e) {
            cursorPanel.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
        }

    });

    javax.swing.JTabbedPane tabbedPane = new javax.swing.JTabbedPane();
    tabbedPane.addTab("Default", new javax.swing.JPanel());
    tabbedPane.addTab("Cursor change", cursorPanel);
    getContentPane().add(tabbedPane);

    setTitle("Cursor test");
    setSize(400, 400);
    setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
    setVisible(true);
}

private java.awt.event.ActionListener delayActionListener(final int delay) {
    java.awt.event.ActionListener listener = new java.awt.event.ActionListener() {

        public void actionPerformed(java.awt.event.ActionEvent ae) {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
            }
        }

    };
    return listener;
}

public static void main(String[] args) {
    new BusyCursorTest();
}

public static java.awt.event.ActionListener getActionListener(final javax.swing.JFrame frame,
    final java.awt.event.ActionListener originalActionListener) {

    java.awt.event.ActionListener actionListener = new java.awt.event.ActionListener() {

        public void actionPerformed(final java.awt.event.ActionEvent e) {

            java.util.TimerTask timerTask = new java.util.TimerTask() {

                public void run() {
                    originalCursor = frame.getCursor();
                    startWaitCursor(frame);
                }

            };
            java.util.Timer timer = new java.util.Timer();

            try {
                timer.schedule(timerTask, DELAY_MS);
                originalActionListener.actionPerformed(e);
            } finally {
                timer.cancel();
                stopWaitCursor(frame);
            }
        }

    };
    return actionListener;
}

private static void startWaitCursor(javax.swing.JFrame frame) {
    frame.getGlassPane().setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
    frame.getGlassPane().addMouseListener(mouseAdapter);
    frame.getGlassPane().setVisible(true);
}

private static void stopWaitCursor(javax.swing.JFrame frame) {
    frame.getGlassPane().setCursor(originalCursor);
    frame.getGlassPane().removeMouseListener(mouseAdapter);
    frame.getGlassPane().setVisible(false);
}

private static java.awt.Cursor originalCursor = null;

private static final java.awt.event.MouseAdapter mouseAdapter = new java.awt.event.MouseAdapter() {
};

public static final int DELAY_MS = 250;

}

import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.Random;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.table.*;

public class TableWithTimer implements ActionListener, Runnable {

    private static final long serialVersionUID = 1L;
    private JFrame frame = new JFrame();
    private JScrollPane scroll = new JScrollPane();
    private JTable myTable;
    private JPanel buttonPanel = new JPanel();
    private JButton startButton = new JButton("Start Thread to Update Table");
    private JButton stopButton = new JButton("Stop Thread for Update Table");
    private JButton newButton = new JButton("Load new Data to Table");
    private int count = 0;
    private int delay = 3;
    private javax.swing.Timer timer = null;
    private boolean runProcess;
    private int row = 0;
    private int column = 0;
    private String value = "Amnd";
    private int amndValue = 0;
    private String valueAt = "";
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private String[] head = {"One", "Two", "Three", "Four", "Five", "Six"};
    private String[][] data = new String[25][6];

    public TableWithTimer() {
        myTable = new TableBackroundPaint0(data, head);
        myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        myTable.setGridColor(Color.gray);
        myTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        final TableCellRenderer cellRendener = myTable.getTableHeader().getDefaultRenderer();
        myTable.getTableHeader().setDefaultRenderer(new TableCellRenderer() {

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value,
                    boolean isSelected, boolean hasFocus, int row, int column) {
                JLabel label = (JLabel) cellRendener.getTableCellRendererComponent(
                        table, value, isSelected, hasFocus, row, column);
                label.setBackground(Color.orange);
                label.setForeground(Color.darkGray);
                label.setFont(new Font("SansSerif", Font.BOLD, 12));
                label.setBorder(BorderFactory.createCompoundBorder(label.getBorder(),
                        BorderFactory.createEmptyBorder(0, 5, 0, 0)));
                label.setHorizontalAlignment(SwingConstants.LEFT);
                label.setHorizontalAlignment(SwingConstants.CENTER);
                if ((label.getText().equals("First")) || (label.getText().equals("Second"))) {
                    label.setForeground(Color.red);
                }
                if ((label.getText().equals("Day")) || (label.getText().equals("Month")) || (label.getText().equals("Year"))) {
                    label.setForeground(Color.blue);
                }
                if ((label.getText().equals("Time"))) {
                    label.setForeground(Color.green);
                }
                return label;
            }
        });
        TableColumnModel cm = myTable.getColumnModel();
        for (int column1 = 0; column1 < cm.getColumnCount(); column1++) {
            TableColumn colLeft1 = cm.getColumn(column1);
            cm.getColumn(column1).setWidth(140);
            cm.getColumn(column1).setPreferredWidth(140);
        }
        //myTable.setFillsViewportHeight(true); // apply paintComponent for whole Viewport
        JButton cornerButtonTop = new JButton();
        cornerButtonTop.setBackground(scroll.getViewport().getBackground());
        JButton cornerButtonBottom = new JButton();
        cornerButtonBottom.setOpaque(false);
        scroll.setCorner(JScrollPane.UPPER_RIGHT_CORNER, cornerButtonTop);
        scroll.setCorner(JScrollPane.LOWER_RIGHT_CORNER, cornerButtonBottom);
        scroll.setViewportView(myTable);
        scroll.setMinimumSize(new Dimension(600, 400));
        scroll.setMaximumSize(new Dimension(900, 600));
        scroll.setPreferredSize(new Dimension(850, 430));
        frame.add(scroll, BorderLayout.CENTER);
        buttonPanel.setLayout(new GridLayout(1, 4, 10, 10));
        startButton.addActionListener(this);
        startButton.setEnabled(false);
        stopButton.addActionListener(this);
        stopButton.setEnabled(false);
        JButton hideButton = new JButton();
        newButton.addActionListener(this);
        newButton.setEnabled(false);
        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);
        buttonPanel.add(hideButton);
        buttonPanel.add(newButton);
        hideButton.setVisible(false);
        frame.add(buttonPanel, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocation(100, 100);
        frame.pack();
        frame.setVisible(true);
        start();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == startButton) {
            runProcess = true;
            new Thread(this).start();
            myTable.requestFocus();
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
        } else if (e.getSource() == stopButton) {
            scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            runProcess = false;
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
            newButton.setEnabled(true);
        } else if (e.getSource() == newButton) {
            runProcess = false;
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
            newButton.setEnabled(false);
            addNewData();
        }
    }

    public void addNewData() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                TableModel model = myTable.getModel();
                for (int j = 0; j < model.getRowCount(); j++) {
                    int column = model.getColumnCount();
                    for (int i = 0; i < column; i++) {
                        model.setValueAt("Deleted", j, i);
                    }
                }
                startNewData();
            }
        });
    }

    private void start() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        timer = new javax.swing.Timer(delay * 100, updateCol());
        timer.start();
    }

    private void startNewData() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        count = 0;
        timer = new javax.swing.Timer(1500, updateCol());
        timer.start();
    }

    @Override
    public void run() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        count = 0;
        Random random = new Random();
        while (runProcess) {
            row = random.nextInt(myTable.getRowCount());
            column = random.nextInt(myTable.getColumnCount());
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    try {
                        amndValue++;
                        valueAt = ((myTable.getValueAt(row, column)).toString());
                        if (!(valueAt.startsWith("A"))) {
                            count++;
                            if (count == ((25 * 6))) {
                                JOptionPane.showMessageDialog(myTable, " Update done ");
                                scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                                runProcess = false;
                            }
                            java.util.Date date = new java.util.Date();
                            String dateTime = sdf.format(date.getTime());
                            myTable.setValueAt((value + " " + String.valueOf(amndValue) + " at: " + dateTime), row, column);
                            //myTable.setValueAt(new Integer(1), row, column); // please uncoment for generate misstype error on EDT
                            myTable.changeSelection(row, column, false, false);
                            System.out.println("update cycle with value :"
                                    + (value + " " + String.valueOf(amndValue) + " at: " + dateTime) + ", table row :" + row
                                    + ", table column " + column);
                        }
                    } catch (Exception e) {
                        runProcess = false;
                        System.out.println("Error for update JTable cell");
                        e.printStackTrace();
                    }
                }
            });
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public Action updateCol() {
        return new AbstractAction("text load action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {

                System.out.println("updating row " + (count + 1));
                TableModel model = myTable.getModel();
                int cols = model.getColumnCount();
                int row = 0;
                for (int j = 0; j < cols; j++) {
                    row = count;
                    myTable.changeSelection(row, 0, false, false);
                    timer.setDelay(200);
                    Object value = "row " + (count + 1) + " item " + (j + 1);
                    model.setValueAt(value, count, j);
                }
                count++;
                if (count >= myTable.getRowCount()) {
                    myTable.changeSelection(0, 0, false, false);
                    timer.stop();
                    System.out.println("update cycle completed");
                    myTable.clearSelection();
                    startButton.setEnabled(true);
                    newButton.setEnabled(true);
                    scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                }
            }
        };
    }

    public static void main(String args[]) {
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                System.out.println(info.getName());
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (UnsupportedLookAndFeelException e) {
            // handle exception
        } catch (ClassNotFoundException e) {
            // handle exception
        } catch (InstantiationException e) {
            // handle exception
        } catch (IllegalAccessException e) {
            // handle exception
        }
        TableWithTimer tableWithTimer = new TableWithTimer();
    }
}

class TableBackroundPaint0 extends JTable {

    private static final long serialVersionUID = 1L;

    TableBackroundPaint0(Object[][] data, Object[] head) {
        super(data, head);
        setOpaque(false);
        ((JComponent) getDefaultRenderer(Object.class)).setOpaque(false);
    }

    @Override
    public void paintComponent(Graphics g) {
        Color background = new Color(168, 210, 241);
        Color controlColor = new Color(230, 240, 230);
        int width = getWidth();
        int height = getHeight();
        Graphics2D g2 = (Graphics2D) g;
        Paint oldPaint = g2.getPaint();
        g2.setPaint(new GradientPaint(0, 0, background, width, 0, controlColor));
        g2.fillRect(0, 0, width, height);
        g2.setPaint(oldPaint);
        for (int row : getSelectedRows()) {
            Rectangle start = getCellRect(row, 0, true);
            Rectangle end = getCellRect(row, getColumnCount() - 1, true);
            g2.setPaint(new GradientPaint(start.x, 0, controlColor, (int) ((end.x + end.width - start.x) * 1.25), 0, Color.orange));
            g2.fillRect(start.x, start.y, end.x + end.width - start.x, start.height);
        }
        super.paintComponent(g);
    }
}

Хммм у вас код есть проблема с Concurency в Swing есть две области

заменить Thread.sleep(задержка); и java.util.Timer с java.swing.Timer, потому что блокируют EDT

но в других руках есть способ, как преднамеренно заблокировать EDT (с помощью splungebob из OTN). ПРИМЕЧАНИЕ: этот пример против всех правил Swing, и работает только в этой форме и как пример

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

public class DelayedComboBoxDemo implements Runnable {

    private JCheckBox chkA = new JCheckBox("A");
    private JCheckBox chkB = new JCheckBox("B");
    private JCheckBox chkC = new JCheckBox("C");
    private JComboBox cboItems = new JComboBox();
    private JFrame frame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new DelayedComboBoxDemo());
    }

    @Override
    public void run() {
        cboItems.addItem("-");
        JPanel p = new JPanel();
        p.add(chkA);
        p.add(chkB);
        p.add(chkC);
        p.add(cboItems);
        frame = new JFrame("Delayed ComboBox Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(p);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        cboItems.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            }

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                int items = cboItems.getItemCount();
                rebuildList();
                if (items != cboItems.getItemCount()) {
                    cboItems.hidePopup();
                    cboItems.showPopup();
                }
            }
        });
    }

    private void rebuildList() {
        frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        Vector<String> items = new Vector<String>();
        if (chkA.isSelected()) {
            items.add("A");
        } else if (chkB.isSelected()) {
            items.add("B");
        } else if (chkC.isSelected()) {
            items.add("C");
        } else {
            items.add("-");
        }
        cboItems.setModel(new DefaultComboBoxModel(items));
        try {
            Thread.sleep(3000); // simulate a long transaction
        } catch (InterruptedException ex) {
        }
        frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }
}

Попробуйте установить его на раме вместо данного компонента

Другие вопросы по тегам