【问题标题】:How to move a rectangle using keys in java如何使用java中的键移动矩形
【发布时间】:2015-05-27 22:59:57
【问题描述】:

我一直在尝试找出如何使用箭头键移动矩形,但似乎有问题。
我正在使用KeyListener 来检测所有的关键输入。
我不知道如何使用 KeyBinding,因此我不希望解决方案拥有它。
我打算在掌握 KeyListener 之后立即学习它。请给我有关如何解决它的建议。

package expo;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Expo extends JPanel implements KeyListener{
    int x = 0; 
    int y = 0;

    @Override
    public void keyTyped(KeyEvent e) {
        //System.out.println("Key Typed");
    }

    @Override
    public void keyPressed(KeyEvent e) {

        if(e.getKeyCode() == KeyEvent.VK_DOWN){
            System.out.println("Key Pressed" + e.getKeyCode());
            y = y + 2;       
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        //System.out.println("Key Released");
    }

    public void paint(Graphics g){
        g.setColor(Color.BLUE);
        g.drawRect(x ,y,100,100);

        repaint();
    }

    public static void main(String[] args) throws InterruptedException {

        Expo expo = new Expo();
        JFrame f = new JFrame();

        f.setVisible(true);
        f.setSize(500,500);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.addKeyListener(expo);

        f.add(expo);
        f.repaint();           
    }
}

【问题讨论】:

  • 1) 覆盖paintComponent,而不是paint 2) 不要在paint(或paintComponent)中调用repaint 3) 如果您希望在KeyListener.keyPressed 被触发后重新绘制,那么调用重绘。 4) 如果 KeyListener 没有触发,请确保包含 Listener 的组件具有焦点
  • 重绘矩形时,会在原来的上面重绘
  • 我也试过paintcomponent,但它不会显示矩形
  • @yj2000 你破坏了绘制链,你必须调用super 方法才能更新背景。绘画是一个破坏性的过程
  • 谢谢,这对我帮助很大!

标签: java keylistener


【解决方案1】:
  1. KeyListener上使用key bindings API,它解决了KeyListener遭受的焦点相关问题。 How to Use Key Bindings
  2. 不要破坏油漆链。如果您覆盖 paint 方法之一,则必须将其称为 super 实现。
  3. 避免覆盖paint,它通常在绘制过程中很高,并且由于绘制的工作方式,绘制子组件时并不总是被调用,这可能会导致一些有趣的问题。约定建议改用paintComponent。请参阅Painting in AWT and SwingPerforming Custom Painting 了解更多详情
  4. 尝试最后调用JFrame#setVisible,建立UI后,您会发现它引起的问题更少

举个例子……

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Expo extends JPanel {

    int x = 0;
    int y = 0;

    public Expo() {
        bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                y += 2;
                if (y + 100 > getHeight()) {
                    y = getHeight() - 100;
                }
                repaint();
            }
        });
        bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                y -= 2;
                if (y < 0) {
                    y = 0;
                }
                repaint();
            }
        });
    }

    public void bindKeyStrokeTo(int condition, String name, KeyStroke keyStroke, Action action) {
        InputMap im = getInputMap(condition);
        ActionMap am = getActionMap();

        im.put(keyStroke, name);
        am.put(name, action);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.BLUE);
        g.drawRect(x, y, 100, 100);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(500, 500);
    }

    public static void main(String[] args) throws InterruptedException {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Expo());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

这一切都很酷,但是现在,当你按住键时,矩形移动,暂停,然后开始稳定移动!?

这其实很正常。相反,您可以做的是建立一个“更新循环”,持续监控一组标志的状态,决定在设置这些标志时做什么并更新 UI。

所以,它的作用是设置一个 Swing Timer,它每 40 毫秒滴答一次,检查当前“垂直键状态”的状态,相应地更新 y 位置并安排一个 repaint,这个由于我们不依赖于重复的击键,因此可以实现更流畅的移动。

这也展示了按键绑定 API 的强大功能,因为它建立了一个 Action 来处理上下移动以及相关的按键释放...整洁

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Expo extends JPanel {

    int x = 0;
    int y = 0;

    public enum VerticalKey {

        UP, DOWN, NONE;
    }

    public enum HorizontalKey {

        LEFT, RIGHT, NONE;
    }

    private VerticalKey verticalKeyState = VerticalKey.NONE;
    private HorizontalKey horizontalKeyState = HorizontalKey.NONE;

    public Expo() {
        bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "pressed.down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new VerticalAction(VerticalKey.DOWN));
        bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "released.down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), new VerticalAction(VerticalKey.NONE));
        bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "pressed.up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new VerticalAction(VerticalKey.UP));
        bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "released.up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), new VerticalAction(VerticalKey.NONE));

        Timer timer = new Timer(40, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                switch (verticalKeyState) {
                    case UP:
                        y -= 2;
                        break;
                    case DOWN:
                        y += 2;
                        break;
                }
                if (y + 100 > getHeight()) {
                    y = getHeight() - 100;
                } else if (y < 0) {
                    y = 0;
                }

                repaint();
            }
        });
        timer.start();
    }

    public void bindKeyStrokeTo(int condition, String name, KeyStroke keyStroke, Action action) {
        InputMap im = getInputMap(condition);
        ActionMap am = getActionMap();

        im.put(keyStroke, name);
        am.put(name, action);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.BLUE);
        g.drawRect(x, y, 100, 100);
    }

    public void setVerticalKeyState(VerticalKey verticalKeyState) {
        this.verticalKeyState = verticalKeyState;
        System.out.println(verticalKeyState);
    }

    public void setHorizontalKeyState(HorizontalKey horizontalKeyState) {
        this.horizontalKeyState = horizontalKeyState;
    }

    public class VerticalAction extends AbstractAction {

        private VerticalKey verticalKey;

        public VerticalAction(VerticalKey verticalKeys) {
            this.verticalKey = verticalKeys;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            setVerticalKeyState(verticalKey);
        }

    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(500, 500);
    }

    public static void main(String[] args) throws InterruptedException {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Expo());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

【讨论】:

    【解决方案2】:

    只需更改这两段代码:

    @Override
    public void keyPressed(KeyEvent e) {
    
        if(e.getKeyCode() == KeyEvent.VK_DOWN){
            System.out.println("Key Pressed" + e.getKeyCode());
            y = y + 2;
    
            repaint();//add this line to update the UI
        }
    }
    

    public void paint(Graphics g){
        super.paint(g);//you should always call the super-method first
    
        g.setColor(Color.BLUE);
        g.drawRect(x ,y,100,100);
    }
    

    这将解决问题。虽然我建议覆盖paintComponent 而不是paint(阅读本文中有关“绘制方法”的部分:Painting in AWT and Swing)。基本上,我所做的更改如下:
    keyPressed 这行:repaint(); 将在方块移动后更新 UI。实际上,该正方形之前也已移动,但在更新 UI 之前不会显示更改。在paint 这一行:super.paint(g); 使面板首先进行默认绘制,其中包括清除整个面板。我已经删除了repaint(); 调用,因为它完全没用。

    注意: 如果您使用paintComponent 而不是paint,则必须将第一个调用从super.paint(g) 更改为super.paintComponent(g) 以避免堆栈溢出。

    【讨论】:

      猜你喜欢
      • 2015-07-26
      • 1970-01-01
      • 1970-01-01
      • 2012-01-02
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多