GUI中移动图形对象时需要使用Timer定时器的场景有哪些?
Swing 中GUI移动对象时Timer的使用场景
你目前基于按键事件直接更新位置的实现,在「仅需要用户单次点击/按键触发位移」的简单场景下确实可以正常运行,但是存在明显的适用局限,以下场景必须使用Timer实现:
- 存在无需用户操作的自动运动元素:比如自动移动的NPC、弹幕、反弹小球、滚动背景、帧动画等,这类元素不需要用户触发事件也要持续更新位置,不用Timer无法实现对应效果。
- 需要流畅的按住持续移动效果:系统自带的按键重复触发有固定首延迟,且重复频率由系统设置决定,会导致按住按键时移动先卡顿再启动,速度不稳定。用Timer可以固定间隔更新位移,仅需要在按键事件里修改移动方向标志位,就能实现均匀流畅的按住移动效果,操作手感更好。
- 有大量统一更新的全局逻辑:如果把碰撞检测、状态刷新、计分、属性变更等逻辑分散到每个按键事件回调中,会产生大量重复代码,维护成本高。将所有全局更新逻辑放到Timer的回调中统一执行,代码结构更清晰,扩展性更强。
- 需要统一控制渲染帧率:可以通过调整Timer的触发间隔(比如设置为16ms对应约60帧),固定整个界面的更新频率,避免不同设备上运行速度差异过大的问题。
无Timer实现代码示例(仅按键触发移动)
import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; public class Test2 extends JPanel{ int x=100,y=200,velX=5,velY=5; int x2=300,y2=200; Action upAction; Action downAction; Action leftAction; Action rightAction; Action wAction; Action aAction; Action sAction; Action dAction; JLabel label3 = new JLabel(); public Test2() { this.setLayout(null); this.setFocusable(true); this.setBackground(Color.LIGHT_GRAY); JLabel label1 = new JLabel("Press WASD to control Green Circle"); label1.setBounds(40, 30, 250, 20); label1.setForeground(Color.GREEN); this.add(label1); JLabel label2 = new JLabel("Press Arrows to control Blue Circle"); label2.setBounds(250, 30, 250, 20); label2.setForeground(Color.BLUE); this.add(label2); label3 = new JLabel(""); label3.setBounds(200, 200, 250, 20); label3.setForeground(Color.RED); this.add(label3); upAction= new upAction(); downAction= new downAction(); leftAction = new leftAction(); rightAction = new rightAction(); wAction= new WAction(); aAction = new AAction(); sAction = new SAction(); dAction= new DAction(); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"), "uplol");; this.getActionMap().put("uplol", upAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"), "downlol"); this.getActionMap().put("downlol", downAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("LEFT"), "leftlol"); this.getActionMap().put("leftlol", leftAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "rightlol"); this.getActionMap().put("rightlol", rightAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('w'), "moveup");; this.getActionMap().put("moveup", wAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('a'),"moveleft"); this.getActionMap().put("moveleft", aAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('s'), "movedown"); this.getActionMap().put("movedown", sAction); this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('d'), "moveright"); this.getActionMap().put("moveright", dAction); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.GREEN); g.fillOval(x,y, 40, 40); g.setColor(Color.BLUE); g.fillOval(x2, y2, 40, 40); } public class upAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { y-=velY; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class downAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { y+=velY; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class rightAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { x+=velX; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class leftAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { x-=velX; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class WAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { y2-=velY; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class AAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { x2-=velX; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class DAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { x2+=velY; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public class SAction extends AbstractAction{ @Override public void actionPerformed(ActionEvent e) { y2+=velY; repaint(); checkCollision(); if(checkCollision()) { label3.setText("OVER"); velY=0; velX=0; } } } public boolean checkCollision() { return (Math.pow(Math.abs(x2-x),2)+Math.pow(Math.abs(y2-y), 2))<=1600; } }
Timer实现代码示例(支持自动运动)
import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class Move extends JPanel implements ActionListener{ javax.swing.Timer timer= new javax.swing.Timer(5, this); int y=0,x=0,velY=2,velX=2; @Override public void paint(Graphics g) { super.paint(g); this.setBackground(Color.MAGENTA); Graphics2D graphics2d = (Graphics2D)g; graphics2d.setColor(Color.BLUE); graphics2d.fill3DRect(100, 150, 100, 30, true); graphics2d.setColor(Color.RED); graphics2d.fillOval(200, 250, 28, 61); graphics2d.setColor(Color.BLACK); graphics2d.drawString("That boy is a monster", 80, 35); //timer.start(); } public Move() { this.setLayout(null); JLabel rotter = new JLabel("fool"); rotter.setBounds(140, 200, 53, 90); this.add(rotter); } @Override public void actionPerformed(ActionEvent e) { if(x<0||x>560) { velX=-velX; } if(y<0|y>360) { velY=-velY; } x+=velX; y+=velY; repaint(); } }
内容的提问来源于stack exchange,提问作者user16743592
相关产品推荐
相关产品推荐

