Displaying buttons at startup

I am trying to make a startup screen for my game, with a background picture and some buttons. However, when I call my Buttons’ paint(Graphics) method, they do not appear on the screen. This code :


   public void paint(Graphics g) {
      g.drawImage(frontpic, 0, 0, Color.white, null);
      quitButton.paint(g);
   }

produces only my background image called frontpic. Why is this? Here is the initialisation I did for my Buttons :


      quitButton = new JButton(QUITGAME);
      quitButton.setActionCommand(QUITGAME);
      quitButton.addActionListener(this);

      getContentPane().setLayout(null);
      getContentPane().add(quitButton);

      getContentPane().setBackground(Color.white);

      // TODO : Set according to resolution. 

      quitButton.setBounds(new Rectangle(850, 30, 120, 20));

      show();

Calling paint on a component will repaint the component but I guess is badly placed.
Try to use the update method to trigger components repaint, this way you should be more coherent with the Swing gfx pipeline.


public void update (Graphics g) {
   this.paint(g);
   quitButton.paint(g);
}

Well, something similar. The code works if I call quitButton’s repaint() method instead of paint(Graphics). Why this should be, I have no idea. If anyone cares to enlighten me, I would be most grateful.

http://java.sun.com/products/jfc/tsc/articles/painting/

Yes, that’s where I got the idea of using repaint. But as far as I could see, it doesn’t explain why this is what works. OTOH, maybe I just missed it skimming through and trying things one after another. :slight_smile:

You are using Swing components and are bypassing the RepaintManager.

The article states:
"Swing Painting Guidelines

Swing programs should understand these guidelines when writing paint code:

For Swing components, paint() is always invoked as a result of both system-triggered and app-triggered paint requests;update() is never invoked on Swing components.

Programs may trigger a future call to paint() by invoking repaint(), but shouldn’t call paint() directly…"

The Swing double buffering may be related to why calling paint() directly wasn’t working.

OK, I think I understand that. It is only moderately un-obvious after being pointed out ;). Maybe you can understand why this doesn’t work?


      scenarionames = new String[]{"blah", "bleh"};

      scenList   = new JList(scenarionames);
      scenViewer = new JScrollPane();
      scenViewer.setViewportView(scenList);
      scenList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      scenList.setVisibleRowCount(-1);
      scenViewer.setAlignmentX(LEFT_ALIGNMENT);

      getContentPane().setLayout(null);
      getContentPane().add(scenViewer);
      getContentPane().add(scenList);

      scenViewer.setBounds(new Rectangle(400, 100, 200, 100));
      scenList.setBounds(new Rectangle(100, 100, 200, 500));
      show();

The way it doesn’t work is that scenViewer is displayed, a nice gray rectangle, but with no list inside it. I display the raw list beside it as a test; that comes out fine - one blah, one bleh, both selectable. I’ve looked through the examples, and I cannot figure out what I am doing differently from what they do. I have no painting code this time, I leave everything to the builtin paint method, which evidently calls paintComponents just as it is supposed to. Why isn’t the JScrollPane painting its contents?

A component can only be in one container. You have added scenList to the JScrollPane AND the content pane…

Don’t add the list to the content pane and don’t call setBounds on the list.

btw. I usually just use

new JScrollPane(scenList);

instead of the two step process to construct then add to the view port view.

Ah, doesn’t that look lovely, now? :smiley: Thank you. I’m not sure whether to feel stupid for not seeing that, or pleased because it works at last. :-/ About the one-step, I used that too at first, then changed it do see if it made a difference.

Aggh! That is, the code works if I put it in the constructor of my game-initialiser object. However, I would like for the list to pop up when the player presses the “New Game” button, which means I would like to have the list waiting in the wings, then update in the actionPerformed method. As far as I understand the thread issues, this is safe, right?

However, if I do things this way, I get that grey rectangle again, instead of the list within a viewer that I get if the code is in the constructor. I’ve tried putting the initialisation code in the constructor and just adding it in actionPerformed, like this:


  constructor () {
      (...)
      scenList   = new JList(scenarionames);
      scenViewer = new JScrollPane(scenList);
      scenList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      scenList.setVisibleRowCount(-1);
      scenViewer.setBounds(new Rectangle(-1000, -1000, 200, 500));
   }

   actionPerformed {
      getContentPane().add(scenViewer);
      repaint();
   }

with the same result. Next I tried initialising scenViewer with width and height both zero, like so :


  constructor () {
      (...)
      scenList   = new JList(scenarionames);
      scenViewer = new JScrollPane(scenList);
      scenList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      scenList.setVisibleRowCount(-1);
      scenViewer.setBounds(new Rectangle(100, 100, 0, 0));
      getContentPane().add(scenViewer);      
   }
    actionPerformed {
      scenViewer.setBounds(new Rectangle(100, 100, 200, 500));
      repaint();
   }

again with the same result. Finally - success at last! - I initialised it to be off screen, then moved it back in when actionPerformed was called, thus :


  constructor () {
      (...)
      scenList   = new JList(scenarionames);
      scenViewer = new JScrollPane(scenList);
      scenList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      scenList.setVisibleRowCount(-1);
      scenViewer.setBounds(new Rectangle(-1000, -1000, 200, 500));
      getContentPane().add(scenViewer);      
   }

    actionPerformed {
      scenViewer.setBounds(new Rectangle(100, 100, 200, 500));
      repaint();
   }

This did have the side effect of suddenly blanking (making white) half my screen, but I was able to fix that in paint(). Could you explain to me why these apparently similar procedures give such different results? You’ve been very helpful so far.

Chances are that the container needed more info - a revalidate() call perhaps? What you can try is setting a break point in the parent containers paint method and then stepping through to see what is happening.

But I would add the scroll pane in the beginning and do a setVisible(false) on it to hide it. The setVisible(true) when your button is pressed.

Ah, that works and is so much more elegant. Thanks.