java - 事件修补线程和工作线程

标签 java swing

我想实现鼠标点击时的“平滑移动”。下面是我的代码:

public void mouseClicked(MouseEvent event) {
    int targetX = event.getX();
    int targetY = event.getY();
    for(int i=0;i<10;++i) {
        x = (x+targetX)/2;
        y = (y+targetY)/2;
        repaint();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) { }
    }
}

但效果是从一个地方“直接”移动到单击的地方。我知道线程可以实现这一点,原则是事件调度线程不应该执行太多操作,但我可以知道为什么上面的代码不起作用吗?谁能给我一些详细的解释吗?

class Animation extends Thread {
    /* … */
    public void run() {
        int targetX = event.getX();
        int targetY = event.getY();

        for(int i=0;i<10;++i) {
            panel.x = (panel.x+targetX)/2;
            panel.y = (panel.y+targetY)/2;
            panel.repaint();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
    }
}

最佳答案

事件调度线程

EDT 执行许多重要的工作,除了处理来自操作系统的系统事件外,它还处理许多内部事件,例如绘画。

目的是提供一个单一的、统一的机制来处理单个线程上下文中的事件,减少竞争条件和死锁的风险以及不良副作用,例如脏/部分绘制(当对象被绘制时,内部状态发生变化,这会影响剩余要绘制的内容)

第一个示例

public void mouseClicked(MouseEvent event) {
    int targetX = event.getX();
    int targetY = event.getY();
    for(int i=0;i<10;++i) {
        x = (x+targetX)/2;
        y = (y+targetY)/2;
        repaint();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) { }
    }
}

我假设 mouseClickedMouseListener 接口(interface)的实现。 MouseEvent 通过 EventQueue 并在 EDT 内进行处理,这意味着 mouseClicked 在 EDT 的上下文中调用。

在该方法存在之前,EDT 无法处理任何进一步的事件。这意味着 for-loopThread.sleep 的组合会阻止 EDT 处理事件长达一秒。

需要注意的是,repaint 不会立即发生,它会通过 RepaintManager 将绘制事件发布到 EventQueue 中。

RepaintManager 的副作用之一是它可以将重复的重绘请求合并为几个实际的绘制事件。

因此,由于 for-loop/Thread.sleep,您要么在很短的时间内获得所有绘制事件(并且您只需看不到它们更新)或者更有可能的是,您正在获得最后一个。

第二个示例

class Animation extends Thread {
    /* … */
    public void run() {
        int targetX = event.getX();
        int targetY = event.getY();

        for(int i=0;i<10;++i) {
            panel.x = (panel.x+targetX)/2;
            panel.y = (panel.y+targetY)/2;
            panel.repaint();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
    }
}

第二个示例虽然表面上看起来不错,但违反了 Swing 的单线程规则,即对 UI 的任何更新都应该在 EDT 的上下文中进行。这是为了确保在绘制周期之间不会发生任何更新,从而可能导致脏油漆或不需要的油漆伪影。

作为一般规则,repaint 是我认为线程安全的唯一方法(还有更多,但我保持简单)。

答案...

很复杂,但动画也很复杂。基本要求是...

  1. 您需要知道从哪里开始
  2. 你需要知道你要去哪里
  3. 您需要知道如何实现这一目标
  4. 应在 EDT 内对组件/UI 进行更新
  5. 应在美国东部时间 (EDT) 之外等待

虽然您可以使用Thread来实现这一点,但它会变得更加困难并且稍微容易出错,并且需要线程之间进行更多协调

您可以使用 SwingWorker 来完成一些猜测,但大多数时候,一个简单的 Swing Timer 就可以完成不错的工作

This example原则上演示了您想要实现的目标,它稍微复杂一些,因为无论线路长度如何,它都会尝试保持恒定的速度,但这只是一个示例

关于java - 事件修补线程和工作线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41736646/

相关文章:

java - JRadioButton 项目监听器未触发

java - 什么是 java.lang.Class<?>[]

java - String.replaceAll() 函数出错

java - 我可以捕获退出 JTextField 的事件吗

java - Netbean 中的 Jframe 形式

java - 带有形状 JPanel 的 Force HeavyWeight Tooltip

java - 如何使用java读取多个JSON文件并将其写入单个JSON文件

java - 高性能且线程安全的初始化 block

java - 使用 JAI 将 swing 组件写入大型 TIFF 图像

java - MigLayout 只增长最后一行