I want to change the panel dynamically and as this answer show, they recommend to use the cardLayout. But I want to change the whole UI(no old button left) and cardLayout seems not so convenient. So I have the following code:
JFrame frame = new JFrame ("Key test");
MyDrawPanel1 dp1 = new MyDrawPanel1(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
JPanel p = new JPanel ();
p.setLayout(new BorderLayout());
p.add(dp1,BorderLayout.CENTER);
frame.getContentPane().add(p);
frame.pack();
frame.setVisible (true);
And in the MyDrawPanel1 there is a button to change panel:
public MyDrawPanel1(final JFrame frame) {
clickButton.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
frame.getContentPane().removeAll();
//frame.validate();
frame.getContentPane().add(new MyDrawPanel2());
((JPanel)frame.getContentPane()).revalidate();
}
});
this.add(clickButton);
}
Edit:some more codes in MyDrawPanel1.
JButton clickButton = new JButton("click");
Image image = new ImageIcon("D:/0.jpg").getImage();
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.drawImage(image, 3, 40, null);
}
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(null), image.getHeight(null));
}
return super.getPreferredSize(); // default
}
But the first panel doesn't disappear and I have to minimize it to refresh it so I can see the second panel. My question is the why validate don't work and if there is any other alternatives. Thanks.
Edit:here are the pictures I snipped about the panel.(first panel):
(after clicked):
Edit:
The madProgrammer and Adarsh Singhal provide two ways to solve the problem. The first is the second the panel don't call the super.paintComponent(), so add it then it works fine (as this answer say, it is the eraser so the first panel was gone). The second is calling the frame.repaint(), but I don't understand why?
We've to use repaint() to tell the components to repaint themselves. Visualize it as your case. While revalidate is used to update the layouts. So, whenever you add/remove components dynamically, you need to call both of them.The following written code displays a JFrame set to CardLayout to draw Red dp1(JPanel) completely on JFrame. On dp1, there is a Jbutton. If you click that button, dp1 will be removed & dp2(JPanel) will be drawn. dp2 is Green to distinguish changes. It seems you've forgotten repaint().
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyFrame extends JFrame{
JButton button;
private static MyFrame frame;
public MyFrame(){
this.setSize(400, 400);
this.setLayout(new CardLayout());
this.setLocationRelativeTo(null);
JPanel dp1 = new JPanel();
dp1.setBackground(Color.RED);
add(dp1);
button = new JButton("Click me to remove dp1 & draw dp2");
dp1.add (button);
JPanel dp2 = new JPanel ();
dp2.setBackground(Color.GREEN);
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
frame.getContentPane().remove(dp1);
frame.add(dp2);
frame.revalidate();
frame.repaint();
}});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible (true);
}
public static void main(String[] args) {
frame = new MyFrame();
}
}
Related
I am currently working on a little game but I just encountered a problem:
I have three classes, the first one is the JFrame:
public class Game
{
public static void main(String[] args)
{
new Game().gui();
}
public void gui()
{
DrawPanel panel = new DrawPanel();
JFrame frame = new JFrame("Test");
//frame.add(panel);
frame.add(new MainMenu());
frame.setSize(800, 700);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
}
Now I have two other classes, one is the mainMenu, currently consisting of just one JButton.
I decided to make the menu its own class because later, I want to call the menu by pressing escape, but the problem is that (for testing reasons) I want to draw an rectangle when "start" is pressed. I tried different approaches but nothing happens.
public class MainMenu extends JPanel implements ActionListener
{
GamePanel panel = new GamePanel();
public MainMenu()
{
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton b1 = new JButton("Start");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipadx = 200;
b1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
add(b1, c);
}
}
public class DrawPanel extends JPanel
{
public void paint(Graphics g)
{
g.drawRect (10, 10, 200, 200);
}
}
There are several errors I found in your code:
You're not adding your DrawPanel to anything because this line
frame.add(panel);
is commented, so, that leads us to the next problem:
You're overriding paint() method instead of paintComponent() and also don't forget to call
super.paintComponent();
at the very beginning of the method. Without it, you're preventing your code to keep painting the rest of the components. See Custom painting in Swing
That still doesn't makes anything appear because you haven't declared a location for your DrawPanel and thus it's taking JFrame's default Layout Manager which is BorderLayout and it's default location when not specified is CENTER, and as you're adding new MainMenu() to it on the same position it replaces the DrawPanel panel, since only one component can exist on the same position.
Instead try and place the DrawPanel to the CENTER and the MainMenu to the SOUTH. It now should look like this:
Don't forget to place your program on the Event Dispatch Thread (EDT) by writing your main method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're implementing ActionListener on MainMenu but not implementing it's methods, remove it or implement the actionPerformed method and move the code inside the b1 action listener to it. However I highly recommend you to take at Should your class implement ActionListener or use an object of an anonymous ActionListener class too
You're playing with MainMenu's JPanel visibility, instead you should try using a CardLayout or consider using JDialogs.
For example I would make a JDialog and place the JButton there, so it will open the JFrame with the Rectangle drawn in it.
Now, the code that made the above output and follows all recommendations (but #6) is:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Game {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Game().gui();
}
});
}
public void gui() {
DrawPanel panel = new DrawPanel();
JFrame frame = new JFrame("Test");
frame.add(panel, BorderLayout.CENTER);
frame.add(new MainMenu(), BorderLayout.SOUTH);
frame.setSize(400, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
}
class MainMenu extends JPanel {
// GamePanel panel = new GamePanel();
public MainMenu() {
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton b1 = new JButton("Start");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipadx = 200;
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
add(b1, c);
}
}
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(10, 10, 200, 200);
}
}
As suggested in the comments by #MadProgrammer, you can also override the getPreferredSize() method of your DrawPanel and then call frame.pack():
Your DrawPanel class should now look like this:
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(10, 10, 200, 200);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
Having Trouble adding components on panels in cardlayout , they are appearing weirdly (very small and top center) , have tried many layouts but not getting appropriate results , i have to place , buttons , Split Panes , tab Panes on different panels
here is the sample code. Having same issue on the code which i m working right now
pls see where i m going wrong
public static void main(String[] args) {
CardLayout cards = new CardLayout();
JPanel cardPanel = new JPanel();
cardPanel.setLayout(cards);
JFrame guiFrame = new JFrame();
//make sure the program exits when the frame closes
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("Frame");
guiFrame.setSize(528, 555);
//This will center the JFrame in the middle of the screen
guiFrame.setLocationRelativeTo(null);
guiFrame.setVisible(true);
JButton B_1 = new JButton("");
JButton B_2 = new JButton("");
JPanel firstCard = new JPanel();
firstCard.add(B_1);
B_1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
cards.next(cardPanel);
}
});
JPanel secondCard = new JPanel();
secondCard.add(B_2);
B_2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
cards.previous(cardPanel);
}
});
cardPanel.add(firstCard);
cardPanel.add(secondCard);
guiFrame.add(cardPanel);
}
}
Your code wouldn't even compile.
I rearranged your code and added the following features:
I started your GUI with a call to SwingUtilities invokeLater. This puts the Swing GUI on the Event Dispatch thread (EDT).
I put like code together (JFrame, JPanel) so that the code would be easier to follow.
I put a JLabel on each of the card panels so you could see which panel was which.
I put text in the JButtons. They were so small because you had no text or label for the buttons to display.
I moved some of the JFrame code to the end of the method. The last thing you do in a Swing GUI is set the frame visible. You have to construct all of the Swing components before you make the JFrame window visible.
Here's the working, tested, minimal example of a card layout that you were trying to code.
import java.awt.CardLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CardLayoutTest implements Runnable {
#Override
public void run() {
JFrame guiFrame = new JFrame();
// make sure the program exits when the frame closes
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("Frame");
final CardLayout cards = new CardLayout();
final JPanel cardPanel = new JPanel();
cardPanel.setLayout(cards);
final JPanel firstCard = new JPanel();
firstCard.setLayout(new FlowLayout());
JLabel label1 = new JLabel("Panel 1");
firstCard.add(label1);
JButton b_1 = new JButton("Swap to Panel 2");
firstCard.add(b_1);
final JPanel secondCard = new JPanel();
secondCard.setLayout(new FlowLayout());
JLabel label2 = new JLabel("Panel 2");
secondCard.add(label2);
JButton b_2 = new JButton("Swap to Panel 1");
secondCard.add(b_2);
b_1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
cards.next(cardPanel);
}
});
b_2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
cards.previous(cardPanel);
}
});
cardPanel.add(firstCard, "First Panel");
cardPanel.add(secondCard, "Second Panel");
guiFrame.add(cardPanel);
guiFrame.setSize(528, 555);
// This will center the JFrame in the middle of the screen
guiFrame.setLocationRelativeTo(null);
guiFrame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new CardLayoutTest());
}
}
This is how CarLayout Works, by default, it puts object in horisontal center and vertical top. If you did not specified size of added component, it is resized to smallest size possible.
Configure your layout alignment, set sizes on your components, or use another layout.
I am trying to make a popup over my JFrame in Swing. I have made it so that the popup will be layered over the old JFrame and disable the old one by passing in the JFrame and then .disable(). However, i am also trying to make the frame behind darken to show that it is disabled.
I found this:
stackoverflow - Change brightness of JFrame
But how do i use it to lower the brightness of the JFrame that i have as a parameter just before i disable it? Something like darken(frame) and it lowers it using the function darken(JFrame frame). Thanks!
In fact, I'm going to make my comment an answer:
To show a window over another window, and disable the lower window, make the upper window a modal JDialog, and pass the lower window in as its parent.
One way to dim a top-level window is to get its glass pane, set it visible, and draw a semi-opaque grey color over it.
Here's my test of concept code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Dialog.ModalityType;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class DimView {
protected static final Color GP_COLOR = new Color(0, 0, 0, 30);
private static void createAndShowGui() {
final JFrame frame = new JFrame("DimView");
final JPanel glassPanel = new JPanel() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(GP_COLOR);
g.fillRect(0, 0, getWidth(), getHeight());
};
};
glassPanel.setOpaque(false);
frame.setGlassPane(glassPanel);
JPanel mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(400, 400));
mainPanel.setBackground(Color.pink);
mainPanel.add(new JButton(new AbstractAction("Push Me") {
#Override
public void actionPerformed(ActionEvent evt) {
glassPanel.setVisible(true);
JDialog dialog = new JDialog(frame, "Dialog",
ModalityType.APPLICATION_MODAL);
dialog.add(Box.createRigidArea(new Dimension(200, 200)));
dialog.pack();
dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
glassPanel.setVisible(false);
}
}));
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(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Can someone explain why the following doesn't work as I expect?
Pressing the button 'should' result in the display only containing the (empty) JScrollPane, ie the input field and button should disappear. However they stay until the component is resized...
public static void main(String[] args)
{
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
Container cp = frame.getContentPane();
cp.setLayout(new FlowLayout());
cp.add(new JScrollPane(panel));
Component textField = new JTextField("i am input");
JButton button = new JButton(new AbstractAction("i am pressy")
{
#Override
public void actionPerformed(ActionEvent e)
{
// this is already on the EDT
panel.removeAll();
panel.revalidate();
}
});
panel.setLayout(new FlowLayout());
panel.add(textField);
panel.add(button);
frame.pack();
frame.setVisible(true);
}
Thanks for your help. p.
When updating a visible GUI the code should be:
panel.revalidate();
panel.repaint(); // sometimes needed, this appears to be one of them
The revalidate() method marks components as needing to be laid out, but until something triggers repaint() you won't see any change. Resizing the parent window is one such trigger; switching applications is another. In this previous version, note how setSize() on the panel obviates the need for repaint(). Similarly, this example changes the layout in resetGame().
The article Painting in AWT and Swing goes into more detail.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
/** #see https://stackoverflow.com/questions/5812002 */
public class RevalidateTest {
private static JPanel panel = new JPanel(); // default FlowLayout
private static JTextField text = new JTextField("Text field");
private static JButton clear = new JButton(new AbstractAction("Clear") {
#Override
public void actionPerformed(ActionEvent e) {
panel.removeAll();
panel.add(reset);
panel.revalidate();
panel.repaint();
}
});
private static JButton reset = new JButton(new AbstractAction("Reset") {
#Override
public void actionPerformed(ActionEvent e) {
panel.removeAll();
panel.add(text);
panel.add(clear);
panel.revalidate();
panel.repaint();
}
});
static void createAndShowGUI() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
panel.add(text);
panel.add(clear);
frame.add(panel); // default BorderLayout center
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
You can execute panel.repaint() as specified in the comment by #Jeremy, however, the UI will still change when you resize the window. The reason being that the removal of the elements from the JPanel will cause the panel to resize. A repaint operation will not cause the panel to resize until the JFrame rechecks its layout (as happens on a window resize).
To make sure that the layout is correctly layed out on a change, you can call frame.validate(). This operation will cause the JFrame to revalidate itself and all child components, which is the same operation that is taking place during a window resize event. To execute this method in your code you would need to change JFrame frame to final, i.e.,
final JFrame frame = new JFrame("test");
Hi all im trying to do is add a button to the screen, so when the user clicks on the button a random dice picture is displayed somewhere else on the screen.
Simple
this is my code how I've tried doing it...
although i cant seem to get the buttons to appear with the picture. its either one or the other. what am i doing wrong?
Any help would be appreciated.
also im sure im using far too much code just to get the desired output.
ava.util.Random;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
class MyComponent extends JComponent {
public void paint(Graphics g) {
ImageIcon icon = new ImageIcon("dice1.png");
int x = 0;
int y = 0;
icon.paintIcon(this, g, x, y);
}
}
class Dice extends Panel
{
BufferedImage image;
public Dice(){
JFrame frame = new JFrame("Test");
JPanel panel = new JPanel();
frame.add(panel);
JButton button2 = new JButton("Roll Die");
button2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//execute when button is pressed
Random r= new Random();
System.out.println(r.nextInt(6)+1);
}
});
panel.add(button2);
frame.add(new MyComponent());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
}
public void paint(Graphics g){
g.drawImage(image, 0, 0, 50,50, null);
}
public static void main(String[] args)
{
Random r= new Random();
System.out.println(r.nextInt(6)+1);
new Dice();
}
}
The issue is that the JFrame's contentPane has a BorderLayout by default and BorderLayout adds components to it's CENTER area by default. BorderLayout only supports one component in each of it's regions so when you add both yourJPanel and your custom component to the frame, only one is acutally displayed. The solution is to set the JFrame's contentPane to use another layout(FlowLayout maybe?):
frame.getContentPane().setLayout(new FlowLayout());
or to add one fo the components to another region of the BorderLayout:
frame.getContentPane.add(new MyComponent(), BorderLayout.West);