Good day,
I might've been a bit vague with the title of my question, but I hope this will explain. The scenario is quite basic - I have a JFrame, in which I have an array of JPlanes. The idea is that when I click on one of them, upon clicking in should become black. Here is the code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
import javax.swing.border.Border;
public class PixelArt {
JFrame frame;
Border blackline;
JPanel squares[][] = new JPanel[100][100];
int x;
int y;
public PixelArt() {
frame = new JFrame("Pixel Art");
frame.setSize(1000, 1000);
frame.setLayout(new GridLayout(100, 100));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
x = i;
y = j;
squares[i][j] = new JPanel();
squares[i][j].setBorder(BorderFactory.createDashedBorder(null));
squares[i][j].addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
x = e.getX();
y = e.getY();
squares[x][y].setBackground(Color.black);
}
});
frame.add(squares[i][j]);
}
}
frame.setVisible(true);
}
public static void main(String[] args) {
new PixelArt();
}
}
The actual problem is that this code does not do what I explained above. It does color in one of the JPlane's black when clicked, but within a 9x9 area starting from the upper corner of the grid. I do not have an explanation for this. The problem seems to be in the following 2 lines:
x = e.getX();
y = e.getY();
One of my guesses is that I have some kind of an offset in the coordinate system, but then this does not explain why regardless on which JPanel I press, the JPanels colored are only in the upper 9x9 area.
Does anyone have a clue how I could fix the problem I described above? If something is unclear in my explanation, please ask. Thank you in advance.
Here is a screenshot of the working code:
you can detect clicked panel using event.getSource() like follow example code
public void mouseClicked(MouseEvent e) {
JPanel panel = (JPanel)e.getSource();//
panel.setBackground(Color.black);
}
the problem is you can't use x,y directly as indexes to array element. because panels has a width and borders too.if you go with x y you have to make some mathematics logic. for example you click middle of your first jpanel in the grid so let's assume x and y coordinate is about 10px but in your code you call [10][10] Janel but actually you should call [0][0].
also as #Cr0w3 says you add listners to all panels.so if you clicked middle of first grid cell or last grid cell there is no difference in x,y.
but if you make a mathematical logic to detect clicked element you need to take in to account your frame/main panel width(also have to update when risize) and border thickness.
also do you really want to do this using 10000 panels ? you may need use a one panel and override paint component method.10000 panels isn't effective for this kind of thing.if you resize or click quickly on panels you will see it take a lot of time. so you may need to draw graphics on a jpanel .see this example
I think you should not add the listener to the panels themselves, but to the frame.
Because the X and Y coordinates might be relative to the panel size (e.g., upper corner of the panel returns 1/1, thus you apply the color to the panel at [1][1] even though you click the panel at [50][50].
If you use the frame for listening at the point you will receive 50/50 as coordinates.
Unfortunately I cannot comment, because of my low reputation but I hope I could help a little.
If you want to add a listener to the panel do not use the coordinates and just apply color to the clicked panel without listening to the coordinates. Using e.getSource() should help you in that case.
Related
The problem is simple. I want to create a vertical tree of values, where as you descend down the levels, the amount of values gets exponentially larger. Let's say the 1st level has 1 numerical value, the next has 10, then the next has 100, then the next has 1000, and so on. The first level is connected to the 2nd level with the use of lines, and the 2nd to the 3rd, and so on, much like a game tree. These values are also evenly spaced, so let's say you have a JPanel which is 500x500. At a y of 100, you have 4 values, and so to evenly space them out, you would have a value at 100, one at 200, and so on.
I've tried incorporating drawString, and connecting them with the drawLine method, and placing this so called diagram on a JPanel. That is actually quite simple, and it works if you only have as many as about 50~ values in a singular level. However, when you only have a 1600x900 screen, you can't fit 100 values (on the x axis, which is 1600) without having a big jumbled up mess.
I was thinking there could be 2 possible solutions for this.
One is that the JPanel is not limited to a set resolution (a.k.a the size of your screen) and is scrollable. If it was 10000 x 900 then making this gigantic tree diagram would actually be quite simple, and I could easily fit the 100 values with enough space between them for the values to actually be discernable. However, as far as I know, it's not possible.
The second solution is that I write these values into a file, but I'm not sure how to go about this.
Does anyone know, theoretically speaking, what could be the simplest solution for properly displaying a large tree diagram filled with hundreds of values in a single level?
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay particular attention to the Performing Custom Painting section.
It turns out it's possible to create one 10000 x 900 drawing JPanel. Adjust the JScrollPane preferred size to the size you want to display. The height should be at least 950 pixels to allow room for the horizontal scroll bar.
I created a checkerboard pattern so you can see that the drawing JPanel does scroll.
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.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LargeDrawingJPanel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LargeDrawingJPanel());
}
#Override
public void run() {
JFrame frame = new JFrame("Large Drawing JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel drawingPanel = new DrawingPanel();
JScrollPane scrollPane = new JScrollPane(drawingPanel);
scrollPane.setPreferredSize(new Dimension(1400, 950));
frame.add(scrollPane, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(10000, 900));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Color[] colors = { Color.RED, Color.BLACK };
int colorIndex = 0;
int x = 0;
int y = 0;
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 9; j++) {
g2d.setColor(colors[colorIndex]);
colorIndex = (colorIndex == 0) ? 1 : 0;
g2d.fillRect(x, y, 100, 100);
y += 100;
}
x += 100;
y = 0;
}
}
}
}
I am having an issue getting this code to run properly. It compiles and initially the frame displays properly. The problem is that when I manually re-size the frame by either maximizing or by dragging the side of the frame over, the text disappears. I am using jGRASP, not sure if that is the issue or not. The code seems to make sense to me, and like I said, it compiles(I know that does not necessarily make it right). I'm still a newbie at this so if anyone can point me in the right direction I would be very appreciative.
import javax.swing.*;
import java.awt.*;
public class JFontSizes extends JFrame {
int x = 5;
int y = 50;
String homework = "This is the first homework assignment";
public JFontSizes() {
super("Increasing Font Sizes");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics brush) {
super.paint(brush);
// This works sometimes. I am not sure if it is a jGRASP issue or something else.
// If I resize the frame, the text disappears, and I cannot get the text to start at the top of the frame
for(int n = 6; n<= 20; ++n) {
brush.setFont(new Font("Serif", Font.PLAIN, n));
brush.drawString(homework, x, y);
y += 15;
}
}
public static void main(String[] args) {
JFontSizes frame = new JFontSizes();
frame.setSize(400, 500);
frame.setVisible(true);
}
}
When first time paint() is called the value of y was 5. And it is incremented in a loop. So that before leaving paint() its value will be 275.
But when you resize your frame paint() is called again and this time the value of y is 275 and when brush.drawString(homework, x, y); is called the homework is printed at 275px bottom from top left corner.
So what you need to do is re-initialize y every time :
public void paint(Graphics brush) {
y = 50;
....
Edit :
As commented by camickr you should override paintComponent(...) instead of paint(...) until you have some specific reason to override paint().
And you mean you are not able to print text at top (even in beginning) then it is because you had initialized y with 50. Which means the text will be drawn at 50px from top.
I am new to Swing in Java and am trying to make a visual grid. I have some simple questions as to how to do the following. Here are some characteristics of the grid:
For each square of the grid, I should be having an equivalent index(i,j) using which I can identify the square and place a text.
Edit - (i,j) is the row/column value - the index of the square in the grid.
The diagonals of the squares should be drawn and each of the four divisions of the square should be having a different color.
Any suggestions as to how to do so.
Given row and column you will need to know the number of columns per row. With this information you can simply do (row * columns) + column which will return the index of the square.
For example, with 8 columns per row, a request for
row = 0, column = 4, will return 4
row = 1, column = 4, will return 12
row = 0, column = 0, will return 0
Rendering the sections of the square is more complex and can be achieved in at least two ways. You could use a Polygon which generates a triangle for each section or Shape and simply use Graphics2D#fill to fill it.
This will come down to how you physically render each square...
Take a look at 2D Graphics for more details and this for an example of both.
A Shape would be much easier to rotate and position and would only require you to have a single instance (or a single instance for each square based on your needs), where as you would require at least 4 Polygons or do some fun rotation...
Updated with simple example
All done with rotating triangles...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DividedSquare {
public static void main(String[] args) {
new DividedSquare();
}
public DividedSquare() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private TriangleShape baseTriangle;
private Color[] colors;
public TestPane() {
colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA};
}
#Override
public void invalidate() {
super.invalidate();
baseTriangle = new TriangleShape(
new Point(0, 0),
new Point(getWidth(), 0),
new Point(getWidth() / 2, getHeight() / 2));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
String text[] = new String[]{
"123.123",
"456.789",
"012.315",
"678.921"
};
FontMetrics fm = g2d.getFontMetrics();
double angel = 0;
for (int index = 0; index < 4; index++) {
g2d.setColor(colors[index]);
Path2D rotated = rotate(baseTriangle, angel);
g2d.fill(rotated);
Rectangle bounds = rotated.getBounds();
int x = bounds.x + ((bounds.width - fm.stringWidth(text[0])) / 2);
int y = bounds.y + (((bounds.height - fm.getHeight()) / 2) + fm.getAscent());
g2d.setColor(Color.WHITE);
g2d.drawString(text[index], x, y);
angel += 90;
}
g2d.setColor(Color.BLACK);
g2d.drawLine(0, 0, getWidth(), getHeight());
g2d.drawLine(getWidth(), 0, 0, getHeight());
g2d.dispose();
}
public Path2D rotate(TriangleShape shape, double angel) {
Rectangle bounds = shape.getBounds();
int x = bounds.width / 2;
int y = bounds.width / 2;
return new Path2D.Float(shape, AffineTransform.getRotateInstance(
Math.toRadians(angel),
x,
y));
}
}
public class TriangleShape extends Path2D.Double {
public TriangleShape(Point2D... points) {
moveTo(points[0].getX(), points[0].getY());
lineTo(points[1].getX(), points[1].getY());
lineTo(points[2].getX(), points[2].getY());
closePath();
}
}
}
Yes, see Making a robust, resizable Chess GUI for a GUI that uses a GridLayout to contain JButton objects. The buttons are held in an 8x8 array (the chessboard), while displayed in a 9 row x 9 column grid layout to accommodate the row (8-1) & column (A-H) identifiers of the board.
Buttons will accept an image that might represent the '4 color diagonal' and text. In the chess board I only use images (the chess pieces).
Edit 1
What is the actual difference between the 4 colored areas (besides the color). E.G. is there supposed to be different functionality depending on which of the diagonal areas the user clicks in?
Each of the 4 colored areas in the square has a value from a table based on some algorithm. The color depends upon the value. Currently, it is just used as an indicator. But the user needs to select an initial square in the grid where I place a mark that it is initial.
And the text. Should it be on top of the '4 color diagonal', beside it, below it..?
I also plan to place a text in each of the region indicating that value. Hence, there is one text field to be placed in each of the four partitions of the square.
Text field or label? A label can show text, but a text field makes it possible to copy the text or change it.
I need a text field because I need to change the text during the course of the algorithm.
By 'I' DYM the program, or the user? The program can change text in a label or text field, but the text field is user editable.
I mean the program
OK - Draw the design and text on an image. Use the image as a button icon. The user can select an initial square by clicking the button. If the text changes, generate a new image and set it to the button.
Edit 2
So do you mean that I have to create an image with 4 colors? Would that not be difficult. I would like to change the colors from the program itself. My algorithm would generate the values and at certain points of the algorithm, I would like to see the visualization.
Not if you generate the image at run-time.
How do I generate an image at run-time?
See the answer to Example images for code and mark-up Q&As The first set of Icons..
...
..As well as the Sprite sheet..
..were generated at run-time in Java code. Each set of images links to the question that includes the code that generated them.
I believe you're describing the SetGridLayout feature in swing. If you want a tutorial on how to set up such a window, you can find it here:
http://docs.oracle.com/javase/tutorial/uiswing/layout/grid.html
After reading your question a second time... I think you plan on drawing a grid...
I'd look into the Draw.java library
Maybe you can try to do this adding a jTable object, this object contains methods that can put values in every value i and j respectively like:
jTable1.setValueAt(Value, i, j);
this will allows you to change the value in every cell.
Basically, I'm trying to move a JButton (or even a JLabel) inside a frame using the arrow keys.
I've successfully figured out the part to detect the arrow key presses. Now all i want to do is to change the position of the button (or label) depending on the key pressed.
E.G. when I press the "UP" key, the button/label should move up by say 5 pixels. I couldn't find the property to change the position of the button.
Do I need to put button/label inside the panel or something like that?
Please help, its not a homework, just curious :)
How I would do it is to use the Graphics instead of a Panel. So the bit of code that draws it could be:
public class panel extends JPanel{
public int X = 50; //Starting x
public int Y = 50; //Starting y
public panel(){
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.drawString("Hello this will be moved", X, Y);//<--- This draws the text
}
}
This bit of code will draw the text and repeatedly draw it. All you have to do is in the Key Listener or what ever was used, do;
X++;
or:
Y++;
That will add 1 everytime. So when you press the key it is moved one x pixel or y pixel or back or forth, depending on the ++ or --. Then add this to the JFrame and it will draw it on screen.
use of a raw image is much better in the application i'm doing but here is something that i accidently found while doing it ;)
This infact can be done by absolute positioning of label(dont know about button) ie. without use of any Layout Manager.
all we have to do is the follwing:
setLayout(null);
Jlabel testLabel = new JLabel("Test Moving label");
now with that done. we have to set
testLabel.setBounds(XCoord+ insets().left, yCoord+ insets().top, 150, 25);
here xCoord and yCoord are the coordinates of the top left pixel of the label. the above line can be added to the actionListener. in my case its the action for pressing UP, RIGHT and LEFT arrow keys.
we can increment and decrement the xCoord and yCoord accordingly.
I'm working on building a chess game in Java, and I'm currently having a bit of trouble getting the GUI exactly the way I want it with Swing. I'm using a GridLayout to organize a grid of 8x8 ChessButtons (which override the JButton so that I can store extra information inside of them such as coordinates). Originally, the ChessButtons wouldn't appear unless I moused over them, but I solved that problem by placing each ChessButton inside a separate JPanel and setting each button's setPreferredSize() to a set height and width.
Now, my problem is that there seems to be a small margin or padding above (and/or below?) each button. I've made sure to set setHgap(0) and setVgap(0) for the GridLayout, so I'm pretty sure the mysterious margin is coming from either the buttons or the JPanels. But, I can't seem to get rid of them, and they seem to be causing each ChessButton to shift a little bit up/down whenever I mouse of them.
I realize this description of the problem might be a little hard to visualize, so I've taken a screenshot (using JButtons rather than ChessButtons so the gaps are slightly easier to recognize): http://img3.imageshack.us/img3/6656/jbuttonmargins.png
Here is the code I used to initialize each ChessButton:
chessBoard = new JPanel(new GridLayout(8, 8, 0, 0));
chessBoard.setBorder(BorderFactory.createEmptyBorder());
for (int i = 0; i <= 65; i++) {
//Create a new ChessButton
ChessButton button = new ChessButton("hi");
button.setBorder(BorderFactory.createEmptyBorder());
button.setPreferredSize(new Dimension(75, 75));
button.setMargin(new Insets(0, 0, 0, 0));
//Create a new JPanel that the ChessButton will go into
JPanel buttonPanel = new JPanel();
buttonPanel.setPreferredSize(new Dimension(75, 75));
buttonPanel.setBorder(BorderFactory.createEmptyBorder());
buttonPanel.add(button);
//Add the buttonPanel to the grid
chessBoard.add(buttonPanel);
}
So, how can I get rid of these vertical spaces between buttons? I'm relatively new to Swing, so I'm sorry if the answer is extremely obvious, but I'd appreciate any help anyone might have to offer! Thanks in advance!
Don't add an empty border; do use setBorderPainted(false).
Addendum: As #camickr notes, the panel's layout may include default gaps. The example below uses no-gap GridLayout accordingly.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/4331699 */
public class ButtonBorder extends JPanel {
private static final int N = 8;
private static final int SIZE = 75;
public ButtonBorder() {
super(new GridLayout(N, N));
this.setPreferredSize(new Dimension(N * SIZE, N * SIZE));
for (int i = 0; i < N * N; i++) {
this.add(new ChessButton(i));
}
}
private static class ChessButton extends JButton {
public ChessButton(int i) {
super(i / N + "," + i % N);
this.setOpaque(true);
this.setBorderPainted(false);
if ((i / N + i % N) % 2 == 1) {
this.setBackground(Color.gray);
}
}
}
private void display() {
JFrame f = new JFrame("ButtonBorder");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ButtonBorder().display();
}
});
}
}
Originally, the ChessButtons wouldn't appear unless I moused over them, but I solved that problem by placing each ChessButton inside a separate JPanel and setting each button's setPreferredSize() to a set height and width
That is not the proper solution. There is no reason to use a JPanel to hold the buttons. In fact, this is probably the cause of the problem. The buttons should show up when you add them to a GridLayout. If they don't show up its probably because you added the buttons to the GUI after making the GUI visible. Components should be added to the GUI BEFORE it is made visible.
Now, my problem is that there seems to be a small margin or padding above (and/or below?) each button
I don't understand why there also isn't a horizontal gap. When you create a JPanel, by default it uses a FlowLayout which also contains a horizontal/vertical gap of 5 pixels. So I understand why you might have the vertical gap of 10 pixels. I don't understand why there is no horizontal gap.
If you need more help post your SSCCE demonstrating the problem. And the SSCCE should use regular JButtons. Get the basics working with standard components before you start playing with custom components. That way you know if the problem is with your custom code or not.
Try adding chessBoard.setPreferredSize(600, 600) to create a JPanel for the board that only has room to fit the buttons (8 buttons each way * 75 size each way on the buttons).