Search code examples
javaswingjtextfield

JTextField on Key Entered flashing black


I am making a game canvas using swing and decided to use JTextField's for the input of a username and password into the panel.

I am buffering an image then rendering it onto the screen instead of drawing everything directly onto the panel real-time.

I have ran into a problem though, I paint a background and have set both of my text fields to opaque, but it seems that whenever I go to enter something into those text field's it flashes a black box where the JTextField is.

It happens in both of my username and password fields. Any idea of what the cause of this could be?

Other helpful information: Whenever I click on a text box, both of the components flash black where the first character would be.

EDIT -- I just noticed that the login button I have also flashes black when MOUSE_ENTERED and 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());


    }
}

}

Here is text fields: (from a seperate class entirely calling to the GamePanel using getPanel()...)

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

Thanks!


Solution

  • The basic problem is, you circumventing Swings repaint process and not honoring the EDT when you up-date your graphics.

    JComponent#getGraphics is a temporary/scratch buffer which will be re-draw on the next repaint. Also, if you didn't create the graphics, you shouldn't dispose it!

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

    I don't know if this will fix it, but it can't hurt. (This WILL effect you FPS and you should be taking into consideration how long it took to paint and how long you want to wait)

    Alternativly, rather then calling paintScreen(), you could call repaint (or have paintScreen do it) and override the paintComponent method to paint your buffer.

    This would allow Swing to resume control over the paint process properly.