I am currently making a volume slider for my game. I want to have a toggleable UI where the player can set their preferred volume, then resume playing.
Disclaimer: This is from a group project where everybody is a first timer to making a more complex game, and we're mainly using this project to gain experience. I'm sure this is full of bad practice and inefficiency, but at the end of the day we're making this mostly for fun.
Here's what it should look like:
Here's the minimal reproducible example in 3 classes:
Main
Intermediate
Slider
Main
import javax.swing.*;
import java.awt.*;
public class Main
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
Intermediate inte = new Intermediate(frame);
frame.add(inte);
frame.setPreferredSize(new Dimension(900,600));
frame.pack();
frame.setVisible(true);
}
}
Intermediate
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class Intermediate extends JPanel
{
boolean b = false;
private Timer tim;
private JFrame f;
private Slider s;
public Intermediate(JFrame f)
{
super();
this.f=f;
tim = new Timer(10, new NewFrameListener());
tim.start();
}
public void stop()
{
tim.stop();
}
public void restart()
{
tim.start();
}
public void handleInputs()
{
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "pressed left");
this.getActionMap().put("pressed left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("left");
}
});
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_U, 0, false), "pressed u");
this.getActionMap().put("pressed u", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if(b)
{
restart();
b=false;
}
else
{
s = new Slider();
f.getContentPane().add(s);
f.setVisible(true);
stop();
b=true;
}
}
});
}
#Override
protected void paintComponent(Graphics g)
{
g.setColor(Color.gray);
super.paintComponent(g);
g.fillRect(0,0,900,600);
}
class NewFrameListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ae)
{
// System.out.println("woo");
handleInputs();
repaint();
}
}
}
Slider
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
public class Slider extends JPanel implements ChangeListener {
public Slider()
{
JPanel container = new JPanel();
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 10, 5);
slider.addChangeListener(this);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(1);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setSize(200, 50);
slider.setBounds(200,200, 200, 50);
slider.setEnabled(true);
container.setBackground(Color.GRAY);
container.add(slider);
this.add(container);
}
#Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
if (!source.getValueIsAdjusting()) {
System.out.println(source.getValue());
}
}
}
The JPanel called container in Slider is completely optional, the functionality remains the same, but then the Slider would fill the whole frame.
The expected behaviour is that the visibility of the frame that contains the slider is toggled every time 'u' is pressed. And that does work, until the slider is interacted with. When that happens every input seems to be suppressed and the frame can no longer be hidden. No errors or exceptions thrown when this happens either.
What is causing this behaviour? How can it fixed?
Edit: Added the relevant code of newFrameTimer and NewFrameListener, as well as repaint and paintComponent.
Edit2: I have now tried placing the Slider's JPanel in an other JPanel to be able to set its size, and I can now interact with the outer JPanel without the action listener ceasing to function, but the moment I touch the Slider it breaks again.
Furthermore I tried disabling the change listener portion of the slider, but that still doesn't fix the issue.
Edit3: I have now also recreated this behaviour independently of the game, and the same thing happens. An input causes the slider (or a JPanel containing the slider) to get added to the frame, at this point the the slider can be toggled, using keyboard inputs, but as soon as the slider is interacted with all keyboard input to the outer Panel is lost. I'll try to see if pressing a button or something similar can restore the expected functionality.
The problem is now resolved, thanks to everyone who commented, special thanks to #macrofox #Abra and #Andrew Thompson .
The cause of the issue was that the when the Slider is in focus Intermediate's listeners are basically disabled, so to return to the game I have to disable the Slider's visibility in a KeyListener of its own.
Side note: In order to resume the game in one press of 'u' the input map in Intermediate for 'u' has to be on onKeyRelease = true
here are the 3 classes in their properly working states:
Main
(unchanged)
import javax.swing.*;
import java.awt.*;
public class Main
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
Intermediate inte = new Intermediate(frame);
frame.add(inte);
frame.setPreferredSize(new Dimension(900,600));
frame.pack();
frame.setVisible(true);
}
}
Intermediate
onKeyRelease = true for "u"'s input map
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Intermediate extends JPanel
{
boolean b = false;
private Timer tim;
private JFrame f;
private Slider s;
public Intermediate(JFrame f)
{
super();
this.f=f;
tim = new Timer(10, new NewFrameListener());
tim.start();
}
public void stop()
{
tim.stop();
}
public void restart()
{
tim.start();
}
public void handleInputs()
{
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "pressed left");
this.getActionMap().put("pressed left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("left");
}
});
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_U, 0, true), "pressed u");
this.getActionMap().put("pressed u", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if(b)
{
restart();
b=false;
remove(s);
}
else
{
s = new Slider();
f.getContentPane().add(s);
f.setVisible(true);
stop();
b=true;
}
}
});
}
#Override
protected void paintComponent(Graphics g)
{
g.setColor(Color.gray);
super.paintComponent(g);
g.fillRect(0,0,900,600);
}
class NewFrameListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ae)
{
// System.out.println("woo");
handleInputs();
repaint();
}
}
}
Slider
the slider itself has the whole class Slider added to it as a key listener
now also implements KeyListener, if the key pressed is 'u', visibility of the JPanel is disabled
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Slider extends JPanel implements ChangeListener, KeyListener {
public Slider()
{
JPanel container = new JPanel();
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 10, 5);
slider.addChangeListener(this);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(1);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setSize(200, 50);
slider.setBounds(200,200, 200, 50);
slider.setEnabled(true);
//container.setBackground(Color.GRAY);
//container.add(slider);
slider.addKeyListener(this);
this.add(slider);
}
#Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
if (!source.getValueIsAdjusting()) {
System.out.println(source.getValue());
}
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_U){this.setVisible(false);}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
Related
My objective is to put a button and a circle on the JFrame. When i click the button the circle should move randomly on the panel/frame.
But when i click the button the circle just move once only and after putting SOP statements i found that "frame.repaint()" is getting called multiple times but this call is triggering the "paintComponent" method only once, the very first time (defined in class Panel1). Its very strange!
I have also provided another code which works as expected but has no buttons to trigger the animation. I have read that repaint() requests are coalesced together and executed once, then how come the second program works?
import java.awt.event.*;
import java.awt.Graphics.*;
import javax.swing.*;
import java.awt.*;
public class SimpleGui3c_4 {
public static void main(String args[]) {
Frame1 frame = new Frame1();
frame.go();
}
}
class Frame1 {
JFrame frame;
Panel1 p;
void go() {
frame = new JFrame();
JButton button1 = new JButton("Color Change");
p = new Panel1();
frame.setSize(500,500);
frame.setVisible(true);
frame.getContentPane().add(BorderLayout.SOUTH, button1);
frame.getContentPane().add(BorderLayout.CENTER, p);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
button1.addActionListener(new ColorActionListener());
}
class ColorActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i=0;i<130;i++) {
System.out.println("Frame repaint started");
frame.repaint();
try {
Thread.sleep(5000);
}catch(Exception ex) {}
System.out.println("Frame repaint ended");
}
}
}
class Panel1 extends JPanel {
public void paintComponent(Graphics g) {
System.out.println("Inside the paint Component method");
int x = (int)(Math.random()*100);
int y = (int)(Math.random()*100);
g.setColor(Color.BLUE);
g.fillOval(x,y,100,100);
System.out.println("Exiting the paint component method");
}
}
}
Code which works but has no button to trigger the animation, it works as soon as i run the code. I am not sure why the below program works and the above program fails!
import javax.swing.*;
import java.awt.*;
public class SimpleAnimation {
int x = 70;
int y = 70;
public static void main(String args[]) {
SimpleAnimation gui = new SimpleAnimation();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300,300);
frame.setVisible(true);
for(int i = 0;i<130;i++) {
//x++;
//y++;
drawPanel.repaint();
try {
Thread.sleep(50);
}catch(Exception ex) {}
}
}//close go
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,this.getWidth(), this.getHeight());
int x = (int)(Math.random()*70);
int y = (int)(Math.random()*70);
g.setColor(Color.green);
g.fillOval(x,y,40,40);
}
}
}
I have also provided another code which works as expected but has no buttons to trigger the animation.
The difference between the two pieces of code is context within which they are been called.
The code that "works" is actually been updated out side the context of the Even Dispatching Thread, in the "main" thread, which means that doing something like Thread.sleep won't prevent the UI from been updated.
The code which does not work is been updated from with the content of the Event Dispatching Thread (from within the ActionListener), which is prevent the EDT from processing new paint requests until after the actionPerformed method returns
Another issue you will face relates to when you decide to update the position of the circle.
paintComponent can be called for all a number of different reasons, many which you don't control. Painting should focus on painting the current state and should never modify it (directly or indirectly). Instead, you should use some kind of update method, whose job it is, is to update the x/y position of the circle and trigger a new paint cycle.
I would highly recommend that you stop and take the time to read through:
Concurrency in Swing for a more detail explanation
How to use Swing Timers for a possible solution
Performing Custom Painting and Painting in AWT and Swing for a better understanding into how painting actually works in Swing.
Your problem is rookie mistake which comes about from not understanding how the API actually works and not understanding the tools available to solve it
There are a number of other "issues" which would result in undesirable behaviour, like not calling setVisible last, so the UI doesn't need be updated again to ensure that the components been added are visible.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
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.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleGui3c_4 {
public static void main(String args[]) {
new SimpleGui3c_4();
}
public SimpleGui3c_4() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Frame1 frame = new Frame1();
frame.go();
}
});
}
public interface Animatable {
public void update();
}
public class Frame1 {
JFrame frame;
Panel1 p;
void go() {
frame = new JFrame();
JButton button1 = new JButton("Color Change");
p = new Panel1();
frame.getContentPane().add(BorderLayout.SOUTH, button1);
frame.getContentPane().add(BorderLayout.CENTER, p);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
button1.addActionListener(new ColorActionListener(p));
frame.pack();
frame.setVisible(true);
}
class ColorActionListener implements ActionListener {
private Animatable parent;
public ColorActionListener(Animatable parent) {
this.parent = parent;
}
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
btn.setEnabled(false);
Timer timer = new Timer(5000, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Frame repaint started");
parent.update();
counter++;
if (counter >= 130) {
((Timer)e.getSource()).stop();
btn.setEnabled(true);
}
}
});
timer.setInitialDelay(0);
timer.start();
}
}
class Panel1 extends JPanel implements Animatable {
private int xPos, yPos;
public Panel1() {
update();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Inside the paint component method");
g.setColor(Color.BLUE);
g.fillOval(xPos, yPos, 100, 100);
System.out.println("Exiting the paint component method");
}
#Override
public void update() {
System.out.println("Inside the update method");
xPos = (int) (Math.random() * 100);
yPos = (int) (Math.random() * 100);
repaint();
}
}
}
}
loadingLab=new JLabel("The name is being saved..");
loadPanel.add(loadingLab);
submitBttn=new JButton("Submit");
submitBttn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Submit Button Clicked!!");
try {
//something is wrong in here as it throws an exception
//what is wrong?
frame.setUndecorated(false);
frame.setOpacity(0.55f);
//when above both lines are commented, the code works fine
//but doesnt have transparency
frame.add(loadPanel,BorderLayout.SOUTH);
frame.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
I am trying to display transparent JFrame when "submit" button is clicked which displays panel with a JLabel...
I have tried using setOpacity(0.55f), but it throws exception.. what am i doing wrong?
Unfortunately I think there's no way to keep the system window decoration, you will probably have to go with the default one. Since I'm not 100% sure if you want to toggle the opacity of the whole frame or just the frame's background, I've included both functions in my example. (mKorbels answer help you more if you don't want to have a decoration)
Code:
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
public class TransparentExample extends JFrame {
public TransparentExample() {
super("TransparentExample");
Color defaultBackground = getBackground();
float defaultOpacity = getOpacity();
JToggleButton button1 = new JToggleButton("Toggle background transparency");
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (button1.isSelected()) {
setBackground(new Color(defaultBackground.getRed(), defaultBackground.getGreen(),
defaultBackground.getBlue(), 150));
} else {
setBackground(defaultBackground);
}
}
});
JToggleButton button2 = new JToggleButton("Toggle opacity of whole frame");
button2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispose();
if (button2.isSelected()) {
setOpacity(0.55f);
} else {
setOpacity(defaultOpacity);
}
setVisible(true);
}
});
getContentPane().setLayout(new FlowLayout());
getContentPane().add(button1);
getContentPane().add(button2);
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame.setDefaultLookAndFeelDecorated(true);
TransparentExample frame = new TransparentExample();
frame.setVisible(true);
}
});
}
}
Picture of frame with no togglebutton selected:
Picture of frame with the first togglebutton selected:
Picture of frame with the second togglebutton selected:
#Programmer007 wrote - the exception is "
java.awt.IllegalComponentStateException: The frame is displayable."
please where I can't see any, for more info about the possible exceptions to read,
as mentioned no idea, everything is about your effort, transformed to the SSCCE / MCVE, short, runnable, compilable
.
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class GenericForm extends JDialog {
private static final long serialVersionUID = 1L;
private Timer timer;
private JDialog dialog = new JDialog();
private int count = 0;
public GenericForm() {
dialog.setSize(400, 300);
dialog.setUndecorated(true);
dialog.setOpacity(0.5f);
dialog.setName("Toggling with opacity");
dialog.getContentPane().setBackground(Color.RED);
dialog.setLocation(150, 150);
dialog.setVisible(true);
timer = new javax.swing.Timer(1500, updateCol());
timer.setRepeats(true);
timer.start();
}
private Action updateCol() {
return new AbstractAction("Hello World") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
boolean bol = dialog.getOpacity() < 0.55f;
count += 1;
if (count < 10) {
if (bol) {
dialog.setOpacity(1.0f);
dialog.getContentPane().setBackground(Color.WHITE);
} else {
dialog.setOpacity(0.5f);
dialog.getContentPane().setBackground(Color.RED);
}
} else {
System.exit(0);
}
}
};
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GenericForm();
}
});
}
}
As the title states this is the first time i am using keybinds in an Java application, in fact this is the first time i have heard of them. I can get my game to work using KeyListener but when i use a CardLayout to switch from my menu to the game i ran into all sorts of focusing problem.
Here is the code i have so far:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class GameFrame extends JFrame {
private final String RENDER = "render";
public GameFrame(){
this.setTitle("Snake By Chris Edwards");
this.setVisible(true);
this.setSize(805, 700);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel render = new JPanel();
render.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.VK_LEFT), "left");
render.getActionMap().put("left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
}
});
render.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.VK_UP), "up");
render.getActionMap().put("up", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
}
});
render.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.VK_RIGHT), "right");
render.getActionMap().put("right", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
}
});
render.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.VK_DOWN), "down");
render.getActionMap().put("down", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
}
});
this.add(render);
}
public static void main(String[] args){
new GameFrame();
}
}
The Render panel paints as expected, but none of the key strokes are being registered, e.g. "test" is never printed to the console.
Can anybody see what i am doing wrong? thanks.
You are defining your KeyStrokes incorrectly:
KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.VK_RIGHT)
You should use:
KeyStroke.getKeyStroke(KeyEvent.VK_D, 0)
You're using the wrong InputMap. Change
render.getInputMap(JComponent.WHEN_FOCUSED).put(
to
render.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
and same for the others.
And in the future, please please don't force us to go through a ton of unrelated uncompilable code. That's not very fair to us volunteers, and only makes it much more difficult for us and for you to test and correct the code. Remember: Always test new concepts in isolation.
e.g.,
import java.awt.event.*;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class GameFrame extends JFrame {
private static final String LEFT = "Left";
private static final String UP = "Up";
private final String RENDER = "render";
public GameFrame() {
this.setTitle("Snake By Chris Edwards");
this.setVisible(true);
this.setSize(805, 700);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel render = new JPanel();
render.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), LEFT);
render.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), LEFT);
render.getActionMap().put(LEFT, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(LEFT);
}
});
render.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), UP);
render.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), UP);
render.getActionMap().put(UP, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(UP);
}
});
this.add(render);
}
public static void main(String[] args) {
new GameFrame();
}
}
I'm trying to display a string when a button is pressed, but it does not work. I do not know what the problem is. I get no error, but that does not bother me. I'm missing something fundamental, I suppose. Please help!!
//I'm trying to draw a string in the frame when a button is pressed, but it won't work..
//Can't figure out what the problem is.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class AppletTwo extends JFrame implements ActionListener
{
JFrame frameOne;
JButton btnOne;
AppletTwo()
{
frameOne = new JFrame("frameOne");
frameOne.setSize(320,240);
frameOne.setLayout(new FlowLayout(FlowLayout.LEFT));
frameOne.setVisible(true);
frameOne.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
btnOne = new JButton("Print");
btnOne.addActionListener(this);
frameOne.add(btnOne);
}
public void actionPerformed(ActionEvent ae)
{
if(ae.getSource() == btnOne)
{
repaint();
}
}
public void paint(Graphics g)
{
g.drawString("Never Works",150,150);
}
public static void main(String[] args)
{
AppletTwo frame1 = new AppletTwo();
}
}
" I'm missing something fundamental, I suppose. "
Yes, you are:
Main problem:
Your class is JFrame which is the component for which you are overriding the paint method. But you create another instance of a JFrame, which is the one you setVisible to. Keep in mind, you haven't drawn anything to this frame. So you are seeing the new instance of frame, not the class frame for which you are painting (and for which you never set visible).
Other problems:
You should always call super.paint[Component] after a paint[Component] override
#Override
public void paint(Graphics g) {
super.paint(g);
}
Don't paint on top level container like JFrame. Instead paint on a JPanel or JComponent and override is paintComponent method and call super.paintComponent, then add that component to the frame. See Performing Custom Painting
Swing apps should be run on the event dispatch thread (EDT). You can do so by wrapping your main code in a SwingUtilities.invokeLater(...). See Initial Threads
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
AppletTwo frame1 = new AppletTwo();
}
});
}
Generally, you always want to set the frame visible after adding your components.
Other notes:
See Extends JFrame vs. creating it inside the the program
UPDATE
Example with all the above mentioned points.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
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.JTextField;
import javax.swing.SwingUtilities;
public class SimpleDrawing {
public SimpleDrawing() {
final DrawingPanel panel = new DrawingPanel();
final JTextField field = new JTextField(15);
JButton button = new JButton("Change name");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
String someString = field.getText();
if (!someString.isEmpty()) {
panel.setString(someString);
}
}
});
JPanel bottomPanel = new JPanel();
bottomPanel.add(field);
bottomPanel.add(button);
JFrame frame = new JFrame();
frame.add(panel);
frame.add(bottomPanel, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private String someString = "Stackoverflow";
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(someString, 75, 75);
}
#Override
public Dimension getPreferredSize() {
return new Dimension (300, 100);
}
public void setString(String someString) {
this.someString = someString;
repaint();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new SimpleDrawing();
}
});
}
}
I am dealing with CardLayout. The JPanel I added as a contentpane to my JFrame has a CardLayout, and I want to swap between different panes. I have a working pane with buttons and five other image panes for the tutorial that are to be displayed only if a certain boolean value is true. I mean, every time this boolean is set true, five swaps should be done using next() method. My problem is that, after the first swap, the screen becomes blank. why does this happen?
Second question. I am using a MouseListener to swap, but I would like the program to do it automatically after some time. I tried to use Thread.sleep(5000), but I get a black screen.
This is my code, where card is a class variable in order to use it in the Mouselistener, parent is the working panel, already created, and ImagePanel is a class to create tutorialPanels, which adds to them the MouseListener below. Also, rootPane is a class pane.
card = new CardLayout();
rootPane = new JPanel(card);
this.getContentPane().add(rootPane);
//create panels to add
ImagePanel inputTutorial = new ImagePanel("backgroundIn.png");
ImagePanel numericTutorial = new ImagePanel("backgroundNum");
ImagePanel outputTutorial = new ImagePanel("backgroundOut");
ImagePanel commandTutorial = new ImagePanel("backgroundCom");
ImagePanel errorTutorial = new ImagePanel("backgroundErr");
ImagePanel finalTutorial = new ImagePanel("backgroundFinal");
//add the panels
rootPane.add(parent);
rootPane.add(inputTutorial);
rootPane.add(numericTutorial);
rootPane.add(outputTutorial);
rootPane.add(commandTutorial);
rootPane.add(errorTutorial);
rootPane.add(finalTutorial);
//set rootPane content panel
this.getContentPane().add(rootPane);
//if the boolean is true
if (firstTime == true) {
card.next(rootPane);
//other swaps done by mouselisteners
}
This is the mouselistener:
//mouse click listener
private class MouseActionListener implements MouseListener {
public void mousePressed (MouseEvent e) {
}
#Override
public void mouseClicked(MouseEvent arg0) {
card.next(rootPane);
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
I know that the listener is executed because I checked it.
Any help would be appreciated, thank you in advance.
"but I would like the program to do it automatically after some time. I tried to use Thread.sleep(5000)"
Don't use Thread.sleep. Instead use a javax.swing.Timer. You can learn more at How to Use Swing Timers
Here's a simple example, using some of your app format.
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
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.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SlideShow {
public SlideShow() {
final CardLayout layout = new CardLayout();
final JPanel mainPanel = createMainPanel(layout);
Timer timer = new Timer(1000, new ActionListener(){
public void actionPerformed(ActionEvent e) {
layout.next(mainPanel);
}
});
timer.start();
JFrame frame = new JFrame();
frame.add(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel(CardLayout layout) {
JPanel panel = new JPanel(layout);
panel.add(new ImagePanel("mario.png"));
panel.add(new ImagePanel("bowser.png"));
panel.add(new ImagePanel("luigi.png"));
panel.add(new ImagePanel("koopa.png"));
panel.add(new ImagePanel("princess.png"));
return panel;
}
private class ImagePanel extends JPanel {
BufferedImage image;
public ImagePanel(String fileName) {
try {
image = ImageIO.read(getClass().getResource("/marioblobs/" + fileName));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(200, 200)
: new Dimension(image.getWidth(), image.getHeight());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SlideShow();
}
});
}
}