Hey everyone I am trying to draw a checkerboard in Java with GUI. I have created a square class for the squares of the game board.
Square Class:
import javax.swing.*;
import java.awt.*;
public class Square extends JPanel {
private int width = 80;
private int height = 80;
private int x,y;
private Color color;
public Square(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics graphics){
//setSize(new Dimension(width,height));
graphics.setColor(color);
graphics.drawRect(x,y, width,height);
graphics.fillRect(x,y,width,height);
}
}
Basically I wanted to create a panel with grid layout of 8 by 8. Then add square objects grid layout panel. I want the first row to contain red,black,red,black,red,black,red,black squares and the second row to contain black,red,black,red,black,red,black,red squares.
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(600,600));
JPanel panel = new JPanel(new GridLayout(8,8));
panel.setLayout(new GridLayout(8, 8));
panel.setBackground(Color.green);
Square redsqr1 = new Square(0,0, Color.RED);
Square blksqr1 = new Square(0,0, Color.BLACK);
Square redsqr2 = new Square(0,0, Color.RED);
Square blksqr2 = new Square(0,0, Color.BLACK);
Square redsqr3 = new Square(0,0, Color.RED);
Square blksqr3 = new Square(0,0, Color.BLACK);
Square redsqr4 = new Square(0,0, Color.RED);
Square blksqr4 = new Square(0,0, Color.BLACK);
Square redsqr5 = new Square(0,0, Color.RED);
Square blksqr5 = new Square(0,0, Color.BLACK);
Square redsqr6 = new Square(0,0, Color.RED);
Square blksqr6 = new Square(0,0, Color.BLACK);
Square redsqr7 = new Square(0,0, Color.RED);
Square blksqr7 = new Square(0,0, Color.BLACK);
Square redsqr8 = new Square(0,0, Color.RED);
Square blksqr8 = new Square(0,0, Color.BLACK);
panel.add(redsqr1);
panel.add(blksqr1);
panel.add(redsqr2);
panel.add(blksqr2);
panel.add(redsqr3);
panel.add(blksqr3);
panel.add(redsqr4);
panel.add(blksqr4);
panel.add(blksqr5);
panel.add(redsqr5);
panel.add(blksqr6);
panel.add(redsqr6);
panel.add(blksqr7);
panel.add(redsqr7);
panel.add(blksqr8);
panel.add(redsqr8);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
When I run the program I get the output
Just curious as to why the output is placed in 2 columns with big space between each square. How would i get them to stay side byside on one row to have the first row to containing red,black,red,black,red,black,red,black squares and the second row to containing black,red,black,red,black,red,black,red squares.
Thanks in advance for your help!
It's because you didn't add all the 64 needed squares to the layout. So the layout cells would be stretched to fill all the space. As the result the output would be messy. Moreover, it's a good idea to set the horizontal and vertical gaps to 0. One more hint is by calling JFrame#add it will add the component to the contentPane and there is no need to get the contentPane to add something to a JFrame. In addition an intermediary JPanel is not needed between the underlying JFrame and the Squares.
Also I changed the main method a little to reduce the hardships of creating and adding the Squares to the output frame:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setPreferredSize(new Dimension(600,600));
frame.setLayout(new GridLayout(8, 8, 0, 0));
frame.getContentPane().setBackground(Color.green);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
frame.add(new Square(0, 0, (i+j)%2==0 ? Color.RED : Color.BLACK));
}
}
frame.pack();
frame.setVisible(true);
}
Hope this would help.
Related
I have a half-working paint/turtle graphics program but I am having a few issues.
I am going to need to save the image that is drawn at some point - I know I need to use a BufferedImage but how do I go about this? I have attached a little bit of my code below.
In short I have a JFrame with a text field and a white JPanel below it. When commands are typed in by the user lines are drawing through another class and appear on the JPanel. I know I won't be able to save them off the JPanel to an image but have no understanding of how to implement a buffered image and what i would/wouldn't need to change.
Please be kind, I am new to programming and haven't finished some parts of my code yet.
Thanks in advance
public class Turtle2 extends JFrame implements ActionListener, KeyListener{
JMenuBar menuBar;
JMenu help, file;
JPanel panel, panel2;
JMenuItem newO, load, save, exitO, about;
JTextField text;
int savecounter = 0, newcounter = 1, part2I = 0;
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
int trigger = 1, turnleftconstant = 0, turnrightconstant =0, direction =0;
int col = 0;
public Turtle2() {
setLayout(new FlowLayout());
setSize(1000, 1000);
setTitle("Graphic");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//menu bar
//sets up general look of window
panel = new JPanel();
panel.setBackground(Color.darkGray);
//implements box layout
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
//sets up drawing area
panel2 = new JPanel();
panel2.setPreferredSize(new Dimension(500, 500));;
panel2.setBackground(Color.WHITE);
panel2.setBorder(BorderFactory.createLineBorder(Color.darkGray, 3));
//starting point on drawing area co-ordinates
x1 = 15; x2 = 15; y1 = 230; y2 =230;
//starting point implemented
LinePen first = new LinePen(x1, y1, x2, y2, col);
panel2.add(first);
//sets up user input text box
text = new JTextField(30);
//add action listener to text box - ready for input and details responses
text.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//retrieves user input
String actions = text.getText();
first.setVisible(true);
//commands from text
//add the command line textfeild to the interface
panel.add(text);
//uses border layout to set the layout of the interface
getContentPane().add(BorderLayout.NORTH, panel);
getContentPane().add(BorderLayout.CENTER, panel2);
//set the size of the interface
setSize(600,600);
//ensure the interface is visible
setVisible(true);
}
I would keep an array of all your lines, then iterate through them, and then use img.createGraphics() to draw your lines and other graphics.
When using a GridLayout, it diveds its container's space evenly between its row and column elements. However when calcuating the cell width wouldn't turn out in an integer, the exceeding space is put between the container's edges and its content in a way, that the container's content is centered by the GridLayout.
In this picture you can see exceeding space (colored green):
Since the frame's size is dragged to 233x233 the LayoutManager would offer each component floor(233 / 20) = 11 pixels height and width. Thus 233 % 20 = 13 pixels exceed and are put at the edges.
That's the code to generate the frame in the picture:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridLayout layout = new GridLayout(20, 0);
layout.setHgap(0);
layout.setVgap(0);
frame.setLayout(layout);
for (int j = 0; j < 20; j++) {
for (int i = 0; i < 20; i++) {
JPanel panel = new JPanel();
panel.setBackground((i + j) % 2 == 0 ? Color.BLACK : Color.WHITE);
frame.add(panel);
}
}
frame.pack();
frame.getContentPane().setBackground(Color.GREEN);
frame.setVisible(true);
So I wonder if there's an easy way to make the container 'clip' or align in a way, that this exceeding space doesn't show up, but instead the container is resized to fit its content perfectly.
That's right but I want to keep the advantage of a GridLayout offering every cell the same size,
Yes well you can't have it both ways.
If you want every cell to be the same size, then you will see the background. One option would be to make the panel non-opaque, so you don't see the background of the panel.
If you want to completely fill the area available with the components, then some components will need to be a different size by one pixel.
For implementing point 2, maybe this example will be easy enough for you to use:
import java.awt.*;
import javax.swing.*;
public class BoardTest
{
private static void createAndShowGUI()
{
Float constraint = new Float(1);
RelativeLayout boardLayout = new RelativeLayout(RelativeLayout.Y_AXIS);
boardLayout.setRoundingPolicy( RelativeLayout.EQUAL );
boardLayout.setFill(true);
JPanel board = new JPanel(boardLayout);
board.setBackground(Color.GREEN);
RelativeLayout rowLayout = new RelativeLayout(RelativeLayout.X_AXIS);
rowLayout.setRoundingPolicy( RelativeLayout.EQUAL );
rowLayout.setFill(true);
for (int j = 0; j < 20; j++)
{
JPanel row = new JPanel( rowLayout );
for (int i = 0; i < 20; i++)
{
JPanel square = new JPanel();
square.setBackground((i + j) % 2 == 0 ? Color.BLACK : Color.WHITE);
row.add(square, constraint);
}
board.add(row, constraint);
}
JFrame frame = new JFrame("BoardTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(board);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
It uses the Relative Layout class which allows you to control how the extra pixels are allocated to each of the components.
I am working on a small example using Java Swing where I want to draw a sine graph on one panel and the co-ordinates of the graph in another panel. So I created a class that extends the JFrame then I created the JPanel for graph and co-ordinates. For displaying co-ordinates I am using JList. Now the problem is the co-ordinates are showing duplicate values. Here is my code:
public class MyFrame extends JFrame {
JList list;
DecimalFormat df = new DecimalFormat("#.###");
DefaultListModel model = new DefaultListModel();
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MyFrame frame = new MyFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MyFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 600, 600);
contentPane = new JPanel();
contentPane.setBorder(new LineBorder(new Color(0, 0, 0)));
setContentPane(contentPane);
contentPane.setLayout(new GridLayout(1, 2, 0, 0));
JPanel panel = new MyGraph();
panel.setBorder(new LineBorder(new Color(0, 0, 0)));
contentPane.add(panel);
list = new JList(model);
list.setVisibleRowCount(4);
JPanel panel_1 = new JPanel();
panel_1.setBorder(new LineBorder(new Color(0, 0, 0)));
panel_1.setLayout(new BoxLayout(panel_1, BoxLayout.Y_AXIS));
JLabel lblNewLabel_1 = new JLabel("X - Y");
panel_1.add(lblNewLabel_1);
JScrollPane slistScroller = new JScrollPane(list);
panel_1.add(slistScroller);
contentPane.add(panel_1);
}
class MyGraph extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xBase = 10;
int top = 100;
int yScale = 10;
int xAxis = 360;
int yBase = top + yScale;
g.drawLine(xBase, top, xBase, top + 2 * yScale);
g.drawLine(xBase, yBase, xBase + xAxis, yBase);
g.setColor(Color.red);
int x2=0, y2=0;
int x1 = xBase + 0;
int y1 = yBase - (int) (10*Math.sin(0) * yScale);
int i;
for (i = 0; i < 10; i++) {
x2 = xBase + i;
y2 = yBase - (int) (10*Math.sin(i) * yScale);
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
df = new DecimalFormat("#.###");
model.addElement(i +" -- " + df.format(10*Math.sin(i)));
}
model.addElement("------END----------");
}
}
}
Here is the output of my program:
As per my program, I have a for loop from angles 0 to 10 and I am adding the values to DefaultListModel model which is added to JList list.
Can someone please help me where I am doing mistake in this code?
Also even when I have this line list.setVisibleRowCount(4); , I was expecting only 4 records displayed to the user with a scroll-bar, but as per the output image it is not working like that.
paintComponent may be any number of times, for any number of reasons, try resizing the frame and see what happens.
Your paint method should focus on doing just that, painting.
You need to change the process so that the paintComponent becomes depend on the model, not the other way round.
Take a look at Painting in AWT and Swing for more details about painting in Swing.
You may also want to consider using a ListCellRender to render the data in the model in the JList, this way, you could more easily share the model and it's data.
See Writing a Custom Cell Renderer for more details
private static void initGUI(char[][] code){
int width = (int) (code[0].length * new JLabel().getFont().getSize2D());
int height = (int) (code.length * new JLabel().getFont().getSize2D()) + 200;
gridgui = new JLabel[code.length][code[0].length];
for(int x = 0; x < code.length; ++x){
for(int y = 0; y < code[0].length; ++y){
gridgui[x][y] = new JLabel();
gridgui[x][y].setText(""+code[x][y]);
gridgui[x][y].setBackground(Color.WHITE);
gridgui[x][y].setOpaque(true);
}
}
listModel = new DefaultListModel<Integer>();
listModel.addElement(5);
//Create the list and put it in a scroll pane.
stacklist = new JList<Integer>(listModel);
JScrollPane listScrollPane = new JScrollPane(stacklist);
listScrollPane.getViewport().setView(stacklist);
frame = new JFrame("Befunge 93! Wow!");
gridpanel = new JPanel();
gridpanel.setLayout(new GridLayout(gridgui.length, gridgui[0].length));
gridpanel.setPreferredSize(new Dimension(width, height));
for(int x = 0; x < gridgui.length; ++x){
for(int y = 0; y < gridgui[0].length; ++y){
gridpanel.add(gridgui[x][y]);
}
}
stackpanel = new JPanel();
stackpanel.setLayout(new GridLayout(100, height));
stackpanel.setPreferredSize(new Dimension(width, height));
stackpanel.add(listScrollPane);
JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
container.add(gridpanel);
container.add(stackpanel);
frame.add(container);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
I would have put less and more compact code, but I honestly have no clue where the problem could be.
The issue is that the items in the JList stacklist don't show up. It just looks like this:
(source: mediafire.com)
(with the arrow pointing to the JList)
I've been troubleshooting this for hours and I fear it's something painfully obvious that will cause my to quit programming forever.
I think you need to understand how GridLayout works...
stackpanel.setLayout(new GridLayout(100, height));
Basic says, create me a grid which has 100 rows and height number of columns...This is reserving space for each cell...
If I change it to...
stackpanel.setLayout(new GridLayout(code.length, 1));
I get...
But I'm only guess at what it is you're trying to achieve...
You should also avoid using setPreferredSize where you can
The problem is when I set the background color of the square JPanel as square.setBackground(colors[j]) the square draws only the first color of the list of colors without displaying the other 3. This is my code:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import java.awt.*;
#SuppressWarnings({ "unused", "serial" })
public class RegionPartition extends JFrame
{
JLayeredPane layeredPane;
JPanel regionBoard;
JLabel regionPiece;
private static int DELAY = 200;
private Color[] colors = new Color[]{Color.PINK, Color.GREEN, Color.BLACK, Color.RED};
public RegionPartition()
{
Dimension boardSize = new Dimension(500, 500);
// Use a Layered Pane for this this application
layeredPane = new JLayeredPane();
getContentPane().add(layeredPane);
layeredPane.setPreferredSize(boardSize);
regionBoard = new JPanel();
layeredPane.add(regionBoard, JLayeredPane.DEFAULT_LAYER);
regionBoard.setLayout( new GridLayout(4, 4) );
regionBoard.setPreferredSize( boardSize );
regionBoard.setBounds(0, 0, boardSize.width, boardSize.height);
Random random = new Random();
for (int i = 0; i < 16; i++) {
JPanel square = new JPanel(new BorderLayout());
square.setBorder(BorderFactory.createLineBorder(Color.black));
regionBoard.add( square );
square.setBackground(Color.green);
int j=0;
square.setBackground(colors[j]);
j++;
}
}
{
JPanel panel = new JPanel()
{
Clients[] c = new Clients[128];
Random random = new Random();
private final int SIZE = 450;
private int DELAY = 9999999;
public void paintComponent (Graphics g)
{
super.paintComponent(g);
for (int i=0; i<c.length; i++)
{
int x = ( int ) ( random.nextFloat() * SIZE ) + 10;
int y = ( int ) ( random.nextFloat() * SIZE ) + 10;
g.drawOval( x, y, 10, 10 );
g.fillOval(x, y, 10, 10);
}
for (int j=0; j<DELAY; j++)
{
repaint();
}
}
};
panel.setOpaque(false);
//Set the glass pane in the JFrame
setGlassPane(panel);
//Display the panel
panel.setVisible(true);
}
public static void main(String[] args)
{
JFrame frame = new RegionPartition();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE );
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
protected void paintComponent(Graphics g)
{
// TODO Auto-generated method stub
}
}
That is because you are always setting j to 0 on each iteration:
int j=0;
square.setBackground(colors[j]);
j++;
you may want to change j for an i or do a nested loop, that depends on what you really want to do here.
If you want to make all 16 squares have all four colors in a grid like manner, you might want to change your loop to something like:
for (int i = 0; i < 16; i++) {
JPanel square = new JPanel(new GridLayout(2,2));
square.setBorder(BorderFactory.createLineBorder(Color.black));
regionBoard.add( square );
for(int j=0; j<4; ++j){
JPanel insideSquare = new JPanel();
insideSquare.setBackground(colors[j]);
square.add(insideSquare);
}
}
Because you only have 4 colors in your color array, but your loop index exceeds this, you could use:
square.setBackground(colors[ i % colors.length]);
to alternate the colors of your squares.
You are instantiating int j within the scope of your for loop, so its value is not preserved across multiple iterations. You should declare it at a point in your code to allow it scope over your entire for loop.
int j = 0;
<for loop>
square.setBackground(colors[j]);
j++;
<end for>
However, your j is serving the purpose of i in this situation, where i is sufficient as an array index. It would be more correct to remove j entirely and instead do the following:
square.setBackground(colors[i]);