Swing:将光标更改为等待光标

7
请参见Java Swing GUI hour glass。但是提供的答案似乎不起作用。
我有如下代码:
private void loadFileMenuItemActionPerformed(java.awt.event.ActionEvent evt) {                                                

    int returnVal = fileChoser.showOpenDialog(this);
    if (returnVal == JFileChooser.APPROVE_OPTION) {

        try {
            this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            // do stuff
        } finally {
            this.setCursor(Cursor.getDefaultCursor());
        }
    } 
}

当用户从菜单栏中选择相应的条目时,会调用此功能。但光标从未改变。请注意,加载文件需要一个文件,因此光标的更改应该是可见的。
我做错了什么?
编辑:此处的this是顶层JFrame。
编辑2:将解决方案移至单独的回答

为了更快地获得帮助,请发布一个SSCCE。请注意,该文档正在此问题上进行审查和讨论,欢迎贡献。 - Andrew Thompson
//做事情是什么意思?在那里实现的代码应该在一个单独的线程中执行。否则,您的应用程序将看不到对光标所做的任何更改。 - oopbase
3
当涉及到EDT(事件分派线程)时,请勿阻塞它,否则GUI将会“冻结”。如果需要重复执行任务,请使用Swing Timer,如果需要长时间运行的任务,请使用 SwingWorker。请参考Concurrency in Swing获取更多细节信息。注意不要调用Thread.sleep(n) - Andrew Thompson
1
@beginner_ 编辑看起来很不错,除了 worker.get() 可能会破坏工作线程的目的... 不要阻塞和等待,而是在工作者本身处理异常,如果出现问题,则进行报告。还要考虑将 this 传递给 MyWorker 构造函数,并使您的内部类 static,而不是 Outer.this.set... 的混乱形式。 :-) - Harald K
哦,你可能想把这个放在答案里,而不是问题中。 :-) - Harald K
5个回答

7

首先创建一个 SwingWorker 类:

private class FileLoader extends SwingWorker<String, Void> {

    private final JFrame frame;
    private final File file;        

    public SdfLoader(JFrame frame, File file) {            
        frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        this.frame = frame;
        this.file = file;
    }

    @Override
    public String doInBackground() throws IOException {

            String result = null; 
            // read file and set result;
        return result;
    }

    @Override
    public void done() {
        try {
            String result = get();
            //do stuff 
        } catch (ExecutionException | InterruptedException ex) {
            // display error
            JOptionPane.showMessageDialog(SdfViewer.this,
                    ioException.getMessage(),
                    "Error opening file",
                    JOptionPane.ERROR_MESSAGE);
        } finally {
            frame.setCursor(Cursor.getDefaultCursor());
        }
    }

}

然后就可以这样调用它:
private void loadFileMenuItemActionPerformed(java.awt.event.ActionEvent evt) { 
    int returnVal = fileChoser.showOpenDialog(this);
    if (returnVal == JFileChooser.APPROVE_OPTION) {
        File file = fileChoser.getSelectedFile();
        logger.debug("Opening SD-File '{}'.", file.getAbsoluteFile());
        FileLoader loader = new FileLoader(this, file);
        loader.execute();
    } 
}  

由@mKorbel编辑,对于我所造成的hi_jack请道歉

  • 我无法通过这个代码找到我的帖子(底层数据库发生了一些事情),

  • 使用这个逻辑,

  • 其余部分在我的评论中

虚拟-1k错误,错误,错误,您的doInBackground()丢失 发布()-进程,done()极其错误 设计,请阅读@trashgod的答案中的评论,如果可以使用 Runnable#Thread代替基于Future和SwingWorker的黑洞进行FileIO、Socket或任何XxxStreams

  • 请按编辑要求删除您的线程中的此代码

.

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_INTERVAL_SELECTION);
        myTable.setRowSelectionAllowed(false);
        myTable.setColumnSelectionAllowed(true);
        //myTable.setCellSelectionEnabled(true);

        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);
    }
}

虚拟-1k错误,错误,错误,你的doInBackground()缺少publish()-process(),done()设计极其错误,请阅读@trashgod在答案中的评论。如果可能的话,可以使用Runnable#Thread来处理FileIO、Socket或任何XxxStreams,而不是基于Future和SwingWorker的黑洞。 - mKorbel
我不需要发布和处理方法,现在已经固定了。Runnable存在问题,因为您无法向用户报告异常...它只会失败,并且异常将不会被注意到:我认为这相当愚蠢,特别是在FileIO方面。 - beginner_
这不是真的,Runnable 没有任何问题,也没有在您的评论中描述。这个描述谈论的是为什么不使用 SwingWorker(具有漫长和大量的错误历史,并且经常重复),也不适用于生产代码,当然一切都取决于您,对于 FileIO、XxxStream、Socket、JDBC,Runnable 是最好的选择。 - mKorbel

5
你看不到变化的原因很可能是你正在EDT上做所有的工作。绝对不能这么做。即使你在一个单独的线程上做了一些事情// do stufffinally块也会在UI有机会重新绘制之前执行。
相反,你应该生成一个SwingWorker来设置等待光标,然后在后台执行繁重的工作(文件加载),最后在完成时重置为正常光标。
这可能表明一开始并不真正需要等待光标,在这种情况下使用进度条更合适。

3

哇,这些代码是什么意思。其实很简单:

final JScrollPane jsp = new JScrollPane(jt);

jsp.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

                //DO SOMETHING
jsp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

如果DO SOMETHING块发生在EDT上,那么光标可能没有机会重新绘制,直到控制权返回到EDT(即在将光标重置回默认值之后)。 - Rangi Keen

3
  • EventDispatchThread 的常见问题是所有的事件在同一时刻完成,然后所有的代码、方法和类都在同一时刻重绘,在 ActionListener 中执行所有代码后/当时。

  • 然后您需要将这些逻辑拆分为两个独立的事件,包装到(最好的选项)两个 Swing Actions 中:

    1. 一个用于使用光标进行切换 - 只能从 PropertyChangeListener 或 JMenuItem 的 ButtonModel 中调用光标管理器

    2. 另一个 Swing Action 或任何 SwingListener 来调用其余或预期的代码

    3. 您可以将这两个 Swing Actions 连接在一起(我的上午),第一个调用第二个


@Andrew Thompson 不必了,我已经关注了这个线程 :-) - mKorbel
非常酷。希望我们很快就能看到一个新的改进版本。 :) - Andrew Thompson
1
@Andrew Thompson 对不起我之前留下的那些空洞的评论,对于我来说很重要的是这个选项作为标准存在于这里,其他的什么都不重要,我会忽略所有的喋喋不休,无论是赞成还是反对…… - mKorbel
1
完全没有问题。 :) 我们每个人都尽力而为。 ;) - Andrew Thompson
顺便说一下,偶尔我会抽出一点时间去“走出舒适区”,但我更喜欢来到一个问题,看到像你可能发布的答案,点赞并继续前进。 :) - Andrew Thompson
1
@Andrew Thompson请勿阅读以下单词,不感谢您的努力,这是Java和C...,这是Swing,这是SSCCE,更好... :-) - mKorbel

2

加载文件时,EDT已经很忙了,因此它没有机会改变光标。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接