VPL start with java - java

I am trying to create a generic node that can modified and to ones wishes but I have run in some trouble because my skills in Swing and such is not so good.
Problem 1: I can't seem to get two Node/Jpanels in the same JFrame.
lesser Problem 2: The stroke is cut off from the JPanel border (Should I build in a kind of bleed so this will not happen?)
Any aid is helpful :)
public class SimpleGui {
public SimpleGui() {
JFrame frame = new JFrame();
NodePanel panel = new NodePanel(3, 2);
panel.setLayout(new javax.swing.SpringLayout());
frame.add(panel);
NodePanel panel2 = new NodePanel(3, 2);
panel2.setLayout(new javax.swing.SpringLayout());
frame.add(panel2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setResizable(true);
frame.setVisible(true);
panel.setLocation(50, 50);
panel2.setLocation(150, 150);
}
}
The generic node/panel class that is added to the JFrame:
public class NodePanel extends JPanel {
private int x = 0, int y = 0;
private PortPanel inPortPanel;
private PortPanel outPortPanel;
int iH;
int oH;
private int width = 120;
private int height = 30;
Graphics2D g2;
public NodePanel(int input, int output) {
inPortPanel = new PortPanel(input);
this.add(inPortPanel);
outPortPanel = new PortPanel(output);
this.add(outPortPanel);
setPreferredSize(calculateDimension());
}
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.black);
g2.setStroke(new BasicStroke(4));
inPortPanel.setLocation(x, y + (height - iH) / 2);
outPortPanel.setLocation(x + width, y + (height - oH) / 2);
drawNode();
}
private void drawNode() {
g2.drawRoundRect(x, y, width, height, 5, 5);
}
private Dimension calculateDimension() {
iH = inPortPanel.getPreferredSize().height;
int iW = inPortPanel.getPreferredSize().width;
oH = outPortPanel.getPreferredSize().height;
height = iH > oH ? iH + iW : oH + iW;
return new Dimension(width, height);
}
}
The port panel that is part of the Node Panel
public class PortPanel extends JPanel {
int x = 0;
int y = 0;
int portWidth = 14;
int portHeight = 14;
int count = 0;
int offset = 22;
Graphics2D g2;
public PortPanel(int i) {
this.count = i;
setPreferredSize(calculateDimensions());
}
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
g2.setColor(Color.black);
drawPorts();
}
private void drawPorts() {
for (int i = 0; i < count; i++) {
g2.fillOval(x, y + (i * offset), portWidth, portHeight);
}
}
private Dimension calculateDimensions(){
int overallHeight = (offset * (count-1)) + portHeight;
return new Dimension(portWidth,overallHeight);
}
}

Node should be a logical class, not a GUI component class.
You should have a single drawing JPanel that can draw all the visual representation of your logical entities.
This way the model can hold multiple logical entities that all can be drawn by the single drawing JPanel without having to worry as much about layout managers.
Don't forget to call your JPanel's super.paintComponent method within its override method so your JPanel can do house-keeping painting.
Avoid giving your JPanels Graphics or Graphics2D fields as that increases the risk of your code throwing a NPE. Use the Graphics object given to your paintComponent method, and if you need to use it in another method that is called by paintComponent, pass it into that method.

Related

How to make a grid of buttons

I'm learning Java these days, my first project is to create a "Go board", 9 * 9 rows and columns, and place black and white stones on the intersections.
I created a board with 9 * 9 lines and columns, now I have to create black and white stones using the JButton component.
Other than the color, size, and position of the button on the first row (setLayout), I was unable to turn the button into a circle and place the stone on the intersection points.
From multiple searches for related guides, I have noticed that there is some unique structure that I am not familiar with for creating and designing buttons.
And now my question comes in - what is the code structure I need to create in order to produce a button in the shape of a circle, size 65 * 65, in black or white? Do I need to create a new class for this? How and where should I integrate JPanel?
public class Main {
public static void main(String[] args) {
Board board = new Board(900, 900, "Go board");
}
}
import java.awt.*;
import javax.swing.*;
public class Board extends JPanel {
private int width;
private int height;
private String title;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Board(int width, int height, String title) {
super();
this.width = width;
this.height = height;
this.title = title;
this.initBoard();
}
public Board() {
super();
}
public void initBoard() {
JFrame f = new JFrame(this.getTitle());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// f.getContentPane().setBackground(Color.getHSBColor(25, 75, 47));
f.setSize(this.getWidth(), this.getHeight());
// f.setLocation(550, 25);
f.add(this, BorderLayout.CENTER);
f.setVisible(true);
JButton stone = new JButton(" ");
f.add(stone);
f.setLayout(new FlowLayout());
stone.setBackground(Color.BLACK.darker());
stone.setBorder(BorderFactory.createDashedBorder(getForeground()));
stone.setPreferredSize(new Dimension(65, 65));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < 10; i++) {
g.drawLine(0, 10 + (i * ((this.getWidth() - 20) / 9)), this.getWidth(),
10 + (i * ((this.getWidth() - 20) / 9)));
g.drawLine(10 + (i * ((this.getHeight() - 20) / 9)), 0, 10 + (i * ((this.getHeight() - 20) / 9)),
this.getHeight());
}
}
}
Before uploading the post, I read the following posts:
Design Button in Java (like in CSS)
How can I set size of a button?
Java: JButton with custom Shape: Fill with Metal Look and Feel Gradient
How to Use Borders
Java JButton
How to use setUI method in javax.swing.JButton
Note: I do not want to access posts that explain how to produce a "Go board", the learning process in this context is my goal.
Use a JPanel with a 9x9 GridLayout and ad to it JButtons configured to your need as demonstrated in the following very basic mre:
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
public class GridOfButtons extends JPanel {
private static final int ROWS = 9, COLS = 9, SIZE = 65, BORDER = 2;
private static final Color BOARD_COLOR = Color.BLACK;
public GridOfButtons() {
setLayout(new GridLayout(ROWS, COLS, BORDER, BORDER));
setBackground(BOARD_COLOR);
StonesFactory factory = new StonesFactory(SIZE);
boolean isBlack = false;
for (int col = 0; col < COLS; col++) {
for (int row = 0; row < ROWS; row++) {
add(factory.makeButton(isBlack));
isBlack = !isBlack;
}
}
this.initBoard();
}
public void initBoard() {
JFrame f = new JFrame("Board Of Buttons");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridBagLayout());
f.add(this);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new GridOfButtons());
}
}
class StonesFactory{
private static final Color STONE = Color.YELLOW, WHITE_STONE = Color.WHITE, BLACK_STONE = Color.BLACK;
private final int size;
private final ImageIcon whiteIcon, blackIcon;
public StonesFactory(int size) {
this.size = size;
whiteIcon = new ImageIcon(createImage(false));
blackIcon = new ImageIcon(createImage(true));
}
JButton makeButton(boolean isBlack){
JButton stone = new JButton();
stone.setPreferredSize(new Dimension(size, size));
stone.setBackground(STONE);
stone.setIcon(isBlack ? blackIcon : whiteIcon);
return stone;
}
//construct image for button's icon
private BufferedImage createImage(boolean isBlack) {
BufferedImage img = new BufferedImage(size , size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(isBlack ? BLACK_STONE : WHITE_STONE);
g2.fillOval(0,0,size,size);
g2.dispose();
return img;
}
}
(Run it online)
Alternatively you can produce the board by custom painting of a JPanel. This will make the individual "stones" not clickable and more difficult to modify:
import java.awt.*;
import javax.swing.*;
public class GridByPainting extends JPanel {
private static final int ROWS = 9, COLS = 9, SIZE = 65, BORDER = 2;
private static final Color BOARD_COLOR = Color.BLACK, STONE = Color.YELLOW,
WHITE_STONE = Color.WHITE, BLACK_STONE = Color.BLACK;
private final Dimension size;
public GridByPainting() {
int x = BORDER + COLS*(SIZE + BORDER);
int y = BORDER + ROWS*(SIZE + BORDER);
size = new Dimension(x,y);
this.initBoard();
}
#Override
public Dimension getPreferredSize() {
return size;
}
public void initBoard() {
JFrame f = new JFrame("Grid By Painting");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridBagLayout());
f.add(this);
f.pack();
f.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth(); int height = getHeight();
int stoneWidth = (width - BORDER) / COLS - BORDER;
int stoneHeight = (height -BORDER)/ ROWS - BORDER ;
//draw board
g.setColor(BOARD_COLOR);
g.fillRect(0, 0, width, height);
boolean isBlack = true;
//draw square stones
for (int col = 0; col < COLS; col++) {
for (int row = 0; row < ROWS; row++) {
int x = BORDER + col*(stoneWidth + BORDER);
int y = BORDER + row*(stoneHeight + BORDER);
g.setColor(STONE);
g.fillRect(x, y, stoneWidth, stoneHeight);
//draw circle
g.setColor(isBlack ? BLACK_STONE : WHITE_STONE);
isBlack = !isBlack;
g.fillOval(x, y, stoneWidth, stoneHeight);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new GridByPainting());
}
}
(Run it online)
It seems like you skipped over some of the important parts of the Oracle tutorial, Creating a GUI With Swing.
Here's my comment from August 23rd, just 10 days ago.
Generally, you create a logical model of a Go board using a plain Java
getter / setter class. You use a drawing JPanel to create the Go board
in the GUI and draw circles to represent the stones. The Oracle
tutorial, Creating a GUI With Swing, will show you the steps to
creating a Swing GUI. Skip the Netbeans section.
So, where's your logical model? Where's your drawing JPanel?
Here's a quick GUI I created.
My code has a logical model. My code has a drawing JPanel.
The first thing I did was create a plain Java getter / setter class to hold a logical representation of a Go Board. I named this the Board class.
The next thing I did was start my Swing GUI with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I used the run method of my Runnable class to create the JFrame. The JFrame methods must be called in a specific order. This is the order I use for all my Swing applications.
I separate the creation of the JFrame from the creation of any subsequent JPanels. I do this to keep my code organized, easy to read, and easy to understand.
I extend a JPanel to create the drawing JPanel. I do this so I can override the paintComponent method of the JPanel class. The drawing JPanel draws (paints) the board state. That's all. Nothing else. Another class will take care of adding pieces to the logical Go board and repainting the drawing JPanel.
The MoveListener class implements a MouseListener (extends a MouseAdapter) to respond to mouse clicks on the Go board. The MoveListener class keeps track of whose turn it is. In a more elaborate version of a Go board, you would have another plain Java getter / setter class to keep track of the game state.
Here's the complete runnable code. I made all the classes inner classes so I could post this code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GoBoard implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new GoBoard());
}
private Board board;
private DrawingPanel drawingPanel;
public GoBoard() {
this.board = new Board();
}
#Override
public void run() {
JFrame frame = new JFrame("Go Board");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.drawingPanel = new DrawingPanel(board);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int margin, pieceRadius, lineSpacing;
private Board board;
public DrawingPanel(Board board) {
this.board = board;
this.margin = 60;
this.pieceRadius = 40;
this.lineSpacing = 100;
this.setBackground(new Color(0x993300));
int width = 8 * lineSpacing + margin + margin;
this.setPreferredSize(new Dimension(width, width));
this.addMouseListener(new MoveListener(board));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
paintHorizontalLines(g2d);
paintVerticalLines(g2d);
paintPieces(g2d);
}
private void paintHorizontalLines(Graphics2D g2d) {
int x = margin;
int y1 = margin;
int y2 = getHeight() - margin;
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(3f));
for (int index = 0; index < 9; index++) {
g2d.drawLine(x, y1, x, y2);
x += lineSpacing;
}
}
private void paintVerticalLines(Graphics2D g2d) {
int x1 = margin;
int x2 = getWidth() - margin;
int y = margin;
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(3f));
for (int index = 0; index < 9; index++) {
g2d.drawLine(x1, y, x2, y);
y += lineSpacing;
}
}
private void paintPieces(Graphics2D g2d) {
int[][] b = board.getBoard();
for (int row = 0; row < b.length; row++) {
for (int column = 0; column < b[row].length; column++) {
int x = column * lineSpacing + margin;
int y = row * lineSpacing + margin;
if (b[row][column] == 1) {
g2d.setColor(Color.BLACK);
g2d.fillOval(x - pieceRadius, y - pieceRadius,
pieceRadius + pieceRadius, pieceRadius + pieceRadius);
} else if (b[row][column] == 2) {
g2d.setColor(Color.WHITE);
g2d.fillOval(x - pieceRadius, y - pieceRadius,
pieceRadius + pieceRadius, pieceRadius + pieceRadius);
}
}
}
}
}
public class MoveListener extends MouseAdapter {
private boolean isBlackTurn = true;
private Board board;
public MoveListener(Board board) {
this.board = board;
}
#Override
public void mouseReleased(MouseEvent event) {
Point point = event.getPoint();
int margin = 60;
int pieceRadius = 40;
int lineSpacing = 100;
int column = (point.x - margin + pieceRadius) / lineSpacing;
int row = (point.y - margin + pieceRadius) / lineSpacing;
int piece = (isBlackTurn) ? 1 : 2;
board.setPiece(piece, row, column);
drawingPanel.repaint();
isBlackTurn = !isBlackTurn;
}
}
public class Board {
private int[][] board;
public Board() {
this.board = new int[9][9];
}
/**
* <p>
* This method inserts a piece on the board.
* </p>
*
* #param piece - 1 for black, 2 for white
* #param row - row of piece
* #param column - column of piece
*/
public void setPiece(int piece, int row, int column) {
this.board[row][column] = piece;
}
public int[][] getBoard() {
return board;
}
}
}

how to move multiple objects in JPanel

I want to create multiple squares that move independently and at the same time,and I think the most efficient way is through the transform method in Graphics2D,but I'm not sure how to make it work for each square.I want the square object to be self contained and create its own transforms(instance transforms). Here's what I have so far.
TransformPanel
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class TranformPanel extends JPanel {
private int[] xcoords = {250,248,253,255,249};
private int[] ycoords = {250,253,249,245,250};
private double randomx = 0;
private double randomy = 0;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawTransform(g,randomx,randomy);
}
private void drawTransform(Graphics g,double randomx,double randomy)
{
Random rn = new Random();
int xnum = rn.nextInt(10)-5;
randomx = xnum;
int ynum = rn.nextInt(10)-5;
randomy = ynum;
Rectangle rect = new Rectangle(250,250,10,10);
AffineTransform transform = new AffineTransform();
Graphics2D g2d = (Graphics2D)g;
transform.translate(randomx,randomy);
g2d.draw(transform.createTransformedShape(rect));
}
}
TransformDraw
import java.awt.*;
import javax.swing.*;
import java.awt.geom.AffineTransform;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
public class TransformDraw{
private static TranformPanel panel = new TranformPanel();
public static void main(String[] args) {
// Setup our JFrame details
JFrame frame = new JFrame();
frame.setTitle("Transform Polygon Example");
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setVisible(true);
frame.add(panel);
Scanner input = new Scanner(System.in);
for (int i=0;i<10;i++)
{
try {
TimeUnit.SECONDS.sleep(1);
frame.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thanks is Advance!
Start by creating something that can manage it's location (and other properties) and which can be "painted"
public interface Box {
public void update(Dimension size);
public void paint(Graphics2D g2d);
}
So, this is pretty basic, all it can do is be updated (within a given area) and be painted. You could expose other properties (like it's bounding box for example) based on your particular needs
Next, we need a simple implementation, something like...
public class DefaultBox implements Box {
private Color color;
private Rectangle bounds;
private int xDelta;
private int yDelta;
public DefaultBox(Color color, Dimension size) {
this.color = color;
bounds = new Rectangle(new Point(0, 0), size);
xDelta = 1 + (int) (Math.random() * 10);
yDelta = 1 + (int) (Math.random() * 10);
}
#Override
public void update(Dimension size) {
bounds.x += xDelta;
bounds.y += yDelta;
if (bounds.x < 0) {
bounds.x = 0;
xDelta *= -1;
} else if (bounds.x + bounds.width > size.width) {
bounds.x = size.width - bounds.width;
xDelta *= -1;
}
if (bounds.y < 0) {
bounds.y = 0;
yDelta *= -1;
} else if (bounds.y + bounds.height > size.height) {
bounds.y = size.height - bounds.height;
yDelta *= -1;
}
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fill(bounds);
}
}
Now, this maintains a simple Rectangle instance, which describes the location and size of the object, it also maintains properties about the color and it's speed.
When update is called, it updates it's location and does some simple bounds checking to make sure that the box remains within the specified area.
When paint is called, it simply paints itself.
Finally, we need some way to update and paint these boxes....
public class TestPane extends JPanel {
private List<Box> boxes;
private Color[] colors = {Color.RED, Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.WHITE, Color.YELLOW};
public TestPane() {
boxes = new ArrayList<>(25);
for (int index = 0; index < 100; index++) {
Color color = colors[(int) (Math.random() * colors.length)];
int width = 10 + (int) (Math.random() * 9);
int height = 10 + (int) (Math.random() * 9);
boxes.add(new DefaultBox(color, new Dimension(width, height)));
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Box box : boxes) {
box.update(getSize());
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Box box : boxes) {
Graphics2D g2d = (Graphics2D) g.create();
box.paint(g2d);
g2d.dispose();
}
}
}
Okay, so again, this is pretty simple. It maintains a List of Box's, a Swing Timer to periodically update the List of Box's, calling their update method. The Timer the simply calls repaint, which (in a round about way) ends up calling paintComponent, which then just calls paint on each instance of Box.
100 boxes...

Java: Drawing using Graphics outside the class which draws the main canvas

I'm trying to develop a Tic Tac Toe game in Java using Graphics.
My problem is: I don't want to add any other methods inside my Grid.class (which draws the lines 3x3), but i want to draw my X or O from a class called Game. My grid class looks like the following:
import java.awt.Graphics;
import javax.swing.JPanel;
public class Grid extends JPanel{
private final int ITEM_WIDTH = 30;
private final int ITEM_HEIGHT = 30;
private final int OUTER_WIDTH = 90;
private final int OUTER_HEIGHT = 90;
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOuter(g);
drawGrid(g);
}
public void drawOuter(Graphics g){
g.drawRect(0, 0, OUTER_WIDTH, OUTER_HEIGHT);
}
public void drawGrid(Graphics g){
//Vertikális
for(int i = ITEM_WIDTH; i < OUTER_WIDTH; i += ITEM_WIDTH){
g.drawLine(i, 0, i, OUTER_HEIGHT);
}
//Horizontális
for(int i = ITEM_HEIGHT; i < OUTER_HEIGHT; i += ITEM_HEIGHT){
g.drawLine(0, i, OUTER_WIDTH, i);
}
}
Thank you for your helps
My suggestion would be try to keep things as simple as possible.
Create ImageIcons that hold your X and O images
Have a GridLayout of JLabels placed in your JPanel above.
Swapping the JLabel's ImageIcon when I wanted to display an X, an O, or a blank.
Proof of concept code. Clicking on the JLabels several times will show the icons. The code has no Tic-Tac-Toe logic though:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class XorOorBlank extends JPanel {
private static final int IMG_WIDTH = 100;
private static final Color BACKGROUND = Color.LIGHT_GRAY;
private static final Color X_COLOR = Color.red;
private static final Color O_COLOR = Color.blue;
private static final Stroke X_STROKE = new BasicStroke(8f);
private static final int GAP = 8;
private static final int SIDE = 3;
private Icon blankIcon = createBlankIcon();
private Icon xIcon = createXIcon();
private Icon oIcon = createOIcon();
public XorOorBlank() {
setBackground(BACKGROUND);
setLayout(new GridLayout(SIDE, SIDE, GAP, GAP));
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Icon icon = label.getIcon();
if (icon == blankIcon) {
icon = xIcon;
} else if (icon == xIcon) {
icon = oIcon;
} else if (icon == oIcon) {
icon = blankIcon;
}
label.setIcon(icon);
}
};
for (int i = 0; i < SIDE; i++) {
for (int j = 0; j < SIDE; j++) {
JLabel label = new JLabel(blankIcon);
label.addMouseListener(mouseListener);
add(label);
}
}
}
private Icon createBlankIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private Icon createXIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.setColor(X_COLOR);
g2.setStroke(X_STROKE);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x1 = GAP;
int y1 = x1;
int x2 = IMG_WIDTH - GAP;
int y2 = x2;
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(x2, y1, x1, y2);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private Icon createOIcon() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
g2.setColor(O_COLOR);
g2.setStroke(X_STROKE);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int x1 = GAP;
int y1 = x1;
int x2 = IMG_WIDTH - 2 * GAP;
int y2 = x2;
g2.drawOval(x1, y1, x2, y2);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
return icon;
}
private static void createAndShowGui() {
JFrame frame = new JFrame("X or O");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new XorOorBlank());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note: not a paintComponent method to be found.
This displays:
You have a few choices...
You Could
Extend from Grid, overriding it's paintComponent method and draw the X/Os within it (calling super.paintComponent)
You Could
Create a interface which provide a simple "draw" method, which when implemented, would paint either X/O. You could then simply add an new instance of this to the Grid class and have it paint the X/O via some kind of loop within the paintComponent method
You Could
Use, something like GridBagLayout or even OverlayLayout and place another panel over the top of the Grid class, or, using BorderLayout, add the panel directly to the Grid class, which would then be responsible for painting the X/O
You Could
Do what Hovercraft Full Of Eels has suggested...

Drawing a pendulum's rotation over time in a JPanel

I am trying to describe in a JPanel the evolution of a pendulum arm over time.
The pendulum has a fix node and the other node is calculated based on the fixed one and some angles fetched from a file. And every 1 second I expect to see the pendulum redrawn with new coordinates.
For the purpose of describing my issue, I have eliminated the file and the angle calculations and please consider that the mobile Point is saved into an ArrayList of Points.
I tried to achieve the gradual rotation over time by calling the drawRotatingLine() method from within the constructor of the RotateLine object.
In the drawRotatingLine() method I have a for loop which:
sets the coordinates of the mobile Point based on the values of ArrayList of Points
introduces a sleep of 1 second
and calls the repaint() method
Trouble is that I only had my program draw the initial position and then the last one, the intermediary ones not getting painted.
The code is quite patchy having put it together from here and there.Please excuse me for having used abusively the BufferedImage, Graphics2D, and the calls to these objects in the paintComponent(...) method not being entirely clear to me, I just needed the program done and at this stage of my experience, I find quite intricate drawing on JPanels.
Below is the whole code:
public class RotateLine extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 600;
private static final int X1 = 100;
private static final int Y1 = 100;
private BufferedImage image;
private Graphics2D bufferedGraphics;
private static ArrayList<Point> pointsList;
private static Point p;
private int counter = 0;
public RotateLine () {
pointsList = new ArrayList<Point>();
p = new Point(X1, Y1);
int X2 = 400;
int Y2 = Y1;
for (int count = 0; count < 4; count++) {
pointsList.add(new Point(X2, Y2));
X2 = X2 - 100;
Y2 = Y2 + 100;
}
image = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_RGB);
bufferedGraphics = image.createGraphics();
drawRotatingLine();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
bufferedGraphics.clearRect(0, 0, PREF_W, PREF_H);
bufferedGraphics.setColor(Color.WHITE);
bufferedGraphics.fillRect(0, 0, PREF_W, PREF_H);
bufferedGraphics.setColor(Color.BLACK);
bufferedGraphics.drawLine(X1, Y1, p.x, p.y);
g.drawImage(image, 0, 0, this);
Toolkit.getDefaultToolkit().sync();
}
public static void main(String[] args) {
JFrame frame = new JFrame("clock");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotateLine());
frame.pack();
frame.setVisible(true);
}
public void drawRotatingLine() {
for (int i = 0; i < pointsList.size(); i++) {
p.x = pointsList.get(i).x;
p.y = pointsList.get(i).y;
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(Pendul.class.getName()).log(Level.SEVERE, null, ex);
}
repaint();
}
}
}
Your problem is common: you are calling Thread.sleep(...) on the Swing event thread, which will put your entire application to sleep. Instead read up and use a Swing Timer. After you Google the Swing Timer tutorial, search this site for Java Swing Timer Animation for decent examples of how to use it for animation.
So,
have your timer's delay be whatever time slice delay you wish the animation to have, although I recommend it not be < 12 msecs.
In the Timer's ActionListener's actionPerformed, set the coordinates of the mobile Point based on the values of ArrayList of Points and an index
Increment the index (very important)
mod the index to maximal value
call repaint
Based on Hovercraft Full of Eels's answer, this is how I altered the initial code using the Java Swing Timer Animation:
public class RotateLine extends JPanel **implements ActionListener**{
private static final int PREF_W = 800;
private static final int PREF_H = 800;
private static final int X1 = 100;
private static final int Y1 = 100;
private static ArrayList<Point> pointsList;
private static Point p;
private int counter = 0;
private int index = 0;
**Timer time = new Timer(10, (ActionListener) this);**
public RotateLine () {
pointsList = new ArrayList<Point>();
p = new Point(X1, Y1);
int X2 = 400;
int Y2 = Y1;
for (int count = 0; count < 300; count++) {
pointsList.add(new Point(X2, Y2));
X2 = X2 - 1;
Y2 = Y2 + 2;
}
**time.start();**
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
drawRotatingLine(g2d);
}
public static void main(String[] args) {
JFrame frame = new JFrame("clock");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotateLine());
frame.pack();
frame.setVisible(true);
}
public void drawRotatingLine(Graphics2D g) {
g.drawLine(p.x, p.y, pointsList.get(index).x, pointsList.get(index).y);
}
**public void actionPerformed(ActionEvent arg0) {
if (index < pointsList.size() - 1){
time.setDelay(20);
repaint();
index++;
}
}**

Repainting/Revalidating JPanel wont work

I am unclear on how to repaint a JPanel in my existing code. I also dont understand how paintComponent is being called. Firstly, I would simply like to clear the JPanel but I cannot even do that.
I want to be able to press the "Next Move" button and repaint the JPanel given a change in the grid I am reading colours from. I am having trouble changing the JPanel at all via the calls, revalidate(), removeAll(), and repaint() to my tiles JPanel.
I call these and nothing happens to the existing JPanel below of a tetris grid (tiles) (pictured)..
In the below code i have a nested class that extends JPanel and the setup function creates the window taking in a 2d arraylist grid and constructing the original grid.
How do I at least clear the JPanel or even better redraw with a new grid... I was thinking a method:
public void redraw(Grid g) {
removeAll();
getColor(); //gets new grid of tiles
repaint();
revalidate();
}
Inside the nested JPanel class, but this doesnt seem to change the JPanel at all not even clear the current drawing.
public class BoardGraphics {
public ArrayList<ArrayList<Color>> colours;
private final int width;
private int height;
private JFrame display;
private TetrisSquares tiles;
private Container c;
public BoardGraphics(int width, int height,
ArrayList<ArrayList<Integer>> grid) {
this.width = width;
this.height = height;
colours = new ArrayList<ArrayList<Color>>(width);
setColor(grid);
setup();
}
public void setup() {
System.out.println("Let the Tetris Begin");
display = new JFrame("Tetris Agent");
final TextArea welcome = new TextArea("Welcome to Tetris", 2, 10,
TextArea.SCROLLBARS_NONE);
welcome.setBackground(Color.black);
welcome.setForeground(Color.red);
welcome.setFont(new Font("monospaced", 0, 11));
welcome.setEditable(false);
Button btnStart = new Button("Next Move");
btnStart.setFocusable(false);
tiles = new TetrisSquares(TetrisBoard.BOARD_WIDTH, height);
c = new Container();
c.setLayout(new BorderLayout());
c.add(welcome, BorderLayout.NORTH);
c.add(tiles);
c.add(btnStart,BorderLayout.SOUTH);
btnStart.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
tiles.removeAll();
tiles.revalidate();
}
});
final Container main = new Container();
main.setLayout(new GridLayout(1, 2));
display.add(c);
display.pack();
display.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
display.setVisible(true);
}
public ArrayList<ArrayList<Color>> getBoard() {
return colours;
}
public void setColor(ArrayList<ArrayList<Integer>> grid) {
ListIterator<Integer> li;
for (int i = 0; i < width; i++) {
colours.add(new ArrayList<Color>());
li = grid.get(i).listIterator();
for (int j = 0; j <= height; j++) {
int n = 0;
if(li.hasNext()) {
n = li.next();
}
switch (n) {
case 1:
colours.get(i).add(Color.red);
break;
case 2:
colours.get(i).add(Color.pink);
break;
case 3:
colours.get(i).add(Color.orange);
break;
case 4:
colours.get(i).add(Color.yellow);
break;
case 5:
colours.get(i).add(Color.green);
break;
case 6:
colours.get(i).add(Color.cyan);
break;
case 7:
colours.get(i).add(Color.blue);
break;
default:
colours.get(i).add(Color.gray);
break;
}
}
}
}
public class TetrisSquares extends JPanel {
public int width;
public int height;
public TetrisSquares(int width, int height) {
this.width = width;
this.height = width;
this.setPreferredSize(new Dimension(width * 20, height * 20));
}
public void redraw() {
removeAll();
//add your elements
//revalidate();
//repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g;
graphics.setColor(Color.BLACK);
/*
* tiles.width = getSize().width / width; tiles.height =
* getSize().height / 800;
*
* insets.left = (getSize().width - width * tiles.width) / 2;
* insets.right = insets.left; insets.top = 0; insets.bottom =
* getSize().height - height * tiles.height;
*/
Dimension size = getSize();
Insets insets = getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
int tileWidth = w / width;
int tileHeight = w / width;
ListIterator<Color> li;
for (int i = 0; i < width; i++) {
li = colours.get(i).listIterator();
int n = 20;
while(li.hasNext()) {
--n;
int x = (int) (i * tileWidth);
int y = (int) (n * tileHeight);
graphics.setColor(li.next());
graphics.fillRect(x, y, tileWidth, tileHeight);
graphics.setColor(Color.black);
graphics.drawRect(x, y, tileWidth, tileHeight);
}
}
}
}
}
Start by having a read through Painting in AWT and Swing to gain a better understanding of the painting system.
removeAll removes all the child components from the container, this isn't really going to help you in this situation...
revalidate deals with the layout subsystem and updating the container hierarchy in response to changes in the container contents, again, not really going to help...
In you paintComponent method, you are referencing a ArrayList called colours, which is used paint the squares. You need to reset this list to stop the paintComponent method from filling in the colors
Your redaw method should look more something like...
public void redraw() {
colours.clear();
repaint();
}

Categories