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

Can't Use .addKeyListener(this) for a static JPanel, but need the JPanel to stay static - Java

我正在尝试制作一个简单的程序,其中椭圆形跟随鼠标光标,如果您在键盘上输入 "r"、"g" 或 "b",椭圆形会发生变化相应地着色。

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

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

这里是Drivers.java里的代码,主要class.

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

}

I have a static JPanel, because I need it to be accessible in all functions and methods.

这不是将字段设为静态的好理由。

However, Java does not let you do this with a static JPanel.

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

请注意,您的编译器错误可能会随着以下简单内容而消失:

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

I need the JPanel to be static so I can set the color in the keyPressed(KeyEvent e) function.

这又不是该字段为静态的好理由。 Swing 侦听器可以随时通过 XxxEvent 参数的 getSource() 方法直接访问被侦听的组件。例如,如果您使用了 KeyListener,则其方法的 KeyEvent 参数有一个 getSource() 方法,该方法将 return 正在监听的组件(这里是您的绘图 JPanel)。如果您需要对其他组件或对象的引用,则通过构造函数 setter 参数将它们传递给侦听器。


  1. 您的主要问题是您试图在静态上下文中使用 this,而 this 在此上下文中不存在。
  2. 第一件事是不要让你的面板字段静态化。您声明您非常了解 Java,但随后给出了使其成为静态的错误理由。而是将其设为实例字段并在需要的地方传递实例。
  3. 此代码还有许多其他问题,包括:
    • 您有一个巨大的静态 main 方法。这个方法应该小得多,它的工作应该是创建程序的关键对象,设置它们 运行 就是这样。
    • 你有一个 Swing 程序,它在代码中有一个 while (true) 和一个 Thread.sleep(...),在以后的迭代中(当你更好地构建你的程序并在事件上启动所有 Swing 代码时thread) 在 Swing 事件线程上被调用的风险。你会想要摆脱这些家伙,并考虑改用 Swing Timer。
    • 您的 paintComponent 方法没有调用 super 的方法,破坏了 Swing 绘画链。
    • 您最好 使用 KeyListener 而是使用键绑定。
    • 甚至不需要您的 while (true) 块。不要轮询鼠标的位置,而是为此使用 MouseListener and/or MouseMotionListener。

例如:

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 字符相关联,我所要做的就是向我的 Map 添加另一个条目:

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

boom它完成了。如果我需要的不仅仅是 1-1 关联,那么我会考虑使用枚举或单独的 class 将相关属性放在一起。