Let's say I'm building a chess app with swing. I'm using an array of JLabels to represent the checkerboard (each has its appropriate icon set as a lightly/dark shaded box). I've created another array of JLabels to hold the icons of the chess pieces, but I'm not familiar enough with swing to know how to implement them to display on top of the checkerboard. Anyone know of any techniques?
I have written a small example that builds a window and two JLabels on top of each other.
Note that the grey.jpg and pawn.png images have 128x128 size and the pawn one has transparent background (this way I prevent the background of the pawn image from hiding the grey rectangle box).
Here is the ChessFrame class that builds the window and adds the components:
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ChessFrame extends JFrame {
private JPanel panel;
private JLabel greyBox;
private JLabel pawn;
public ChessFrame() {
super();
/* configure the JFrame */
this.setSize(300, 300);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addComponents() {
panel = new JPanel();
greyBox = new JLabel(new ImageIcon("images/grey.jpg"));
pawn = new JLabel(new ImageIcon("images/pawn.png"));
/* add the pawn inside the grey box (we have to set a layout for the grey box JLabel) */
greyBox.setLayout(new BorderLayout());
greyBox.add(pawn);
/* add grey box to main JPanel and set its background to white so we observe the result better */
panel.add(greyBox);
panel.setBackground(Color.WHITE);
this.getContentPane().add(panel);
}
#Override
public void setVisible(boolean b) {
super.setVisible(b);
}
}
And here is a Main class that creates a ChessFrame object and shows the window:
public class Main {
public static void main(String[] args) {
ChessFrame chessFrame = new ChessFrame();
chessFrame.addComponents();
chessFrame.setVisible(true);
}
}
Related
I have issue with drawing shapes inside of JPanel that I already added using Netbeans GUI. Now, I have no idea where to add code for drawing a circle inside of that JPanel and how to insert and call it in the JPanel that is sitting empty now, waiting for this shape to be drawn. I already set up destination JPanel to be Flow layout.
Netbeans Designer created a big class in which I have entire frame with this JPanel, and I want to keep it inside of it as I can't really add it any other way because Designer doesn't let me change main initComponents method in which all components are sitting now. I have been reading tutorials and previous posts but noone really encountered this using Netbeans Designer.
SO can someone just help me with adding proper method in this frame class and how to call it from JPanel I want to draw in. JPanel is 50x50 pixels.
So as per #Abra, I changed some code:
so I made a new Circle Class, adjusted it a bit as I don't want to create a new frame but put this in JPanel.
public class Circle extends JPanel {
Color color;
public void circle(Color color) {
this.color = color;
setPreferredSize(new Dimension (30,30));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(0, 0, r, r);
g.setColor(color);
}
private void showGUI() {
JPanel panel = new JPanel();
panel.add(this, FlowLayout.CENTER);
panel.setVisible(true);
}
}
Then I opened JPanel in Designer, and added code to run it, in initComponents method like this:
circlePanel.setPreferredSize(new java.awt.Dimension(40, 40));
new Circle().showGUI();
PanelDS.add(circlePanel);
circlePanel is destination for this drawing and is inside PanelDS itself. It doesn't work this way tho, but Netbeans shows no errors in code. Additionally, how can I forward color to circle class.
In order to draw on a JPanel you need to override the paintComponent() method of JPanel. In order to override the method, you need to create a class that extends JPanel. I don't think that there exists a GUI designer that can generate the required code for you. So you have to write the code of the class that extends JPanel.
Here is a minimal example. It displays a blue circle.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class Drawing2 extends JPanel {
private JFrame frame;
public Drawing2() {
setPreferredSize(new Dimension(100, 100));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(25, 25, 50, 50);
}
private void showGui() {
frame = new JFrame("Drawing");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
new Drawing2().showGui();
}
}
Here's what you should see when you run the above code.
First off, I am new to JFrame and all the associated classes so I am still learning how to do this.
My current goal is to draw multiple images on a single JFrame. So far, I can get test2.png to draw, but not test1.png. Any suggestions or help understanding JFrame is appreciated. This is my main class:
package com.osj.oneshotjava;
import java.awt.Dimension;
import javax.swing.JFrame;
/**
*
* #author BCG04
*/
public class actorTest {
public static void main(String []args){
JFrame jFrame = new JFrame("OSJ actor test");
jFrame.setPreferredSize(new Dimension(640, 480)); // sets window size
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Actor2 Background = new Actor2(jFrame, "test1.png");
Actor2 testActor = new Actor2(jFrame, "test2.png");
jFrame.pack(); // automatically adjusts window size (also sets window size based on the maximum and minimum sizes)
jFrame.setVisible(true);
}
}
And this is Actor2:
package com.osj.oneshotjava;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* #author BCG04
*/
public class Actor2 { //Purpose: make it easer to add multiple images to a single JFrame using only a single call to Actor2's constuctor rather than repeating the same section of code for each image.
private BufferedImage image = null;
private JLabel jLabel = null;
public Actor2(JFrame jFrame, String filename){
try
{ // try to load a image 'filename' into 'image'
image = ImageIO.read(new File(filename));
}
catch (Exception e)
{
e.printStackTrace(); // if loading fails, print the error
System.exit(1); // then exit with an error code 1 'unsuccessful exit'
}
ImageIcon imageIcon = new ImageIcon(image); // create a new ImageIcon that contains 'image'
JPanel jPanel = new JPanel();
jLabel = new JLabel();
jLabel.setIcon(imageIcon); // set JLabel 'jLabel' to contain 'imageIcon'
jPanel.add(jLabel);
jFrame.getContentPane().add(jPanel, BorderLayout.CENTER); // makes window visible?
}
public JLabel getJLabel(){
return jLabel;
}
}
Edit:
-removed Thread.sleep(1000); and setLocation(90, 90); since they were not relevant to the question or the problem and I originally had them in to test whether I could move images.
-removed jLabel.setBounds as it did not seem to do anything.
+added a comment clarifying Actor2's goal.
I'd like to clarify my end goal, I would like to create a simple 2d game that uses Java.
Here is a complete, self contained example that is close to what you're after. It is to demonstrate the use of a layout manager.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
public class DuelingJLabels{
public static void startGui(){
JFrame frame = new JFrame();
JPanel scene = new JPanel();
Actor red = new Actor(Color.RED);
Actor blue = new Actor(Color.BLUE);
//scene.setLayout( null );
scene.add(red.image);
scene.add(blue.image);
//scene.setPreferredSize( new Dimension(512, 512) );
frame.add(scene, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
static class Actor{
int x, y;
JLabel image;
public Actor(Color c){
BufferedImage a = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics g = a.getGraphics();
g.setColor(c);
g.fillOval(0, 0, 64, 64);
image = new JLabel();
image.setIcon(new ImageIcon(a));
image.setLocation( x, y );
image.setSize( 64, 64);
image.addMouseListener( new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent evt){
x = x+64;
if(x>=448){
x = 0;
y += 64;
}
image.setLocation(x, y);
}
});
}
}
public static void main(String[] args){
EventQueue.invokeLater( DuelingJLabels::startGui );
}
}
Take note of the line scene.setLayout(null); if you run the example with that line commented out, then you will see two circles side by side. That is because we are letting swing handle the layout. scene is a JPanel with a FlowLayout by default.
Now when you click the circles, nothing happens* because we tell the new position but the layout manager resets the position.
*Actually they move sometimes, but if you trigger a re-validation then they get moved by the layout manager.
So now remove the comment on scene.setLayout(null); and notice the difference.
The frame is tiny, and we have to manually resize it to see our scene.
There is only one circle.
If you click on the circle, it moves.
That's because we have told swing to not use a layout manager for the JPanel scene. That means it will not reposition the components in the scene for us, and it will not adjust the sizes for us either.
The other line that is commented setPreferredSize makes scene tell the parent component a size it would like to be at. If you uncomment that line then the JFrame will not start out incredibly small. You should only use that with custom components, otherwise you can end up conflicting with the layout manager.
Another tool, which I have found usefull is the JLayeredPane because it gives you some depth. I also think the example is good.
Finally, another technique for putting custom graphics arbitrarily is to #Override paintComponent. That way you can draw whatever, where-ever on your component.
I've made a JFrame with Diferent JButtons and i'd like to get an image from another class. Any ideas? Or how draw on the same class but on the action performed?
Because it doesnt let me to do any drawings...my complier always gives me error messages
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.*;
public class red extends JFrame {
public JButton b;
public JButton b1;
public JButton b2;
public JButton b3;
public JButton b4;
public static Image p;
public static Graphics g;
public red() throws IOException {
gui1 x = new gui1();
setTitle(" ");
setSize(1200,700);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
b= new JButton("click");
b1= new JButton();
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e0){
b1.setBounds(0, 0, 200, 200);
b.show(false);
add(x);
}
});
b.setBounds(0, 0, 100, 100);
add(b1);
add(b);
setVisible(true);
}
public static void main(String[] args) throws IOException {
red k = new red();
}
}
import java.awt.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class gui1 extends Canvas {
public static Image p;
public void paint(Graphics g){
g.drawImage(p, 700, 200, 100, 100, this);
}
{
try {
p= ImageIO.read(new File("Lighthouse.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Phew! I see A LOT of errors in your code (even after I corrected the compilation errors):
You're not following the Java naming conventions:
Class names should be nouns, in mixed case with the first letter of each internal word capitalized
while red is a noun it should be more descriptive and be capitalized. The same goes for gui1
You're extending JFrame which in plain english would say: red is a JFrame, you should really avoid this and create your GUI based on JPanels instead... see Java Swing using extends JFrame vs callint it inside of class
You're setting size (a REAAAAAAALLY big one window for the JButton sizes you're using), instead use pack()
You're using null-layout, while pixel-perfect GUIs might seem like the easiest way to create complex GUIs for Swing newbies, the more you use them the more problems related to this you'll find in the future, they are hard to maintain and cause random problems, they don't resize, etc. Please read Null layout is evil and Why is it frowned upon to use a null layout in Swing? for more information about why you should avoid its use and why you should change your GUI to work with Layout Managers along with Empty Borders for extra spacing between components.
You're making use of a deprecated method JFrame#show() you should be using JFrame#setVisible(...) instead.
Related to point #4, you shouldn't be calling setBounds(...) method, but let that calculations to the layout managers.
You're not placing your program on the Event Dispatch Thread (EDT), Swing is not thread safe, you can fix this by changing your main() method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're mixing AWT and Swing components, instead of using AWT's Canvas use Swing's JPanel which has more functionality and support.
Images will become embedded resources once they're packaged in a JAR file, so it's wise to start treating them as if they already were, not as external files as shown in the embedded-resource tag.
Once you change from Canvas to JPanel you should override its paintComponent(...) method and not paint(...) and call it's super.paintComponent(g) method as the first line, also don't forget to add the #Overrides annotation. See the tutorial on Swing custom painting.
You're abusing the use of static keyword, see how does the static keyword works?
After seeing all the above errors I recommend you to go back and Learn the basics of the language before starting with a graphical environment which will only add more difficulty to your learning.
From what I understand you want to draw an image on a button click, if that's the case then you can wrap your image in a JLabel and add that JLabel to a JPanel which then is added to a parent JPanel which is later added to the JFrame:
As you can see in the GIF above, the icon is displayed after user presses the button.
Obviously this can be improved for the GUI to be more "attractive" with combinations of layout managers and empty borders as stated before.
This was done with the following code:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageDrawingFromOneClassToAnother {
private JFrame frame;
private JPanel pane;
private JPanel leftPane;
private JPanel rightPane;
private ImageIcon icon;
private JButton button;
private JLabel label;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageDrawingFromOneClassToAnother().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
icon = new ImageIcon(this.getClass().getResource("king.png")); //Read images as if they were already embedded resources
button = new JButton("Draw image");
label = new JLabel(""); //Create an empty label
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(icon); //On button click, we set the icon for the empty label
}
});
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200); //Set a size for the main panel
}
};
pane.setLayout(new GridLayout(1, 2)); //The main panel
leftPane = new JPanel(); //The button panel
leftPane.setLayout(new BoxLayout(leftPane, BoxLayout.PAGE_AXIS));
leftPane.add(button);
rightPane = new JPanel(); //The panel where the image will be drawn
rightPane.add(label);
//We add both (button and image) panels to the main panel
pane.add(leftPane);
pane.add(rightPane);
frame.add(pane); //Add the main panel to the frame
frame.pack(); //Calculate its preferred size
frame.setVisible(true); //Set it to be visible
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I'm trying to make a main menu for the board game risk. There's a custom background image, and ideally I would like the buttons to appear over the image. However when I run my code, only the button called "New Game" appears, and the other buttons will appear if you hover the mouse over them. I have tried just about everything (there are similar problems on here) but can't seem to fix the problem. Maybe it has something to do with my code? I appreciate any help/suggestions!
package View;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.SpringLayout;
/**
* These classes set up the GUI of the Risk program.
* The main menu, dialog for setting player count, dialog for name/color settings for each
* player, the Risk game board, and a menu used during a Risk game session are included.
**/
public class Menu extends JFrame {
private JPanel mainPanel;
private JButton newGameButton;
private JButton loadGameButton;
private JButton quitButton;
private JButton ruleButton;
private String newGameButtonName = "newGameBtn";
private String loadGameButtonName = "loadGameBtn";
private String quitButtonName = "quitBtn";
private String ruleButtonName = "rulebtn";
//private SpringLayout mainLayout;
private static BufferedImage img;
/**
* Constructs the main menu.
**/
public Menu()
{
add( mainMenu( ) );
//setTitle("Risk: UConn Edition");
setPreferredSize(new Dimension(640, 700));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
toFront();
pack();
setVisible(true);
}
/**
* creates the buttons for the jPanel
*
* #return
*/
private JPanel mainMenu()
{
// Creates the panel
mainPanel = new JPanel();
// Sets Layout
//mainLayout = new SpringLayout();
mainPanel.setLayout(null);
// Creates buttons
newGameButton = new JButton("New Game");
newGameButton.setBounds(20,300,150,50);
newGameButton.setOpaque(false);
newGameButton.setContentAreaFilled(false);
newGameButton.setForeground(Color.RED);
newGameButton.setBackground(Color.BLUE);
loadGameButton = new JButton("Load Game");
loadGameButton.setBounds(20,400,150,50);
//loadGameButton.setOpaque(false);
//loadGameButton.setContentAreaFilled(false);
loadGameButton.setForeground(Color.RED);
quitButton = new JButton("Quit");
quitButton.setBounds(490,400,150,50);
quitButton.setOpaque(false);
quitButton.setContentAreaFilled(false);
quitButton.setForeground(Color.RED);
ruleButton = new JButton("Rules");
ruleButton.setBounds(490,300,150,50);
ruleButton.setOpaque(false);
ruleButton.setContentAreaFilled(false);
ruleButton.setForeground(Color.RED);
// Sets button commands
newGameButton.setActionCommand(newGameButtonName);
loadGameButton.setActionCommand(loadGameButtonName);
quitButton.setActionCommand(quitButtonName);
// Adds buttons to mainPanel
mainPanel.add(newGameButton);
mainPanel.add(loadGameButton);
mainPanel.add(quitButton);
mainPanel.add(ruleButton);
// add(mainPanel);
return mainPanel;
}
private Image createImage(){
try {
img = ImageIO.read(
Menu.class.getResource("../resource/riskUconn.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return img;
}
/**
* paint method
*/
#Override
public void paint (Graphics g) {
Image img = createImage();
g.drawImage(img, 20,20,this);
super.paint(g);
}
// Action listeners for Menu
protected void riskViewActionListeners(ActionListener evt)
{
newGameButton.addActionListener(evt);
loadGameButton.addActionListener(evt);
quitButton.addActionListener(evt);
}
public static void main(String [] args){
Menu m = new Menu();
}
}
Painting components doesn't always notify the parent or child components. Instead of overriding paint, try overriding paintComponent and paint your background there, this is what paintComponent is really meant for, painting the background.
You should avoid overriding paint of top level containers.
Most top level containers have a series of layers, including the JRootPane, contentPane and even a glassPane, all of which will paint over the top of the a frame.
Instead, create a custom component, which extends from something like JPanel and use it as you base component. You can override it's paintComponent and paint the background within it. Then add this component to your frame, maybe even making it the content pane.
Remember, if a component is opaque, it will cover any child components
You should also avoid loading resources from within any paint method (especially continuously reloading them) as this can have a serve affect on the performance of your program. Painting should paint as fast as it can.
For example and example
When I add Swing component (like a JButton) to a JPanel, it renders with it's 'preferred size'.
However, the preferred size is actually larger than the painted button. There appears to be an invisible border around it.
Here's a simple frame with my test panel:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TestPanel pnl = new TestPanel();
frame.getContentPane().add(pnl);
frame.pack();
frame.setVisible(true);
Here's my test panel ...
public class TestPanel extends JPanel {
JButton btn1 = new JButton("Test1");
JButton btn2 = new JButton("Test2");
public TestPanel() {
this.add(btn1);
this.add(btn2);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.RED);
Dimension dim = btn1.getPreferredSize();
g.drawRect(btn1.getX(), btn1.getY(), (int)(dim.getWidth()), (int)(dim.getHeight()));
}
}
Notice I painted btn1's "PreferredSize" in RED to demonstrate that the preferredSize is actually larger than the button itself.
My question is, how can I determine the width and height of the painted button, not the JButton's preferredSize?
Any help is greatly appreciated, thanks!
UPDATE
Because I actually need this to work for all Swing components, here's a screen shot with the more components.
Unfortunately, I need to figure this out, determining the "real" size of the visible widget is crucial to my application.
I don't think this is particular or practically achievable.
The problem is, the button is using the "unpainted" area to paint other elements, like the focus highlight.
You could try look at the AbstractButton#set/getMargin
If nothing better comes along, note that the authors "recommend that you put the component in a JPanel and set the border on the JPanel."
Addendum: Based on your comments below, it's clear that your question is not about rendering borders but about establishing a component's boundary. What you perceive as unused space is actually reserved by the UI delegate for any number of uses, e.g. selection highlighting or esthetic coherence. You can get an idea of how this varies by selecting different Look & Feel themes in the examples here and here.
Using getbounds():
Using setBorder():
import component.Laf;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* #see https://stackoverflow.com/a/15490187/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new FlowLayout());
// https://stackoverflow.com/a/11949899/230513
f.add(Laf.createToolBar(f));
f.add(decorate(new JButton("Test")));
f.add(decorate(new JTextField("Test")));
f.add(decorate(new JTextArea(3, 8)));
f.add(decorate(new JCheckBox("Test")));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private JPanel decorate(final JComponent c) {
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle r = c.getBounds();
g.setColor(Color.red);
// NB pen hangs down and to the right
g.drawRect(r.x - 1, r.y - 1, r.width + 1, r.height + 1);
}
};
p.add(c);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}