1

我正在使用 swing 制作游戏画布,并决定使用JTextField's 在面板中输入用户名和密码。

我正在缓冲图像,然后将其渲染到屏幕上,而不是实时将所有内容直接绘制到面板上。

虽然我遇到了一个问题,我画了一个背景并将我的两个文本字段都设置为不透明,但似乎每当我在这些文本字段中输入一些内容时,它都会闪烁一个黑框JTextField

它发生在我的用户名和密码字段中。知道这可能是什么原因吗?

其他有用信息:每当我单击文本框时,两个组件都会在第一个字符所在的位置闪烁黑色。

编辑 - 我刚刚注意到我的登录按钮在 和 时也闪烁MOUSE_ENTERED黑色MOUSE_EXIT

public class GamePanel extends JPanel implements Runnable {


public GamePanel(int width, int height) {
    this.pWidth = width; 
    this.pHeight = height;

    setController(new LoginGameController(this));

    setPreferredSize( new Dimension(pWidth, pHeight));
    setBackground(Color.BLACK);

    setFocusable(true);
    requestFocus();    // the JPanel now has focus, so receives key events

    // create game components

    addMouseListener(this);
    addKeyListener(this);

    setLayout(null);

    startGame();
}
 private void startGame()
  // initialise and start the thread 
  { if (animator == null) {
      animator = new Thread(this);
      animator.start();
    }
  }

public void run() {
    while(true) {
          gameUpdate(); 
          if(getGraphics() != null){
              gameRender();   // render the game to a buffer
              paintScreen();  // draw the buffer on-screen
          }
          try {
            Thread.sleep(28);
          } catch (InterruptedException e) {}
    }
}

private void paintScreen() {
    Graphics2D g = (Graphics2D) getGraphics();

    if ((g != null) && (img != null))
            g.drawImage(img, 0, 0, null);

    Toolkit.getDefaultToolkit().sync();
    g.dispose();
}

private void gameRender() {
    if(getWidth() > 0 && getHeight() > 0)
        img = createImage(getWidth(), getHeight());

    if(img != null) {
        Graphics2D g = (Graphics2D) img.getGraphics();
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, pWidth, pHeight);

        getController().render(img);

        paintComponents(img.getGraphics());


    }
}

}

这是文本字段:(从一个使用 getPanel() 完全调用 GamePanel 的单独类...)

//Setup Login fields
    usernameTF = new JTextField();
    usernameTF.setOpaque(false);
    usernameTF.getCaret().setBlinkRate(0);
    usernameTF.setForeground(Color.WHITE);
    usernameTF.setBounds(USERNAME_FIELD);
    usernameTF.setBorder(null);
    getPanel().add(usernameTF);

    passwordTF = new JPasswordField();
    passwordTF.setOpaque(false);
    passwordTF.getCaret().setBlinkRate(0);
    passwordTF.setForeground(Color.WHITE);
    passwordTF.setBounds(PASSWORD_FIELD);
    passwordTF.setBorder(null);
    getPanel().add(passwordTF);

    loginBtn = new JButton();
    loginBtn.setOpaque(false);
    loginBtn.setBackground(null);
    loginBtn.setBorder(null);
    loginBtn.setBounds(LOGIN_BUTTON);
    loginBtn.addMouseListener(getPanel());
    getPanel().add(loginBtn);

谢谢!

4

2 回答 2

3

基本问题是,您在更新图形时绕过了 Swings 重绘过程并且不遵守 EDT。

JComponent#getGraphics是一个临时/临时缓冲区,将在下一次重绘时重新绘制。此外,如果您没有创建图形,则不应丢弃它!

public void run() {
    while(true) {
          gameUpdate(); 
          if(getGraphics() != null){
              gameRender();   // render the game to a buffer
              try {
                  SwingUtilities.invokeAndWait(new Runnable() {
                      public void run() {
                          paintScreen();  // draw the buffer on-screen
                      }
                  });
              } catch (Exception exp) {
                  exp.printStackTrace(); // please clean this up
              }
          }
          try {
            Thread.sleep(28);
          } catch (InterruptedException e) {}
    }
}

我不知道这是否会解决它,但它不会受到伤害。(这会影响你的 FPS,你应该考虑绘画需要多长时间以及你想等待多长时间)

或者,paintScreen()您可以调用repaint(或已经paintScreen这样做)并覆盖该paintComponent方法来绘制缓冲区,而不是调用。

这将允许 Swing 正确地恢复对绘制过程的控制。

于 2012-10-17T02:56:22.547 回答
2

一个窗口或组件至少有三种重绘方式。

  1. 当操作系统丢失部分窗口缓冲区时,根据需要从操作系统请求。
  2. 当库组件改变其状态时,根据需要从库组件中获取。
  3. 通过手动操作自己的帧缓冲区。

通常,以下途径是活跃的:

操作系统可以请求更新特定的矩形。这是由工具包拾取的。repaint此外,组件也可以通过其方法向工具包发出其更改的信号。该工具包会持续收集合并传入的重绘请求一段时间,然后要求窗口重绘特定的矩形。窗口的默认操作是通过调用其paintComponent不与实体子级重叠的方法来绘制自身,然后递归绘制其组件。

如果您自己进行渲染,工具包也会这样做。由于您没有覆盖您的paintComponent,因此当此方法运行时,它将作为默认值。默认操作是什么都不做。该工具包然后拾取空帧缓冲区并在其上绘制按钮(不绘制其背景)。

您可以禁用工具包的渲染过程并自己进行所有渲染(获取缓冲区,在其中绘制,然后提交)。您可以调用该setIgnoreRepaint(true)方法来忽略操作系统的请求。

于 2012-10-17T03:17:10.627 回答