事件修补线程和工作线程

时间:2017-01-19 08:01:50

标签: 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) {
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

事件调度线程

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

我假设mouseClicked是来自MouseListener接口的实现。 MouseEvent传递EventQueue并在EDT内处理,这意味着在EDT的上下文中调用mouseClicked

在该方法存在之前,EDT无法处理任何其他事件。这意味着for-loopThread.sleep的组合阻止EDT处理事件最多一秒。

重要提示,repaint不会立即发生,它会通过EventQueue将彩票事件发布到RepaintManager

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的单线程规则,也就是说,应该在EDT的上下文中对UI进行任何更新。这是为了确保在油漆循环之间不会发生任何更新,从而可能导致脏涂料或不必要的油漆伪影。

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

答案......

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

  1. 你需要知道你从哪里开始
  2. 你需要知道你要去哪里
  3. 你需要知道你是如何到达那里的
  4. 应在EDT
  5. 内对组件/ UI进行更新
  6. 等待应该在EDT之外完成
  7. 虽然你可以使用Thread来实现这一点,但它变得相当困难并且容易出错,并且需要在线程之间进行更多协调

    您可以使用SwingWorker来完成一些猜测工作,但大多数情况下,简单的Swing Timer做得很好

    This example原则上证明了你想要实现的目标,它稍微复杂一些,因为无论线路长度如何,它都会尝试保持速度,但是& #39;是一个例子