我正在使用 cardLayout 更改“ View ”(此类有一个 JFrame 变量)。当用户点击新游戏按钮时,会发生这种情况:
public class Views extends JFrame implements ActionListener {
private JFrame frame;
private CardLayout cl;
private JPanel cards;
private Game game;
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("New game")) {
cl.show(cards, "Game");
game.init();
this.revalidate();
this.repaint();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
game.loop();
}
});
}
}
}
游戏的循环方式和类标题:
public class Game extends JPanel implements KeyListener {
public void loop() {
while (player.isAlive()) {
try {
this.update();
this.repaint();
// first class JFrame variable
jframee.getFrame().repaint();
// first class JFrame variable
jframee.getFrame().revalidate();
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void update() {
System.out.println("updated");
}
}
我正在使用 paintComponent() 绘画
public void paintComponent(Graphics g) {
System.out.println("paint");
...
}
其实它并没有画任何东西。当我不调用 loop() 方法(所以它只绘制一次)时,所有图像都被正确绘制。但是当我调用 loop() 方法时,窗口中什么也没有发生。 (即使 JFrame 上的关闭按钮也不起作用。)
如何解决? (当我在游戏类中创建 JFrame 时,一切正常,但现在我想要更多 View ,所以我需要在其他类中使用 JFrame。)
谢谢。
最佳答案
前体:The Event Dispatch Thread (EDT) .
Swing 是单线程的。这是什么意思?
Swing 程序中的所有处理都以事件开始。 EDT 是一个线程,它按照以下几行(但更复杂)循环处理这些事件:
class EventDispatchThread extends Thread {
Queue<AWTEvent> queue = ...;
void postEvent(AWTEvent anEvent) {
queue.add(anEvent);
}
@Override
public void run() {
while (true) {
AWTEvent nextEvent = queue.poll();
if (nextEvent != null) {
processEvent(nextEvent);
}
}
}
void processEvent(AWTEvent theEvent) {
// calls e.g.
// ActionListener.actionPerformed,
// JComponent.paintComponent,
// Runnable.run,
// etc...
}
}
调度线程通过抽象对我们隐藏:我们通常只编写监听器回调。
actionPerformed。repaint 会发布一个事件:处理该事件时,将在 EDT 上调用 paintComponent。invokeLater 发布一个事件:处理事件时,run is called on the EDT .事件任务按发布顺序依次处理。
只有当当前事件任务返回时,才能处理下一个事件。这就是为什么我们不能在 EDT 上无限循环。 actionPerformed(或 run,在您的编辑中)永远不会返回,因此调用 repaint post 绘制事件但是它们从未被处理过,程序似乎卡住了。
这就是“阻止”EDT 的意思。
基本上有两种方法可以在 Swing 程序中制作动画:
使用 Thread (或 SwingWorker )。
使用线程的好处是处理是关闭 EDT 完成的,因此如果有密集处理,GUI 仍然可以并发更新。
使用 javax.swing.Timer .
使用计时器的好处是处理是在 EDT 上完成的,因此无需担心同步,并且可以安全地更改 GUI 组件的状态。
一般来说,如果有理由不使用定时器,我们应该只在 Swing 程序中使用线程。
对于用户来说,它们之间没有明显的区别。
您对 revalidate 的调用向我表明您正在修改循环中组件的状态(添加、删除、更改位置等)。这对于关闭 EDT 来说不一定是安全的。如果您正在修改组件的状态,那么使用计时器而不是线程是一个令人信服的理由。使用没有适当同步的线程会导致难以诊断的细微错误。参见 Memory Consistency Errors .
在某些情况下,组件上的操作是在 tree lock 下完成的(Swing 确保它们本身是线程安全的),但在某些情况下它们不是。
我们可以将一个循环变成如下形式:
while ( condition() ) {
body();
Thread.sleep( time );
}
进入以下形式的 Timer:
new Timer(( time ), new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if ( condition() ) {
body();
} else {
Timer self = (Timer) evt.getSource();
self.stop();
}
}
}).start();
这是一个简单的示例,演示了同时使用线程和计时器的动画。绿色条在黑色面板上循环移动。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class SwingAnimation implements Runnable{
public static void main(String[] args) {
SwingUtilities.invokeLater(new SwingAnimation());
}
JToggleButton play;
AnimationPanel animation;
@Override
public void run() {
JFrame frame = new JFrame("Simple Animation");
JPanel content = new JPanel(new BorderLayout());
play = new JToggleButton("Play");
content.add(play, BorderLayout.NORTH);
animation = new AnimationPanel(500, 50);
content.add(animation, BorderLayout.CENTER);
// 'true' to use a Thread
// 'false' to use a Timer
if (false) {
play.addActionListener(new ThreadAnimator());
} else {
play.addActionListener(new TimerAnimator());
}
frame.setContentPane(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
abstract class Animator implements ActionListener {
final int period = ( 1000 / 60 );
@Override
public void actionPerformed(ActionEvent ae) {
if (play.isSelected()) {
start();
} else {
stop();
}
}
abstract void start();
abstract void stop();
void animate() {
int workingPos = animation.barPosition;
++workingPos;
if (workingPos >= animation.getWidth()) {
workingPos = 0;
}
animation.barPosition = workingPos;
animation.repaint();
}
}
class ThreadAnimator extends Animator {
volatile boolean isRunning;
Runnable loop = new Runnable() {
@Override
public void run() {
try {
while (isRunning) {
animate();
Thread.sleep(period);
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
};
@Override
void start() {
isRunning = true;
new Thread(loop).start();
}
@Override
void stop() {
isRunning = false;
}
}
class TimerAnimator extends Animator {
Timer timer = new Timer(period, new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
animate();
}
});
@Override
void start() {
timer.start();
}
@Override
void stop() {
timer.stop();
}
}
static class AnimationPanel extends JPanel {
final int barWidth = 10;
volatile int barPosition;
AnimationPanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
setBackground(Color.BLACK);
barPosition = ( width / 2 ) - ( barWidth / 2 );
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
int currentPos = barPosition;
g.setColor(Color.GREEN);
g.fillRect(currentPos, 0, barWidth, height);
if ( (currentPos + barWidth) >= width ) {
g.fillRect(currentPos - width, 0, barWidth, height);
}
}
}
}
关于Java 游戏循环(绘画)卡住了我的窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29745778/
我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht