JLabel components stay on screen - java

I'm working on a really basic bar chart which has to display 6 values. The problem I'm running into is that when i put the bars on the screen once they stay on the screen, and i cannot get them off. I've tried using the remove, repaint and revalidate functions but these all do not work.
What do I have to do to remove the bars so they don't clog up?
My code:
import javax.swing.*;
import java.awt.*;
import java.util.Collections;
public class BarChart extends JPanel
{
private JLabel[] bars;
public BarChart(int[] data)
{
update(data);
}
public void update(int[] data)
{
this.setSize(190, 155);
this.setLayout(null);
int max = 0;
for (int i = 0; i < 6; i++) {if (data[i] > max) {max = data[i];}}
bars = new JLabel[6];
for (int i = 0; i < 6; i++)
{
bars[i] = new JLabel();
bars[i].setOpaque(true);
bars[i].setBackground(Color.RED);
int height = (max != 0) ? (data[i]*155)/max : 0;
System.out.printf("%d, %d, %d,... ", height, data[i], max);
this.add(bars[i]);
bars[i].setSize(25, height);
bars[i].setLocation((31*i)+5, 155-height);
}
System.out.println("");
}
}

For your current code, you would need to call removeAll(), then revalidate() and repaint() on the JPanel would "solve" your problem, but you have other unrelated problems:
You're setting a component's size when you should be producing a preferred size -- this is what layout managers generally work with
You're using null layouts, a very dangerous thing
You're using a "magic" number as a for loop ending condition -- a VERY dangerous thing to do. How do you know that the data array has 6 and only items within it. What harm is there in simply using the data array's length as you've likely done hundreds of times before?
Instead, consider using more flexible code that will adapt to any size of data you give it and that avoids null layouts. For example consider the following code that draws the bars within the JPanel's paintComponent method:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TestBarChart extends JPanel {
private static final int[] INIT_DATA = { 1, 2, 4, 5, 6, 9 };
protected static final int MIN_DATA_lENGTH = 5;
protected static final int MAX_DATA_LENGTH = 9;
private static final int MAX_VALUE = 9;
private static final int PREF_W = 300;
private static final int PREF_H = 240;
private BarChart2 barChart2 = new BarChart2(INIT_DATA, MAX_VALUE, PREF_W, PREF_H);
public TestBarChart() {
barChart2.setBorder(BorderFactory.createLineBorder(Color.BLUE));
JPanel chartsPanel = new JPanel(new GridLayout(1, 0));
chartsPanel.setLayout(new GridLayout(1, 0));
chartsPanel.add(barChart2);
JButton resetDataBtn = new JButton(new AbstractAction("Reset Data") {
#Override
public void actionPerformed(ActionEvent e) {
int dataLength = (int) ((MAX_DATA_LENGTH - MIN_DATA_lENGTH) * Math.random()) + MIN_DATA_lENGTH;
int[] data = new int[dataLength];
for (int i = 0; i < data.length; i++) {
data[i] = (int) (MAX_VALUE * Math.random()) + 1;
}
barChart2.setData(data, MAX_VALUE);
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(resetDataBtn);
setLayout(new BorderLayout());
add(chartsPanel);
add(btnPanel, BorderLayout.PAGE_END);
}
private static void createAndShowGui() {
TestBarChart mainPanel = new TestBarChart();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class BarChart2 extends JPanel {
private static final double BAR_WIDTH = 0.90;
private int prefW;
private int prefH;
private static final Color BAR_COLOR = Color.RED;
private int[] data;
private int maxValue;
public BarChart2(int[] data, int maxValue, int prefW, int prefH) {
setData(data, maxValue);
this.prefW = prefW;
this.prefH = prefH;
}
public final void setData(int[] data, int maxValue) {
this.data = data;
this.maxValue = maxValue;
repaint();
}
public int[] getData() {
return data;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(BAR_COLOR);
// simple algebraic calculations on where to place the bars
double denom = data.length + 1 - BAR_WIDTH;
int barWidth = (int) ((getWidth() * BAR_WIDTH) / denom);
for (int i = 0; i < data.length; i++) {
int x = (int) (getWidth() * (i + 1 - BAR_WIDTH) / denom);
int height = (int) (getHeight() * data[i] / (double) maxValue);
int y = (int) (getHeight() - height);
g.fillRect(x, y, barWidth, height);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(prefW, prefH);
}
}
Note that the bar charts re-size to fill the GUI if you re-size the GUI. Note that the chart accomodates any number of data bars, all depending on the length of the data array passed into it.

You need to repaint the component.
getContentPane().validate();
getContentPane().repaint();

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 can I add a rectangle in BorderLayout.SOUTH?

I am trying to add a thing like this in my music player application in swing.
I tried to add a rectangle to BorderLayout.SOUTH, but it never appeared on screen. Here is what I did:
public class MyDrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(200,200,200,200);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
MyDrawPanel a = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.SOUTH,a);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1000,1000);
frame.setVisible(true);
}
}
I just did not try 200,200,200,200, but I tried a lot of values, even with the help of a for loop, but it never appeared on screen. If I used CENTER instead of SOUTH it appeared. I read the documentation to check how fillRect works, but it simply said it added x+width and y+height. The point (0,0) is the top left corner. I checked that by adding a rectangle to CENTER layout. How cam I do it?
I did not share the output, because it was just a blank screen.
The values you give to fillRect are wrong. The first two are the top left corner's coordinates, relative to the component you're painting in; in your case the MyDrawPanel. With the code you posted, this drawing area is outside of the container the panel is placed in. You want to do
g.fillRect(0,0,200,200);
A note: You usually want to call frame.pack() after you've finished adding all components, so it can layout itself. In your case, this results in a tiny window because the system doesn't know how large it should be. You probably want to add a method
public Dimension getPreferredSize() {
System.out.println("getting pref size");
return new Dimension(200, 200);
}
to ensure it's always large enough to draw the full rectangle.
Also, you should call frame.getContentPane().setLayout(new BorderLayout()) before. You can print it out without setting it to see it is not the default. EDIT: As VGR points out, the documentation says that it is in fact a BorderLayout. I cannot confirm that is the case - it is in fact a RootLayout. That seems to behave like a BorderLayout though.
I thought this might make a quick little project. Here's the level meter I came up with.
The important parts are the DrawingPanel and the LevelMeterModel. The DrawingPanel takes the information from the LevelMeterModel and paints the bars on a JPanel.
The LevelMeterModel is an int array of levels, a minimum level, and a maximum level. The maximum level could be calculated, but I assumed music has a certain volume and frequency range.
The JFrame holds the DrawingPanel. A Swing Timer varies the levels somewhat randomly. The random numbers are in a small range so the bar heights don't change abruptly.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class LevelMeterGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LevelMeterGUI());
}
private final DrawingPanel drawingPanel;
private final LevelMeterModel model;
public LevelMeterGUI() {
this.model = new LevelMeterModel();
this.drawingPanel = new DrawingPanel(model);
}
#Override
public void run() {
JFrame frame = new JFrame("Level Meter GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
System.out.println("Frame size: " + frame.getSize());
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
model.setRandomLevels();
drawingPanel.repaint();
}
});
timer.start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int drawingWidth, drawingHeight, margin, rows;
private final Dimension barDimension;
private final LevelMeterModel model;
public DrawingPanel(LevelMeterModel model) {
this.model = model;
this.margin = 10;
this.rows = 20;
this.barDimension = new Dimension(50, 10);
int columns = model.getLevels().length;
drawingWidth = columns * barDimension.width + (columns + 1) * margin;
drawingHeight = rows * barDimension.height + (rows + 1) * margin;
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(drawingWidth, drawingHeight));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int maximum = model.getMaximumLevel();
double increment = (double) maximum / rows;
int peak = rows * 75 / 100;
int x = margin;
for (int level : model.getLevels()) {
int steps = (int) Math.round((double) level / increment);
int y = drawingHeight - margin - barDimension.height;
for (int index = 0; index < steps; index++) {
if (index < peak) {
g.setColor(Color.GREEN);
} else {
g.setColor(Color.RED);
}
g.fillRect(x, y, barDimension.width, barDimension.height);
y = y - margin - barDimension.height;
}
x += margin + barDimension.width;
}
}
}
public class LevelMeterModel {
private final int minimumLevel, maximumLevel;
private int[] levels;
private final Random random;
public LevelMeterModel() {
this.minimumLevel = 100;
this.maximumLevel = 999;
this.levels = new int[8];
this.random = new Random();
setRandomLevels();
}
public void setRandomLevels() {
for (int index = 0; index < levels.length; index++) {
levels[index] = getRandomLevel(levels[index]);
}
}
private int getRandomLevel(int level) {
if (level == 0) {
return random.nextInt(maximumLevel - minimumLevel) + minimumLevel;
} else {
int minimum = Math.max(level * 90 / 100, minimumLevel);
int maximum = Math.min(level * 110 / 100, maximumLevel);
return random.nextInt(maximum - minimum) + minimum;
}
}
public int[] getLevels() {
return levels;
}
public int getMinimumLevel() {
return minimumLevel;
}
public int getMaximumLevel() {
return maximumLevel;
}
}
}

How to Make Hexagonal JButtons

I have written a short game. In the existing implementation I have a GridBagLayout with buttons located as chess board. Each button occupies the whole grid. Game works fine. My next task is to change the board to be consist of hexagonal buttons, not rectangles like currently. I completely don't know how to do this. Buttons should look like these on the picture:
This isn't the prettiest way, but It will at least give you an Idea:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class HexagonPattern extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ROWS = 7;
private static final int COLUMNS = 7;
private HexagonButton[][] hexButton = new HexagonButton[ROWS][COLUMNS];
public HexagonPattern() {
setLayout(null);
initGUI();
}
public void initGUI() {
int offsetX = -10;
int offsetY = 0;
for(int row = 0; row < ROWS; row++) {
for(int col = 0; col < COLUMNS; col++){
hexButton[row][col] = new HexagonButton(row, col);
hexButton[row][col].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
HexagonButton clickedButton = (HexagonButton) e.getSource();
System.out.println("Button clicked: [" + clickedButton.getRow() + "][" + clickedButton.getCol() + "]");
}
});
add(hexButton[row][col]);
hexButton[row][col].setBounds(offsetY, offsetX, 105, 95);
offsetX += 87;
}
if(row%2 == 0) {
offsetX = -52;
} else {
offsetX = -10;
}
offsetY += 76;
}
}
public static void main(String[] args) {
HexagonPattern hexPattern = new HexagonPattern();
JFrame frame = new JFrame();
frame.setTitle("Hexagon Pattern");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(new Point(700, 300));
frame.add(hexPattern);
frame.setSize(550, 525);
frame.setResizable(false);
frame.setVisible(true);
}
//Following class draws the Buttons
class HexagonButton extends JButton {
private static final long serialVersionUID = 1L;
private static final int SIDES = 6;
private static final int SIDE_LENGTH = 50;
public static final int LENGTH = 95;
public static final int WIDTH = 105;
private int row = 0;
private int col = 0;
public HexagonButton(int row, int col) {
setContentAreaFilled(false);
setFocusPainted(true);
setBorderPainted(false);
setPreferredSize(new Dimension(WIDTH, LENGTH));
this.row = row;
this.col = col;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon hex = new Polygon();
for (int i = 0; i < SIDES; i++) {
hex.addPoint((int) (50 + SIDE_LENGTH * Math.cos(i * 2 * Math.PI / SIDES)), //calculation for side
(int) (50 + SIDE_LENGTH * Math.sin(i * 2 * Math.PI / SIDES))); //calculation for side
}
g.drawPolygon(hex);
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
}
}
Test it out!
This program consists of 2 classes:
HexagonButton, which uses Graphics to draw a hexagon into a JButton. It also returns the row and column values when getRow or getCol are called.
HexagonPattern, which is the main class. It makes the pattern by laying them out with setBounds(x, y, width, height). It uses an ActionListener to print the coordinates of the Hexagon clicked, by calling getRow and getCol.
Like I said, this isn't the greatest program. If you want to make the hexagons smaller, then you'll have to change many variables.

JLabel won't display image - NullPointerException

this is my first Java GUI program, and really only my second java program, so take it easy on me :) My program is a result of a lot of googling and reading java docs. My problem is that I have a sprite sheet of 52 cards, and am attempting to save these cards individually to a Buffered Image array using subImage, and just for testing purposes, display all 52 in a window. The File is in the correct directory I made sure of that. I believe that my problem lies with my use of Jlabels, or simply a foolish mistake. Anyways, here is my class that does the sprite sheet splitting
package gui;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class crdimgs extends JPanel {/**
*
*/
static final long serialVersionUID = 1L;
public final int width = 10;
public final int height = 20;
public int rows = 13;
public int cols = 5;
public BufferedImage image;
File cardimg = new File("Cards.jpg");
BufferedImage cards[];
public void loadsplit(File loadimage){
try{
image = ImageIO.read(loadimage);
} catch(Exception error){
System.out.print("error");
}
cards = new BufferedImage[cols*rows];
}
public crdimgs() {
loadsplit(cardimg);
setLayout(new GridLayout(rows, cols, 1, 1));
int x = 0;
int y = 0;
int subimg = 0;
for( int i = 0; i < rows; i++)
{
JPanel panel = new JPanel();
cards[subimg] = new BufferedImage(width, height, 5);
cards[subimg] = image.getSubimage(x, y, width, height);
panel.add(new JLabel(new ImageIcon(cards[subimg])));
add(panel);
x+=width;
subimg++;
}
y+=height;
x=0;
}
}
}
And my Main class
package gui;
import javax.swing.JFrame;
import java.awt.Color;
public class cards extends JFrame {
private static final long serialVersionUID = 1L;
public cards(){
setTitle("Poker");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(1000, 700);
setLocationRelativeTo(null);
this.getContentPane().setBackground(Color.GREEN);
setVisible(true);
setResizable(false);
add(new crdimgs());
}
public static void main(String[] args){
new cards();
}
}
Errors I receive at the moment are:
errorException in thread "main" java.lang.NullPointerException
at gui.crdimgs.<init>(crdimgs.java:53)
at gui.cards.<init>(cards.java:22)
at gui.cards.main(cards.java:28)
Likely your image is null, possibly because you're looking in the wrong place for it, but to find out, check out which line is 53.
Suggestions:
Start small and then build on. Don't try to do complex image manipulation until you first successfully read and show a single image.
Check where Java is looking for the image, because likely it isn't where you think it is. Put System.out.println(System.getProperty("user.dir")); in your code to find out.
Call setVisible(true) after adding all components to the JFrame.
Improve your exception display code by printing the stack trace: error.printStackTrace() (thanks Mad!).
For example:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
public class PlayingCardTest {
public static void main(String[] args) {
String pathToDeck = "http://www.jfitz.com/cards/classic-playing-cards.png";
try {
final List<ImageIcon> cardImgList = CreateCards.createCardIconList(pathToDeck);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Moving Cards");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CardGameTable(cardImgList, frame));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
#SuppressWarnings("serial")
class CardGameTable extends JLayeredPane {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color BASE_COLOR = new Color(0, 80, 0);
private static final int CARD_COUNT = 20;
private static final int WIDTH_SHOWING = 20;
private JPanel basePane = new JPanel(null);
public CardGameTable(List<ImageIcon> cardImgList, final JFrame frame) {
basePane.setSize(getPreferredSize());
basePane.setBackground(BASE_COLOR);
add(basePane, JLayeredPane.DEFAULT_LAYER);
final MyMouseAdapter myMouseAdapter = new MyMouseAdapter(this, basePane);
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
for (int i = 0; i < CARD_COUNT; i++) {
JLabel card = new JLabel(cardImgList.remove(0));
card.setSize(card.getPreferredSize());
int x = (PREF_W / 2) + WIDTH_SHOWING * (CARD_COUNT - 2 * i) / 2 -
card.getPreferredSize().width / 2;
int y = PREF_H - card.getPreferredSize().height - WIDTH_SHOWING * 2;
card.setLocation(x, y);
basePane.add(card);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
class MyMouseAdapter extends MouseAdapter {
private JLabel selectedCard = null;
private JLayeredPane cardGameTable = null;
private JPanel basePane = null;
private int deltaX = 0;
private int deltaY = 0;
public MyMouseAdapter(JLayeredPane gameTable, JPanel basePane) {
this.cardGameTable = gameTable;
this.basePane = basePane;
}
#Override
public void mousePressed(MouseEvent mEvt) {
Component comp = basePane.getComponentAt(mEvt.getPoint());
if (comp != null && comp instanceof JLabel) {
selectedCard = (JLabel) comp;
basePane.remove(selectedCard);
basePane.revalidate();
basePane.repaint();
cardGameTable.add(selectedCard, JLayeredPane.DRAG_LAYER);
cardGameTable.revalidate();
cardGameTable.repaint();
deltaX = mEvt.getX() - selectedCard.getX();
deltaY = mEvt.getY() - selectedCard.getY();
}
}
#Override
public void mouseReleased(MouseEvent mEvt) {
if (selectedCard != null) {
cardGameTable.remove(selectedCard);
cardGameTable.revalidate();
cardGameTable.repaint();
basePane.add(selectedCard, 0);
basePane.revalidate();
basePane.repaint();
selectedCard = null;
}
}
#Override
public void mouseDragged(MouseEvent mEvt) {
if (selectedCard != null) {
int x = mEvt.getX() - deltaX;
int y = mEvt.getY() - deltaY;
selectedCard.setLocation(x, y);
cardGameTable.revalidate();
cardGameTable.repaint();
}
}
}
class CreateCards {
private static final int SUIT_COUNT = 4;
private static final int RANK_COUNT = 13;
public static List<ImageIcon> createCardIconList(String pathToDeck)
throws MalformedURLException, IOException {
BufferedImage fullDeckImg = ImageIO.read(new URL(pathToDeck));
int width = fullDeckImg.getWidth();
int height = fullDeckImg.getHeight();
List<ImageIcon> iconList = new ArrayList<ImageIcon>();
for (int suit = 0; suit < SUIT_COUNT; suit++) {
for (int rank = 0; rank < RANK_COUNT; rank++) {
int x = (rank * width) / RANK_COUNT;
int y = (suit * height) / SUIT_COUNT;
int w = width / RANK_COUNT;
int h = height / SUIT_COUNT;
BufferedImage cardImg = fullDeckImg.getSubimage(x, y, w, h);
iconList.add(new ImageIcon(cardImg));
}
}
Collections.shuffle(iconList);
return iconList;
}
}
Which shows:

Zero values in the array in the method paint

I would like to make a graphical representation of the audio signal.
I have a problem with entering data array to the method paint (Graphics g).
Data entered in the method setData(int intValue) works fine.
But if I want to print a data array in the method paint() I have zero values.
Why?
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class MyPlotter extends JPanel{
int width = 320;
int height = 130;
int frameSize;
int[] data;
public MyPlotter(int fSize){
setSize(width,height);
setPreferredSize(this.getSize());
this.frameSize = fSize;
data = new int[fSize+1];
}
public void setData(int[] intValue){
data = intValue;
// this works fine:
for (int i=0; i<440; i++)
System.out.println("setData "+data[i]);
repaint();
}
public void paint (Graphics g){
// some code:
// g.drawLine(...)
// g.setColor(...)
// etc...
for (int i = 0; i< frameSize-1;i++)
{
//ZERO values:
System.out.println("paint() "+(data[i]));
// g.drawline(...);
}
}
}
Edit:
Array Data is entered from MyPanel.class
import javax.swing.JPanel;
public class MyPanel extends JPanel {
private MyPlotter plotter;
public MyPanel(){
setSize(320,210);
plotter = new MyPlotter(440);
add(this.plotter,0);
}
public void setData(int[] data){
plotter.setData(data);
}
}
data = intValue;
Here you are psssing reference of intValue to data. If you change data intValue array also get changed.
Try this :
data = System.arraycopy( intValue, 0, data, 0, intValue.length);
Instead of :
data = intValue;
I think that possibly the "repaint()" method you called modifies the original array "intValue"
The assignment you have made data = intValue; only makes "data[]" a reference of your original array so as repaint is called seems like the data is reset or the reference is lost.If you want to copy the array you can do any of the following if a & b are two arrays:
b = Arrays.copyOf(a, a.length);
0r
b = new int[a.length];
System.arraycopy(a, 0, b, 0, b.length);
or
b = (int[]) a.clone();
or
int b[] = new int[a.length];
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
Your code is OK. The problem is in the main method. Ensure you have added only one plotter to the panel.
public static void main(String[]args){
int[] mass = new int[]{1,2,3,4,5,6,7,8,9,10};
JFrame frame = new JFrame();
MyPanel mp = new MyPanel();
frame.add(mp);
frame.setSize(300, 300);
mp.setData(mass);
frame.setVisible(true);
}
Works well.
I'm sorry to answer my own post, but I changed the program completely.
I do not know if I can remove the code from the first post. So I put in a new post.
I changed a application but still have a problem with entering the array to another method.
Copy arrays by "Arrays.copyOf b = (a, a.length)" and changing the array as static does not fix a problem.
I can't enter array from setSignal() to getSignal() in OscilloscopeController class.
OscilloscopeController.java
import java.util.Arrays;
import java.util.Random;
public class OscilloscopeController {
private static int frameSize = (int) (44100 / 100F);
private int idx, numFramesToSkip = 10;
private static int[] toDraw = new int[frameSize];
private static int[] data = new int[frameSize];
public OscilloscopeController() {
for (int i = 0; i < frameSize; i++) {
toDraw[i] = 0;
data[i] = 0;
}
}
public void setSignal(int in) {
// Because the Model layer is off, I generate a test signal
Random randomGenerator = new Random();
int j = 0;
for (int i = 0; i < 44100; i++) {
int randomInt = randomGenerator.nextInt(32675 * 2) - (32675);
if (i % 4 == 0) {
j = randomInt;
}
in = j;
}
if (idx++ > frameSize * numFramesToSkip)
{
idx = 0;
}
else
{
if (idx < frameSize) {
data[idx] = in;
}
if (idx == frameSize)
{
// HERE IS PROBLEM. I'D LIKE THIS toDraw in getSignal()
toDraw = Arrays.copyOf(data, data.length);
}
}
}
public int[] getSignal() {
// IF UNCOMMENT BELOW WORKS FINE, OTHERWISE ZERO VALUES
/*
Random randomGenerator = new Random();
int j=0;
for (int i = 0; i < 440; i++) {
int randomInt = randomGenerator.nextInt(32675*2)-(32675);
if (i%4==0) j = randomInt;
toDraw[i]=j;
}
*/
for (int i = 0; i < frameSize; i++)
System.out.println("Controller.getSignal() = "+ toDraw[i]);
return toDraw;
}
}
MainPanel contains main method:
import java.awt.*;
import javax.swing.*;
public class MainPanel extends JPanel {
public static void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MainPanel());
frame.pack();
frame.setVisible(true);
}
});
}
private final OscilloscopeView oscView = new OscilloscopeView();
private final OscilloscopeController oscController = new OscilloscopeController();
public MainPanel() {
setPreferredSize(new Dimension(400, 200));
// a lot of other components here
oscView.setData(oscController.getSignal());
add(oscView);
}
}
OscilloscopeView.java
import javax.swing.JFrame;
import javax.swing.JPanel;
public class OscilloscopeView extends JPanel {
private OscPlotter plotter = new OscPlotter(441);;
public OscilloscopeView()
{
setSize(320,210);
add(this.plotter);
}
public void setData(int[] data){
plotter.setData(data);
}
}
OscPlotter.java
import java.awt.Color;
import java.awt.Graphics;
import java.util.Arrays;
import javax.swing.JPanel;
public class OscPlotter extends JPanel{
private int width = 320;
private int height = 130;
private float widthC;
private float heightC;
private int sampleSize;
private int frameSize;
private int[] toDraw;
private float x,prevX;
public OscPlotter(int fSize){
setSize(width,height);
setPreferredSize(this.getSize());
this.frameSize = fSize;
this.toDraw = new int[441];
sampleSize = 1;
}
public void setData(int[] data){
toDraw = Arrays.copyOf(data, data.length);
repaint();
}
#Override
public void paintComponent(Graphics g){
widthC = ((float)width/frameSize);
sampleSize = (16 == 16 ? 65536 : 256);
heightC = (((float)height-15)/sampleSize);
g.setColor(Color.black);
g.fillRect(0,0,width,height);
g.setColor(Color.gray);
for (int i = 1; i<height;i+=height/8)
g.drawLine(0,i,width,i);
for (int i = 1; i<width;i+=width/8)
g.drawLine(i,0,i,height);
g.setColor(Color.lightGray);
g.drawLine(0,(int)(height/2),width,(int)((height/2)));
g.setColor(Color.green);
x = 0;
for (int i = 0; i< frameSize-1;i++)
{
prevX = x;
x +=widthC;
// draw the read waveform data
g.drawLine ((int)prevX,(int)((height/2+toDraw[i]*heightC)),(int)x,(int)((height/2+toDraw[i+1]*heightC)));
}
}
}

Categories