不能将.addKeyListener(this)用于静态JPanel,但需要JPanel保持静态 - Java

时间:2015-06-27 12:36:49

标签: java swing static jpanel keylistener

我正在尝试创建一个椭圆跟随鼠标光标的简单程序,如果在键盘上输入“r”,“g”或“b”,椭圆会相应地改变颜色。

但是,我无法让KeyListener工作。这是我的问题。我有一个静态JPanel,因为我需要它可以在所有函数和方法中访问。但是,Java不允许您使用静态JPanel执行此操作。我需要JPanel是静态的,所以我可以在keyPressed(KeyEvent e)函数中设置颜色。

我非常了解Java的基础知识,并且正在掌握一些更复杂的概念。请尝试解释是否有任何复杂的代码。谢谢!

以下是Drivers.java中的代码,主要类。

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class Drivers implements KeyListener 
{

    // panel.red = panel.red - 3;
    // panel.green = panel.green - 3;
    // panel.blue = panel.blue - 3;

    public static JFrame frame = new JFrame();
    public static ShapesPanel panel = new ShapesPanel().addKeyListener(this);
    // Notice the error we get with the addKeyListener(this);

    public static void main(String[] args)
    {
        // Creates new pointer info
        PointerInfo info;
        // Creates a point (for mouse tracking)
        Point point;
        JLabel label = new JLabel();
        panel.add(label);
        // Set window size
        panel.setPreferredSize(new Dimension(300, 350));
        // Set panel inside frame
        frame.setContentPane(panel);
        // Transparent 16 x 16 pixel cursor image.
        BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
        // Create a new blank cursor.
        Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
        cursorImg, new Point(0, 0), "blank cursor");
        // Set the blank cursor to the JFrame.
        frame.getContentPane().setCursor(blankCursor);
        // Compile everything into the frame
        frame.pack();
        // Set frame to close on red exit button
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Get screen size
        Dimension sSize = Toolkit.getDefaultToolkit().getScreenSize();
        // Position frame
        frame.setLocation(sSize.width / 2 - frame.getWidth(), sSize.height / 2 - frame.getHeight());
        // Make frame visible
        frame.setVisible(true);
        // Set name of frame
        frame.setTitle("Graphical User Interface");
        // While loop to draw oval
        while(true)
        {
            // Repaint the panel 
            panel.repaint();
            // Get mouse info (for tracking)
            info = MouseInfo.getPointerInfo();
            // Set mouse location data to point
            point = info.getLocation();
            // Create variables to store coordinates of oval from mouse point location
            int x = (int) point.getX();
            int y = (int) point.getY();
            // Assign those coordinate variables to oval
            panel.x = x;
            panel.y = y;
//          System.out.println("X: " + x);
//          System.out.println("Y: " + y);
//          System.out.println("X: " + point.getX());
//          System.out.println("Y: " + point.getY());
            // Try-catch to sleep, to reduce some memory
            try
            {
                Thread.sleep(10);
            }
            catch(InterruptedException e)
            {

            }
        }
    }
    // If key is pressed
    public void keyPressed(KeyEvent e) 
    {
        // If key is R, change color and print that key has been pressed
        if (e.getKeyCode() == KeyEvent.VK_R) 
        {
            System.out.println("R");
            panel.red = 255;
            panel.green = 0;
            panel.blue = 0;
        }
        // If key is G, change color and print that key has been pressed
        if (e.getKeyCode() == KeyEvent.VK_G) 
        {
             System.out.println("G");
             panel.red = 0;
             panel.green = 255;
             panel.blue = 0;
        }
        // If key is B, change color and print that key has been pressed
        if (e.getKeyCode() == KeyEvent.VK_B) 
        {
            System.out.println("B");
            panel.red = 0;
            panel.green = 0;
            panel.blue = 255;
        }
    }
    // Doesn't do anything.. yet
    @Override
    public void keyReleased(KeyEvent e) 
    {

    }

    @Override
    public void keyTyped(KeyEvent e) 
    {

    }
}

而且,这是ShapesPanel.java中的代码:

import java.awt.*;
import javax.swing.*;

public class ShapesPanel extends JPanel 
{
    // Create x and y variables, and set as default to zero
    public int x = 0, y = 0;
    // Create RGB variables, used for changing color
    public int red = 0, green = 0, blue = 0;
    private static final long serialVersionUID = 1L;

    // Create new paintComponent function, using an override
    @Override
    public void paintComponent(Graphics g)
    {
        // Create new Graphics2D g2 version
        Graphics2D g2 = (Graphics2D) g;
        // Reset screen, so there are no trails
        g2.clearRect(0, 0, getWidth(), getHeight());
        // Set background, currently unfunctional
//      g2.setBackground(new Color(235, 150, 30));
        // Set color palette, using RGB variables
        g2.setColor(new Color(red, green, blue));
        // Use color palette to draw oval, using x and y variables
        g2.fillOval(x, y, 100, 100);
    }

}

1 个答案:

答案 0 :(得分:6)

  

我有一个静态JPanel,因为我需要它可以在所有函数和方法中访问。

这不是使字段静态的好理由。

  

但是,Java不允许您使用静态JPanel执行此操作。

这根本不是真的。您可以将KeyListeners或任何其他类似的构造添加到静态和非静态字段中。您的问题与使用静态字段的限制无关。这都是因为您尝试在this不存在的静态上下文中使用this

请注意,您的编译器错误可能会像以下一样简单:

public static ShapesPanel panel = new ShapesPanel().addKeyListener(new Drivers());
  

我需要JPanel是静态的,所以我可以在keyPressed(KeyEvent e)函数中设置颜色。

这再次不是该领域静止的好理由。 Swing侦听器可以通过XxxEvent参数的getSource()方法随时随地直接访问侦听组件。例如,如果您使用了KeyListener,则其方法的KeyEvent参数具有getSource()方法,该方法将返回正在侦听的组件(此处为您的绘图JPanel)。如果需要引用其他组件或对象,则通过构造函数setter参数将它们传递给侦听器。

  1. 您的主要问题是您尝试在静态上下文中使用this,并且在此上下文中不存在this
  2. 首先,不要让你的面板字段静止。你说你很熟悉Java,但是给它一个错误的理由让它变得静止。而是将其设为实例字段并在需要的地方传递实例。
  3. 此代码还有许多其他问题,包括:
    • 你有一个巨大的静态主要方法。这个方法应该小得多,它的工作应该是创建你的程序的关键对象,设置它们运行,就是这样。
    • 你有一个Swing程序,代码中有while (true)Thread.sleep(...),在以后的迭代中(当你更好地构建程序并在事件线程上启动了所有Swing代码时)在Swing事件线程上调用冒险。你会想要摆脱这些家伙,并考虑使用Swing Timer。
    • 你的paintComponent方法没有调用super的方法,打破了Swing绘画链。
    • 使用KeyListener而不是使用Key Bindings会更好。
    • 甚至不需要while (true)块。不要轮询鼠标的位置,而是使用MouseListener和/或MouseMotionListener进行此操作。
  4. 例如:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.event.*;
    import java.util.HashMap;
    import java.util.Map;
    import javax.swing.*;
    
    public class KeyBindingTest {
       // start gui
       private static void createAndShowGui() {
          KeyBindingPanel mainPanel = new KeyBindingPanel();
    
          JFrame frame = new JFrame("Key Binding Example");
          frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
          frame.getContentPane().add(mainPanel);
          frame.pack();
          frame.setLocationByPlatform(true);
          frame.setVisible(true);
       }
    
       // start all in a thread safe manner
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }
    
    class KeyBindingPanel extends JPanel {
       private static final long serialVersionUID = 1L;
       private static final int PREF_W = 600;
       private static final int PREF_H = PREF_W;
       private static final Color BACKGROUND = Color.WHITE;
       private Color ovalColor = Color.blue;
       private int ovalX = PREF_W / 2;
       private int ovalY = PREF_H / 2;
       private int ovalWidth = 100;
    
       public KeyBindingPanel() {
          setName("Key Binding Eg");
          setBackground(BACKGROUND);
    
          final Map<Color, Integer> colorKeyMap = new HashMap<>();
          colorKeyMap.put(Color.BLUE, KeyEvent.VK_B);
          colorKeyMap.put(Color.RED, KeyEvent.VK_R);
          colorKeyMap.put(Color.GREEN, KeyEvent.VK_G);
    
          // set Key Bindings
          int condition = WHEN_IN_FOCUSED_WINDOW;
          InputMap inputMap = getInputMap(condition);
          ActionMap actionMap = getActionMap();
    
          for (final Color color : colorKeyMap.keySet()) {
             int keyCode = colorKeyMap.get(color);
             KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0);
             inputMap.put(keyStroke, keyStroke.toString());
             actionMap.put(keyStroke.toString(), new ColorAction(color));
          }
    
          MyMouse myMouse = new MyMouse();
          addMouseMotionListener(myMouse);
       }
    
       public void setOvalColor(Color color) {
          ovalColor = color;
          repaint();
       }
    
       public void setOvalPosition(Point p) {
          ovalX = p.x;
          ovalY = p.y;
          repaint();
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          Graphics2D g2 = (Graphics2D) g;
          g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
          g2.setColor(ovalColor);
          int x = ovalX - ovalWidth / 2;
          int y = ovalY - ovalWidth / 2;
          g2.fillOval(x, y, ovalWidth, ovalWidth);
       }
    
       @Override // make panel bigger
       public Dimension getPreferredSize() {
          if (isPreferredSizeSet()) {
             return super.getPreferredSize();
          }
          return new Dimension(PREF_W, PREF_H);
       }   
    }
    
    class ColorAction extends AbstractAction {
       private static final long serialVersionUID = 1L;
       private Color color;
    
       public ColorAction(Color color) {
          this.color = color;
       }
    
       @Override
       public void actionPerformed(ActionEvent e) {
          // get reference to bound component
          KeyBindingPanel panel = (KeyBindingPanel) e.getSource();
          panel.setOvalColor(color);
       }
    }
    
    class MyMouse extends MouseAdapter {
       @Override
       public void mouseMoved(MouseEvent e) {
          // get reference to listened-to component
          KeyBindingPanel panel = (KeyBindingPanel) e.getSource();
          panel.setOvalPosition(e.getPoint());
       }
    }
    

    您可能会问,为什么在创建密钥绑定时使用Map<Color, Integer>

    这样做允许我使用for循环来避免代码重复。通常更简洁的代码更容易理解和调试。它还使以后更容易增强程序。例如,如果稍后我想添加Color.CYAN并将其与c char相关联,那么我所要做的就是在我的Map中添加另一个条目:

    colorKeyMap.put(Color.CYAN, KeyEvent.VK_C);
    

    热潮已经完成了。如果我需要超过1-1的关联,那么我会考虑使用Enum或单独的类来保存相关的属性。