Slynus
Slynus

Reputation: 35

Java Swing : Create a Grid of custom JComponents with padding

I'm trying to create a grid of customs JComponents. I want the grid being resizable without deform on my JComponent.

But I am only able to get a grid with clipped JComponent and bad resizing.

What I get :

link

What I want :

link

Here is my code :

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import javax.swing.*;


public class Pawn extends JComponent {

   private static Color pileColor = Color.LIGHT_GRAY;
   private static Color faceColor = Color.DARK_GRAY;

   private static Color mouseOverColor = new Color(255, 255, 127);
   private static int padding = 10;

   private String label;

   private boolean pawnState;
   private int radius;

   private int row;
   private int column;

   public static void main(String[] args) {
      javax.swing.SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            createAndShowGUI();
         }
      });
   }

   private static void createAndShowGUI() {
      JFrame testPawnFrame = new JFrame("Test de la classe Pawn");
      JPanel testPawnPanel = new JPanel();
      testPawnPanel.setLayout(new GridLayout(3, 3,Pawn.padding*2,Pawn.padding*2));


      for (int i = 0; i < 9; i++) {
         testPawnPanel.add(new Pawn());
      }

      testPawnFrame.add(testPawnPanel);
      addQuit(testPawnFrame);

      testPawnFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      testPawnFrame.pack();
      testPawnFrame.setVisible(true);
   }

   public Pawn() {
      this.pawnState = true;
      this.radius = 50;
      this.row = -1;
      this.column = -1;
   }

   public Pawn(int row, int column) {
      this();
      this.row = row;
      this.column = column;
   }

   public void setPosition(int row, int column) {
      if (row >= 0 && column >= 0) {
         this.row = row;
         this.column = column;
      } else {
         throw new Error("La position donee est incorrecte.");
      }
   }

   public void paint(Graphics g) {
      super.paintComponent(g);

      Graphics2D g2 = (Graphics2D) g;

      Ellipse2D circle = new Ellipse2D.Double(padding, padding, radius * 2, radius * 2);

      if (pawnState) {
         g2.setColor(pileColor);
      } else {
         g2.setColor(faceColor);
      }

      g2.fill(circle);
      g2.setColor(Color.BLACK);
      g2.draw(circle);
      g2.drawRect(0, 0, 2 * (radius + padding), 2 * (radius + padding));

   }

   public Dimension getPreferredSize() {
      return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
   }

   public Dimension getMinimumSize() {
      return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
   }

   public void setBounds(int x, int y, int width, int height) {      
      radius = (int) Math.min(width, height) / 2;

      super.setBounds(x, y, width+(padding*2), height+(padding*2));

      repaint();
   }

   public static void addQuit(JFrame frame) {
      ActionListener actionQuit = new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent event) {
            System.exit(0);
         }
      };

      Box quitBox = new Box(BoxLayout.X_AXIS);
      frame.add(quitBox, BorderLayout.SOUTH);
      JButton quitButton = new JButton("Quit");
      quitButton.addActionListener(actionQuit);
      quitBox.add(Box.createHorizontalGlue());
      quitBox.add(quitButton);
   }

}

Edit : I done some improvement to the code, thanks you all. It's not perfect and it's not really optimized ,the resizing is pretty slow. (if you have idea for optimize please tell me !)

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import javax.swing.*;

public class Pawn extends JComponent {

  private static Color pileColor = Color.LIGHT_GRAY;
  private static Color faceColor = Color.DARK_GRAY;
  private static Color mouseOverColor = new Color(255, 255, 127);

  private String label;

  private boolean pawnState;

  private int radius;
  private double padding;

  private int row;
  private int column;

  public static void main(String[] args) {
     javax.swing.SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
           createAndShowGUI();
        }
     });
  }

  private static void createAndShowGUI() {
     JFrame testPawnFrame = new JFrame("Test de la classe Pawn");
     JPanel testPawnPanel = new JPanel();
     testPawnPanel.setLayout(new GridLayout(3, 3));

     for (int i = 0; i < 9; i++) {
        testPawnPanel.add(new Pawn());
     }

     testPawnFrame.add(testPawnPanel);
     addQuit(testPawnFrame);

     testPawnFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     testPawnFrame.pack();
     testPawnFrame.setVisible(true);
  }

  public Pawn() {
     this.pawnState = true;
     this.radius = 50;
     this.padding = 0.1;
     this.row = -1;
     this.column = -1;
  }

  public Pawn(int row, int column) {
     this();
     this.row = row;
     this.column = column;
  }

  public void setPosition(int row, int column) {
     if (row >= 0 && column >= 0) {
        this.row = row;
        this.column = column;
     } else {
        throw new Error("La position donee est incorrecte.");
     }
  }

  public void paint(Graphics g) {
     super.paint(g);
     Graphics2D g2 = (Graphics2D) g;

     Dimension size = getSize();

     radius = (int)(
             (Math.min(size.width, size.height) / 2)
             -(radius*padding));

     Ellipse2D circle = new Ellipse2D.Double(
             (size.width/2)-radius, 
             (size.height/2)-radius, 
             radius * 2, radius * 2);

     if (pawnState) {
        g2.setColor(pileColor);
     } else {
        g2.setColor(faceColor);
     }

     g2.fill(circle);
     g2.setColor(Color.BLACK);
     g2.draw(circle);
     g2.drawRect(0, 0, getWidth()-1, getHeight()-1);
  }

  public Dimension getPreferredSize() {
     int size = 2 * (radius);
     return new Dimension(size, size);
  }

  public static void addQuit(JFrame frame) {
     ActionListener actionQuit = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent event) {
           System.exit(0);
        }
     };

     Box quitBox = new Box(BoxLayout.X_AXIS);
     frame.add(quitBox, BorderLayout.SOUTH);
     JButton quitButton = new JButton("Quit");
     quitButton.addActionListener(actionQuit);
     quitBox.add(Box.createHorizontalGlue());
     quitBox.add(quitButton);
  }

}

That what i get now :

link


Upvotes: 2

Views: 865

Answers (1)

camickr
camickr

Reputation: 324118

//super.paint(g);
super.paintComponent(g);

As already mentioned you need to invoke super on the method your are overriding

Don't override setBounds(). It is the job of the layout manager to determine the size/location of a component. This is the main problem with your code.

Some other problems:

There is no need to override getMinimumSize(). If you did, you would simply use:

return getPreferredSize();

Don't duplicate code if you don't have to.

return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );

Don't repeat calculations. Create a variable. It is easier to debug and change.

//return new Dimension(2 * (radius + padding) , 2 * (radius + padding) );
int size = 2 * (radius + padding) + 1;
return new Dimension(size, size);

Note the "+1". I added this because the way the drawRect(...)method works. Try the code without the "+1" and you will see the bottom/right lines are missing. This is a perfect example of why you use variable to hold calculations. The change only needs to be done in one place.

Edit:

One more change:

//testPawnPanel.setLayout(new GridLayout(3, 3,Pawn.padding*2,Pawn.padding*2));
testPawnPanel.setLayout(new GridLayout(3, 3));

You don't want any space between your components, so let the layout manger do its job.

Upvotes: 3

Related Questions