触发setCursor方法后,光标图标未更改。

5
我的应用程序中有一个可调整大小的表头列JTable。通常情况下,当我将光标移动到表头进行调整大小时,光标图标会改变为调整大小箭头,类似于<-->。
但在以下情况下,情况有所不同。在同一Frame中有一个按钮操作,在执行操作期间,我正在使用Container.setCursor(Cursor cursor)方法将光标设置为繁忙图标,并在操作完成后将其更改回默认光标。
有时,如果我在进行按钮操作后移动光标以调整大小表头,则光标图标不会更改为调整大小箭头,光标根本不会更改。
这是否可以视为Java Swing中的错误,还是有解决此问题的方法?
更新:附带示例代码。
import java.util.*;  
import java.awt.*;  
import javax.swing.*;  
import java.awt.event.*;

public class ColumnResizeIconTest extends JFrame {

JScrollPane scrollPane;
JTable table;
JButton button;

public ColumnResizeIconTest() {
    setLayout(new BorderLayout());
    addComponents();
    setSize(300,300);
}

private void addComponents() {
    addButton();
    addTable();
}

private void addButton() {
    button = new JButton("Click Me");
    button.addActionListener( new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
            setWaitCursor();
            for(int i=0; i<2000; i++) {
                System.out.print(i);
            }
            setDefaultCursor();
        }
    });
    add(button, BorderLayout.NORTH);
}

private void addTable() {
    scrollPane = new JScrollPane(createTable());
    add(scrollPane, BorderLayout.CENTER);
}

private JTable createTable() {
    Object[][] cellData = { { "1-1", "1-2","1-3" }, { "2-1", "2-2", "2-3" }, { "3-1", "3-2", "3-3" } };
    String[] columnNames = { "column1", "column2", "column3" };
    table = new JTable(cellData, columnNames);
    return table;
}

private void setWaitCursor() {
    Container container = getContentPane();
    setWaitCursor(container);
}

private void setWaitCursor(Container container) {
    for(int iCount = 0; iCount < container.getComponentCount(); iCount++) {
        Component child = (Component) container.getComponent(iCount);
        if(child instanceof Container) {
            setWaitCursor((Container) child);
        } else {
            child.setCursor(new Cursor(Cursor.WAIT_CURSOR));
        }
    }
    container.setCursor(new Cursor(Cursor.WAIT_CURSOR));
}

private void setDefaultCursor() {
    Container container = getContentPane();
    setDefaultCursor(container);
}

private void setDefaultCursor(Container container) {
    for(int iCount = 0; iCount < container.getComponentCount(); iCount++) {
        Component child = (Component) container.getComponent(iCount);
        if(child instanceof Container) {
            setDefaultCursor((Container) child);
        } else {
            child.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        }
    }
    container.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}

public static void main(String[] argv) throws Exception {
    ColumnResizeIconTest test = new ColumnResizeIconTest();
    test.setVisible(true);
}
}

点击按钮几次并尝试调整表格列的大小。光标会被固定为默认光标。


2
  1. 为了更快地获得更好的帮助,请发布一个SSCCE
  2. “这可以被认为是一个错误吗..” 可能。 “..在Java Swing中..” 可能不是。 “..还是有解决这个问题的方法吗?” 修复你的代码(可能)。
- Andrew Thompson
1
@Andrew Thompson 今天可能需要多少时间?同意从你的3个中选择4个。 - mKorbel
@AnupD,他们正在谈论从JButton调用的操作以异常结束/操作仍在运行,因为您正在启动长时间和困难的事件。 - mKorbel
2
没有错误 - 很可能是在重新设置光标时做错了什么(涉及到三个光标,检测正确的光标进行重新设置并不完全简单)。 - kleopatra
@AndrewThompson 请原谅我的错误,因为我是这个论坛的新手。 我已经附上了示例代码。 请尝试并查看自己。 我有屏幕截图,但我不知道如何在此处附加它们。 - AnupD
显示剩余6条评论
2个回答

8

如我在评论中已经提到的:重新/设置光标并不是完全简单的,即使对于单个组件也是如此 :-) 递归设置光标以等待的基本问题是假设所有组件都有默认光标。

正如您在表头上看到的那样,这种假设是不正确的:在该组件上,“默认”可以是defaultCursor或resizeCursor,具体取决于鼠标位置。此外,内部光标切换并不是非常智能:它不检查状态(从我的记忆中,我之前遇到过这个事实)。

不太确定您想要达到什么目的,因此没有具体的解决方案,除了完全放弃递归设置,这太难了。选项可能包括:

  • 使框架的根面板(glassPane)可见,并在其中设置等待光标
  • 在较小的区域上使用 JLayer (jdk7) 或 JXLayer (jdk6),并在其中设置等待光标
  • 在某个地方使用较不显眼的可视化方式,例如 JProgressBar 或 JXBusyLabel(在SwingX项目中

附加说明(对于 @mKorbel :-)

问题很容易重现,只需对 OPs SSCCE 进行一些更改(感谢!):将 addButton 方法更改如下,然后单击按钮,在显示等待光标时将鼠标移动到标题栏,然后移动到另一列(跨越列边界)。这样做几次会导致标题栏上的光标变得不可预测...

private void addButton() {
    button = new JButton("Click Me");
    final ActionListener off = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            setDefaultCursor();
            button.setEnabled(true);
        }

    };
    button.addActionListener( new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
            setWaitCursor();
            button.setEnabled(false);
            Timer timer = new Timer(2000, off);
            timer.setRepeats(false);
            timer.start();
        }
    });

    add(button, BorderLayout.NORTH);
}

直到……,我才确定13号星期五是明天。如果我没记错的话,这是与EDT相关的问题。我读到了一个类似的问题(不知道在哪里),关于这个问题,这个过程必须分成两个独立的行动,否则不能保证来自本地操作系统的对象事件顺序,也许与焦点相同。 - mKorbel
@mKorbel,通常情况下EDT违规是自然罪犯,但这一次不是,仅仅只是不完整的逻辑 :-) - kleopatra
@kleopatra 您是正确的。_"基本问题在于假设所有组件都具有默认光标"。_ 我可以通过在设置光标为WAIT并将其返回DEFAULT时排除JTableHeader来修复我的SSCCE中的问题。我需要将其应用到我的应用程序中并在那里进行检查。感谢您的回答。我现在没有投票的特权。所以在这里+1。 - AnupD
@mKorbel 感谢您的有效帮助。 - AnupD

1

1) 你有重定向代码

for(int i=0; i<2000; i++) {
    System.out.print(i);
}

关于后台任务,您可以使用 javax.swing.TimerSwingWorker 或将这些代码行包装在 Runnable#Thread 中,例如 此处

2) 在 Error/Exception 上也必须恢复 Cursor,这可能是光标未更改的原因。


我担心你没有正确理解我的问题。在我点击按钮之前,如果我将光标放在表列标题之间的间隙上,光标会变成<->,表示我可以调整大小。当我点击按钮时,在执行操作之前,它会将光标更改为小时玻璃,并开始执行操作。当操作完成后,光标将更改为默认光标并离开操作。之后,如果我将光标放在表头列间隙上,图标将是默认图标,而不是<->。如果我点击几次按钮,就会发生这种情况。但我仍然能够调整列的大小。 - AnupD
无法重现您的问题,即使我多次按JButton,我将光标重定向到JFrame、JButton、JTable,并且只有在生成某些异常并且在finally块中未更改光标时,才会出现此问题,然后光标.WAIT_CURSOR,嗯,将永远停留。 - mKorbel
嗯...我已经理解了异常部分,但这不是这里的情况。我希望我能与您分享问题的视频或截图。 - AnupD
怀疑者 :-) 很容易复制,看看我的补充说明。 - kleopatra

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