Spik330
Spik330

Reputation: 502

JPanel JComponents not repainting unless JFrame.validate(); is called after adding components to a visable frame

Frame -> MainContentPane -> MapPanel -> Map -> Tile[]

JFrame -> JPanel(new GridBagLayout) -> JPanel -> JPanel(new GridLayout(31,30) -> JComponent

I'm trying to paint 32x32 pix tiles on a JPanel, but no matter where I call repaint(); it will only paint the tiles if I call validate(); on the JFrame. If I bypass Panels and paint directly (use of the draw() methods) onto the MapPanel the images will only paint if I resize or move the JFrame so that the Frame has to be repainted by the repaintmanager. However calling repaint(), validate() or both on my ContentPanel will not paint the images.

If I understand how Java.swing paints thing right, if I call a repaint on the highest level Component it should repaint all child Components that the repaintmanager thinks should be repainted. Since I am adding components after the frame has been set to visible I need to call validate() on the highest level Component to tell the repaintmanager that there are new things. Am I right with this understanding?

Things that will not work:

telling me to add all the components before setting the frame to visible. The Map and Tile[] are going to be changing quite regularly, and it would be very impractical to reset the Frame every time I add/remove something.

public class NetScore {

    public static void main(String[] args) {
        MapPanel mapPanel = new MapPanel();
        InfoPanel infoPanel = new InfoPanel();
        ImageLoader imageLoader = new ImageLoader();

        Player player = new Player("Tester", imageLoader);

        JPanel contentPane = new MainContentPane((JPanel)mapPanel, (JPanel)infoPanel);

        System.out.println(mapPanel.getHeight());
        System.out.println(mapPanel.getWidth());

        MapBuilder mapBuilder = new MapBuilder(player, imageLoader);

        Map map = new Map(mapBuilder, mapPanel, player);
        map.drawMap();

        System.out.println();
    }

    }
public class MapPanel extends JPanel implements ActionListener{

    Map map;
    Timer clock;
    public MapPanel(){
        clock = new Timer(500, this);
        clock.start();

    }

    public void addMap(Map map){
        this.map = map;
        this.add(map);
        this.validate();
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        System.out.println(map == null);
        System.out.println("paint mapPanel");
        Graphics2D g2 = (Graphics2D) g;
        if(map == null){
            //map.draw(g2);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //repaint();

    }
    }
public class MainContentPane extends JPanel implements ActionListener{

    public JFrame frame;
    Timer clock;

    public MainContentPane(JPanel mapPanel ,JPanel infoPanel){
        clock = new Timer(500, this);
        clock.start();
        frame = new Frame();
        JPanel contentPane = new JPanel();
        frame.setContentPane(contentPane);
        contentPane.setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();

        c.weightx = 2;
        c.gridx = 0;
        c.gridy = 0;
        c.fill = GridBagConstraints.BOTH;
        contentPane.add(mapPanel, c);

        c.weightx = 1;
        c.weighty = 1;
        c.gridx = 1;
        c.gridy = 0;
        c.fill = GridBagConstraints.BOTH;


        contentPane.add(infoPanel, c);
        frame.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();

    }

    class Frame extends JFrame{

        public Frame(){
            this.setTitle("netScore");
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            this.setBounds(100, 10, 1500, 1000);
            this.setResizable(false);
        }

    }
    }
public class Map extends JPanel{


    private Tile[][] tiles;
    private MapBuilder mapBuilder;
    private Player player;


    public Map(MapBuilder mapBuilder, MapPanel mapPanel, Player player){
        this.player = player;
        this.mapBuilder = mapBuilder;
        this.setLayout(new GridLayout(31,30));
        mapPanel.addMap(this);
    }

    public void loadMap(){
        tiles = mapBuilder.buildMap();
    }

    public void drawMap(){
        loadMap();
        this.removeAll();
        for(int i = 0; i < tiles.length; i++){
            for(int p = 0; p < tiles[i].length; p++){
                this.add(tiles[i][p]);
            }
        }
        validate();
    }
    public void draw(Graphics2D g2){
        if(tiles != null){
            for(int i = 0; i < tiles.length; i++){
                for(int p = 0; p <tiles[i].length; p++){
                    tiles[i][p].draw(g2, i*32, p*32);
                }
            }
        }
    }

      //    private class GlassPanel extends JComponent{
      //        
      //        
      //        @Override
      //        protected void paintComponent(Graphics g) {
      //            super.paintComponent(g);
      //            Graphics2D g2 = (Graphics2D) g;
      //            g2.drawImage(player.getImage(), player.getX(), player.getY(), null);
      //            
      //        }
      //        
      //    }
      }
public class Tile extends JComponent{

    private int id;
    private boolean collision;
    private BufferedImage image;

    public Tile(char diCo, int id, ImageLoader imageLoader){
        this.id = id;

        collision = (Character.isUpperCase(diCo));
        image = imageLoader.getTileImage(id, diCo); 
        setPreferredSize(new Dimension(32, 32));
    }

     // public Dimension getPreferredSize(){
     //     return new Dimension(32,32);
     // }

    public boolean isCollision() {
        return collision;
    }

    public void draw(Graphics2D g2, int x, int y){
        System.out.println("paint tile, id "+ id);
        g2.drawImage(image, null, x, y);
    }

    public void paintComponent(Graphics g){
        System.out.println("paint tile, id "+ id);
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.drawImage(image, null, 0, 0);
    }
}

Edit: Added minimal code. This will work if I replace validate(); with revalidate(); But I don't want to use revalidate(); if nothing on the Panel needs to be invalided. Am I wright with that thinking?

public class test {
    public static void main(String[] args) throws Exception {

    MapPanel mapPanel = new MapPanel();
    ContentPanel contentPanel = new ContentPanel((JPanel)mapPanel);
    Map map = new Map();
    mapPanel.add(map);
    map.loadMap();

    }
}
class MapPanel extends JPanel{
    public MapPanel(){
    //this.setBackground(Color.BLACK);

    }
}

class Map extends JPanel{
   BufferedImage image;
   public Map(){
        try {
            image = ImageIO.read(new File("graphics//brick_brown0.png"));
        } catch (IOException e) {
            System.err.println("can't find file.");
        }
    setLayout(new GridLayout(31,30));
    setPreferredSize(new Dimension(962,992));
}
public void loadMap(){
    for(int i = 0; i < 30; i++){
        for(int p = 0; p < 31; p++){
            add(new Tile(image));
        }
    }
    validate();
}
}

class Tile extends JComponent{

BufferedImage image;
public Tile(BufferedImage image){
    this.image = image;
    setPreferredSize(new Dimension(32,32));
}

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.drawImage(image, null, null);
}
}


class ContentPanel extends JPanel implements ActionListener{
Timer clock = new Timer(100, this);

public ContentPanel(JPanel mapPanel){
    clock.start();
    setLayout(new BorderLayout());
    JFrame frame = new Frame();
    frame.setContentPane(this);
    add(mapPanel);
    frame.setVisible(true);
}

@Override
public void actionPerformed(ActionEvent e) {
    repaint();
}

private class Frame extends JFrame{ 
    public Frame(){
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setBounds(100, 100, 1000, 1000);
    }
}
}

Upvotes: 1

Views: 505

Answers (1)

Andrew Thompson
Andrew Thompson

Reputation: 168845

The basic problem with the code as posted was that the JFrame was being set visible prior to the components being added. While there are ways to add components and make them visible after the top-level container becomes visible, they seem unnecessary in this case.

Here is a working version that uses an image generated at run-time, with a little space in the GridLayout to show that the grid is 31 x 30 components.

enter image description here

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

public class TestRepaint {

    public static void main(String[] args) throws Exception {

        MapPanel mapPanel = new MapPanel();
        Map map = new Map();
        mapPanel.add(map);
        map.loadMap();
        new ContentPanel((JPanel) mapPanel);
    }
}

class MapPanel extends JPanel {

    public MapPanel() {
        this.setBackground(Color.RED);
    }
}

class Map extends JPanel {

    BufferedImage image;

    public Map() {
        image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_BGR);
        setLayout(new GridLayout(31, 30,2,2));
        //setPreferredSize(new Dimension(962, 992));
    }

    public void loadMap() {
        for (int i = 0; i < 30; i++) {
            for (int p = 0; p < 31; p++) {
                add(new Tile(image));
            }
        }
        validate();
    }
}

class Tile extends JComponent {

    BufferedImage image;

    public Tile(BufferedImage image) {
        this.image = image;
        setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.drawImage(image, null, null);
    }
}

class ContentPanel extends JPanel implements ActionListener {

    Timer clock = new Timer(100, this);

    public ContentPanel(JPanel mapPanel) {
        clock.start();
        setLayout(new BorderLayout());
        JFrame frame = new Frame();
        frame.setContentPane(this);
        add(mapPanel);
        frame.pack();
        frame.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }

    private class Frame extends JFrame {

        public Frame() {
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            //setBounds(100, 100, 1000, 1000);
        }
    }
}

Upvotes: 1

Related Questions