I am trying to make a JComponent application which uses two JFrames, one frame with alterable sliders and textfields for the graphical display of a firework on the second. When the "fire" button is pressed, a rendering of the firework should appear. However, I have found through placing strategic print statements, that my paintComponent() method does not run even though the conditional statement wrapping the code is satisfied. I have also double checked all of my other methods to ensure that correct values are generated at the correct times. After looking through all of the JComponent literature and questions I could find, I'm afraid I cannot get it to work - this problem is most likely derived from my lack of familiarity with the library. That being said, any advice no matter how rudimentary, will be much appreciated. Abridged code is below:
*The swing timer may also be the issue for I am not sure if I have used it correctly
[fireworksCanvas.java]
public class fireworkCanvas extends JComponent implements ActionListener{
private static final long serialVersionUID = 1L;
private ArrayList<Ellipse2D> nodes = new ArrayList<Ellipse2D>();
private ArrayList<Line2D> cNodes = new ArrayList<Line2D>();
private ArrayList<QuadCurve2D> bCurves = new ArrayList<QuadCurve2D>();
private int[] arcX;
private int[] arcY;
private Color userColor;
private Random rand = new Random();
private int shellX, shellY, fType, theta, velocity;
private Timer timer;
private int time;
private double g = -9.8; //gravity in m/s
public boolean explosivesSet;
public fireworkCanvas() {
time = rand.nextInt(3000) + 2000;
timer = new Timer(time, this); // 5 seconds
timer.start();
fType = 0;
}
#Override
public void paintComponent(Graphics g){
if (explosivesSet) {
System.out.println("fType" + fType);
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
g.setColor(Color.BLACK);
g.drawPolyline(arcX, arcY, arcX.length);
for (Ellipse2D e : nodes) {
System.out.println("painting nodes"); // NEVER PRINTS
g.setColor(userColor);
g.fillOval(shellX + (int) e.getX(), shellY + (int) e.getY(), (int) e.getWidth(), (int) e.getHeight());
}
for (Line2D l: cNodes) {
System.out.println("painting cNodes"); // NEVER PRINTS
g.setColor(determineColor("l"));
g.drawLine(shellX + (int) l.getX1(), shellY + (int) l.getY1(), shellX + (int) l.getX2(), shellY + (int) l.getY2());
}
for (QuadCurve2D c: bCurves) {
System.out.println("painting curves"); // NEVER PRINTS
g.setColor(determineColor("c"));
g2D.draw(c);
}
}
}
public Color determineColor(String type) {
// returns color
}
public void setExplosives() {
if (fType != 5 && fType != 0) {
nodes.clear(); // clears three array lists with FW components
cNodes.clear(); // these are the components to paint for the
bCurves.clear(); // firework explosion graphic
setArc(); // stores path of shell for a polyLine to be drawn
// builds and generates components for FW based on type chosen (fType)
setExplosivesSet(true);
repaint();
}
}
public void setArc() {
// builds int[] for shellX, shellY
}
#Override
public void actionPerformed(ActionEvent e) {
// nothing is here??
// should I use the action performed in some way?
}
[GUI.java]
public class GUI extends JFrame implements ActionListener, ChangeListener, ItemListener, MouseListener{
private static JFrame canvasFrame = new JFrame("Canvas");
private fireworkCanvas canvas = new fireworkCanvas();
private Choice fireworkChooser = new Choice();
private JSlider launchAngle = new JSlider();
private JSlider velocity = new JSlider();
private JSlider r = new JSlider();
private JSlider g = new JSlider();
private JSlider b = new JSlider();
private JPanel panel = new JPanel();
private JButton button = new JButton("Fire!");
private JLabel launchLabel = new JLabel("Launch Angle ");
private JLabel velocityLabel = new JLabel("Velocity ");
private JLabel rLabel = new JLabel("Red ");
private JLabel gLabel = new JLabel("Green ");
private JLabel bLabel = new JLabel("Blue ");
public static int fHeight = 500;
public static int fWidth = 500;
public GUI() {
this.add(panel);
panel.add(button);
panel.add(fireworkChooser);
panel.add(launchAngle);
panel.add(launchLabel);
panel.add(velocity);
panel.add(velocityLabel);
panel.add(r);
panel.add(rLabel);
panel.add(g);
panel.add(gLabel);
panel.add(b);
panel.add(bLabel);
addActionListener(this);
BoxLayout bl = new BoxLayout(getContentPane(), BoxLayout.Y_AXIS);
setLayout(bl);
fireworkChooser.addItemListener(this);
launchAngle.addChangeListener(this);
velocity.addChangeListener(this);
r.addChangeListener(this);
g.addChangeListener(this);
b.addChangeListener(this);
button.addActionListener(this);
fireworkChooser.add("Firework 1");
fireworkChooser.add("Firework 2");
fireworkChooser.add("Firework 3");
fireworkChooser.add("Firework 4");
fireworkChooser.add("Super Firework");
launchAngle.setMinimum(1);
launchAngle.setMaximum(90);
velocity.setMinimum(1);
velocity.setMaximum(50);
r.setMinimum(0);
r.setMaximum(255);
g.setMinimum(0);
g.setMaximum(255);
b.setMinimum(0);
b.setMaximum(255);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 200);
}
#Override
public void stateChanged(ChangeEvent e) {
// sets FW variables
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
canvas.setfType(fireworkChooser.getSelectedIndex()+1);
canvas.setExplosives();
canvas.repaint();
canvas.setExplosivesSet(false);
System.out.println("button fired");
}
}
public static void createAndShowGUI() {
GUI gui = new GUI();
gui.pack();
gui.setLocationRelativeTo(null);
gui.setVisible(true);
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fireworkCanvas canvas = new fireworkCanvas();
canvasFrame.pack();
canvasFrame.add(canvas);
canvasFrame.setLocationRelativeTo(null);
canvasFrame.setVisible(true);
canvasFrame.setSize(fWidth, fHeight);
canvasFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
First of all:
public fireworkCanvas()
Class names should start with an upper case character. All the other classes in your code follow this rule. Learn by example.
private Choice fireworkChooser = new Choice();
Choice is an AWT component don't mix AWT components in a Swing application. Use a JComboBox.
that my paintComponent() method does not run
fireworkCanvas canvas = new fireworkCanvas();
canvasFrame.pack();
canvasFrame.add(canvas);
You add the canvas to the frame AFTER you pack() the frame, so the size of the canvas is (0, 0) and there is nothing to paint.
The canvas should be added to the frame BEFORE the pack() and you should implement getPreferredSize() in your FireworkCanvas class so the pack() method can work properly.
Read the section from the Swing tutorial on Custom Painting for the basics and working examples to get you started.
Related
I am trying to make a little program that includes changing the color of a Panel time-based.
Right now I am just trying to do that part without the rest. So I just wrote a little Interface with only one panel and I want to change the color within a loop multiple times.
The problem is, even though the thread pauses for the correct amount of time, the color of the Panel doesn't change correctly. It changes just sometimes in the loop not every time.
my Interface Class:
import javax.swing.*;
import java.awt.*;
//creates the Interface
public class Interface extends JFrame {
private JPanel frame1;
public Interface (String titel) {
super(titel);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame1 = new JPanel();
this.frame1.setPreferredSize(new Dimension (200, 200));
setLayout(new FlowLayout());
add(frame1);
this.setVisible(true);
}
public JPanel getFrame1() {
return frame1;
}
}
my Pause Class:
import java.util.TimerTask;
//supposed to pause the thread by #pause amount of milliseconds
public class Pause extends TimerTask {
private int pause;
public Pause(int pause){
this.pause = pause;
}
#Override
public void run() {
System.out.println("Timer"+ pause+" task started at:"+System.currentTimeMillis());
pause();
System.out.println("Timer task"+ pause+" ended at:"+System.currentTimeMillis());
}
public void pause() {
try {
Thread.sleep(this.pause);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
my Blink Class
import javax.swing.*;
import java.awt.*;
public class Blink {
private JPanel frame1;
public Blink(Interface anInterface){
this.frame1 = anInterface.getFrame1();
}
// blink should change the color of the JPanel inside my Frame.
// Its supposed to change to red for 200 ms
// and then to white again for 1000 ms.
// this should be repeated 10 times.
public void blink() {
Pause pause1 = new Pause(200);
Pause pause2 = new Pause(1000);
pause2.run();
int i = 1;
while(i <= 10){
i++;
frame1.setBackground(Color.red);
frame1.repaint();
pause1.run();
frame1.setBackground(Color.white);
frame1.repaint();
pause2.run();
}
}
public static void main ( String[] args ) {
Interface anInterface = new Interface("Title");
anInterface.setVisible(true);
Blink blink = new Blink(anInterface);
blink.blink();
}
}
According to Concurrency to Swing you cannot simply Thread.sleep the Thread where the GUI runs because it will freeze it, hence events cannot take place. Instead, for any kind of animation or long-heavy task (consider Thread.sleep as one), Swing Timers and Swing Workers should be used. In your case, a javax.swing.Timer fits better.
One example of its usage:
public class Blink {
private JPanel frame1;
private int pause1TimesRan;
private int pause2TimesRan;
private Timer pauser1, pauser2;
public Blink(Interface anInterface) {
this.frame1 = anInterface.getFrame1();
//Create pauser 1 with delay 200ms
pauser1 = new Timer(200, e -> {
if (pause1TimesRan == 10) {
pauser1.stop();
return;
}
Color color = randomColor();
frame1.setBackground(color);
System.out.println("Pauser #1 changed background to: " + color);
pause1TimesRan++;
});
//Create pauser 2 with delay 1000ms
pauser2 = new Timer(1000, e -> {
if (pause2TimesRan == 10) {
pauser2.stop();
return;
}
Color color = randomColor();
frame1.setBackground(color);
System.out.println("Pauser #2 changed background to: " + color);
pause2TimesRan++;
});
}
private static Color randomColor() {
return new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
}
public void blink() {
pauser1.start();
pauser2.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Interface anInterface = new Interface("Title");
anInterface.setVisible(true);
Blink blink = new Blink(anInterface);
blink.blink();
});
}
static class Interface extends JFrame {
private JPanel frame1;
public Interface(String titel) {
super(titel);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame1 = new JPanel();
this.frame1.setPreferredSize(new Dimension(200, 200));
setLayout(new FlowLayout());
add(frame1);
this.setVisible(true);
}
public JPanel getFrame1() {
return frame1;
}
}
}
One off-topic advice is to name your methods (and variables) properly. You called the method getFrame1(), but it is actually a JPanel and not a JFrame. So, a better name could be getPanel(). Also, about the SwingUtilities.invokeLater part, read What does SwingUtilities.invokeLater does.
I wonder why the repiant() method is not working as intended any more ... Ex :
public class Main extends JPanel implements ActionListener, MouseListener,MouseMotionListener{
private ArrayList<Node> nodes;
private ArrayList<Edge> edges;
private boolean AddNode;
private int no_Of_Nodes;
private int width = 30, height = 30;
public static void main(String[] args){
Main M = new Main();
M.Start();
}
public void Start() {
nodes = new ArrayList<Node>();
edges = new ArrayList<Edge>();
JFrame f = new JFrame("SFG");
JPanel main_Panel = new JPanel(new BorderLayout());
JPanel buttons = new JPanel();//Buttons Containser
JPanel draw = new JPanel();
ArrayList<JButton> bs = new ArrayList<JButton>();
JButton b1 = new JButton("Add Node");
b1.addActionListener(new Add_Node());
JButton b2 = new JButton("Add Edge");
b2.addActionListener(new Add_Edge());
JButton b3 = new JButton("Add Arc");
b3.addActionListener(new Add_Arc());
JButton b4 = new JButton("Clear all");
b4.addActionListener(new Clear());
JButton b5 = new JButton("Solve");
b5.addActionListener(new Solve());
Bs.add(b1);
Bs.add(b2);
Bs.add(b3);
Bs.add(b4);
Bs.add(b5);
for (int i = 0; i < bs.size(); i++) {
Buttons.add(bs.get(i));
}
Buttons.setBackground(Color.GRAY);
main_Panel.add(Buttons,BorderLayout.SOUTH);
draw.setBackground(Color.darkGray);
draw.addMouseMotionListener(this);
draw.addMouseListener(this);
main_Panel.add(Draw);
main_Panel.setBackground(Color.GRAY);
f.add(main_Panel);
f.setSize(1024, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
And these are methods
public void actionPerformed(ActionEvent arg0) {
this.repaint();
}
public class Add_Node implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Add Node");
addNode = true;
}
}
Now here when I add node and call repaint nothing appears in the paint area :
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
if(addNode){
addNode(arg0);
addNode = !addNode;
}
System.out.println(nodes.size());
this.repaint();
}
private void addNode(MouseEvent arg0) {
// TODO Auto-generated method stub
int x = arg0.getX();
int y = arg0.getY();
Node n = new Node(No_Of_Nodes++);
n.setX_Pos(X);
System.out.println(x + " " + y);
n.setX_Pos(Y);
nodes.add(n);
}
And that's my paint() method which is not working anymore
public void paint(Graphics g){
super.paintComponents(g);
FontMetrics f = g.getFontMetrics();
int nodeHeight = Math.max(height, f.getHeight());
System.out.println("In repaint");
for (Node n : nodes) {
System.out.println(n.getX_Pos() + " " + n.getY_Pos());
int nodeWidth = Math.max(width, f.stringWidth(Integer.toString(n.getNode_ID()))+width/2);
g.setColor(Color.white);
g.fillOval(n.getX_Pos()-nodeWidth/2, n.getY_Pos()-nodeHeight/2, nodeWidth, nodeHeight);
g.setColor(Color.black);
g.drawOval(n.getY_Pos()-nodeWidth/2, n.getY_Pos()-nodeHeight/2, nodeWidth, nodeHeight);
g.drawString(Integer.toString(n.getNode_ID()) , n.getX_Pos()-f.stringWidth(Integer.toString(n.getNode_ID()))/2, n.getY_Pos()+f.getHeight()/2);
}
}
TIA and sorry for long question :)
In your Start method, you never actually add Main to the JFrame. Based on the out of context snippets of code you've provided, I'm left to "assume" that you are overriding paint of the Main class, this means, paint will NEVER be called as it's not actually attached to a displayable component
Start shouldn't be creating a JFrame. You should create an instance of Main and then add it to an instance of a JFrame, the two should be separate. The for Main should be created and added to Main itself
You shouldn't override paint (as a general rule), but you should defiantly NOT circumvent the painting process by calling one of the other paint methods, like paintComponents. Instead (again "assuming" you're overriding paint in Main), you should override the paintComponent method and call super.paintComponent BEFORE you do any custom painting
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details.
Also, you might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
I have a Java program with two textareas and a button. I want to allow the user to write one one textarea using a touch pen or mouse and when he/she clicks the button, the drawn content should be send to textarea no 2.
So the textarea where the user is writing on, I gave a mousemotion listener with paintComponent method in it.
When I run the application, I have realized that texarea method getText() and setText() can't set or get the drawn content.
Is there way I can achieve the above task? I have also tried JPanel but it doesn't have the setContent() method.
I appreciate any help.
This is my first textarea where user is writing on with touchpen.
public class Area1 extends JTextArea {
int pointcount = 0;
Point[] points = new Point[10000];
public Area1() {
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent event) {
if (pointcount < points.length) {
points[pointcount] = event.getPoint();
++pointcount;
repaint();
}
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < pointcount; i++) {
g.fillOval(points[i].x, points[i].y, 4, 4);
}
}
}
this is my overall application with textarea2 and button
public class Handwriting extends JFrame {
private JTextArea area2;
private JButton b1;
Area1 area1;
public Handwriting() {
Box box = Box.createHorizontalBox();
area1 = new Area1();
area1.setRows(30);
area1.setColumns(30);
area2 = new JTextArea(30, 30);
b1 = new JButton("Copy");
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == b1) {
area2.setText(area1.getText());
}
}
});
box.add(area1);
box.add(b1);
box.add(area2);
add(box);
}
public static void main(String[] args) {
Handwriting hand = new Handwriting();
hand.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
hand.setSize(500, 500);
hand.setVisible(true);
}
}
One possible solution would be to make one class as a Inner Class of the other , that way even private instance fields can be accessed
I followed steps from many articles but the undo system not behaving as expected. first, the undo button goes back correctly till the first edit but can't go beyond the first edit. also, when I click undo and then re-edit it circles back to the last edit. It's better if you execute the code and see yourself.
Another question, while I'm trying to figure out a solution I read that saving images in an Array is memory intensive, is that true even for this simple paint class, and what is the alternative? saving the image's graphic?
import java.awt.*;import java.awt.image.BufferedImage;import java.awt.event.*;import java.util.ArrayList;import javax.swing.*;import javax.swing.event.*;import javax.swing.undo.*;
public class Painter extends JFrame{
//attributes//
Painter.Canvas canvas;
JPanel controlPanel;
JButton undoButton;
JButton redoButton;
PainterHandler handler;
Container container;
//undo system elements//
UndoManager undoManager; // history list
UndoableEditSupport undoSupport; // event support
//constructor//
public Painter()
{
super("Painter-test");
controlPanel = new JPanel();
undoButton = new JButton("undo");
redoButton = new JButton("redo");
handler = new Painter.PainterHandler();
container = getContentPane();
canvas = new Painter.Canvas();
this.organizer();
}// end constructor
public void organizer()
{
controlPanel.setLayout(null);
controlPanel.setPreferredSize(new Dimension(120,350));
controlPanel.setBackground(null);
//add undo listeners to undo/redo buttons.
undoButton.addActionListener( new AbstractAction()
{
public void actionPerformed( ActionEvent evt )
{ undoManager.undo(); refreshCanvas(); refreshUndoRedo();}
});
redoButton.addActionListener(new AbstractAction()
{
public void actionPerformed(ActionEvent evt )
{ undoManager.redo(); refreshCanvas(); refreshUndoRedo(); }
});
// initilize the undo/redo system.
undoManager= new UndoManager();//history list
// event support, instance.
undoSupport = new UndoableEditSupport();
//add undoable edit listener to the support instance.
undoSupport.addUndoableEditListener(new UndoableEditListener()
{
public void undoableEditHappened (UndoableEditEvent event)
{
UndoableEdit edit = event.getEdit();
undoManager.addEdit( edit );
refreshUndoRedo();
}
});
refreshUndoRedo();
canvas.setPreferredSize(new Dimension(600,400));
//place buttons on panel.
undoButton.setBounds(10, 160, 80, 20);
redoButton.setBounds(10, 181, 80, 20);
//add components to panel.
controlPanel.add(undoButton);
controlPanel.add(redoButton);
//add panels to window.
container.add(canvas,BorderLayout.WEST);
container.add(controlPanel, BorderLayout.EAST);
}//end organizerTab3()
public void refreshCanvas() { canvas.repaint(); }
//refresh undo, redo buttons.
public void refreshUndoRedo()
{
// refresh undo
undoButton.setEnabled( undoManager.canUndo() );
// refresh redo
redoButton.setEnabled( undoManager.canRedo() );
}
//INNER CLASSES
ArrayList<BufferedImage> imagesArray = new ArrayList<BufferedImage>();
BufferedImage imageCopy;
Graphics graphics;
BufferedImage image;
int index;
private class Canvas extends JPanel
{
public Canvas()
{
//Panel properties
setSize(600,400);
setBackground(new Color(84,84,118));
image = new BufferedImage(getWidth(), getHeight(),BufferedImage.TYPE_INT_ARGB);
//add Listeners.
addMouseMotionListener(handler);
addMouseListener(handler);
}//end constructor
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
}//end inner class PaintPanel
private class PainterHandler extends MouseAdapter
{
#Override
public void mouseDragged(MouseEvent event)
{
if(event.getComponent().equals(canvas))
{
if (image != null)
{
// Paint into the image
graphics = image.getGraphics();
graphics.setColor(new Color(249,30,138));
graphics.fillOval(event.getX(), event.getY(), 20, 20);
imageCopy = new BufferedImage(canvas.getWidth(),canvas.getHeight(),BufferedImage.TYPE_INT_ARGB);
imageCopy.getGraphics().drawImage(image, 0, 0, null);
canvas.repaint();
}
}
}//end mouseDragged(MouseEvent event)
#Override
public void mouseReleased(MouseEvent event)
{
if(event.getComponent().equals(canvas))
{
//UNDO SYSTEM START//
//add image to the array.
imagesArray.add(imageCopy);
// get image's index.
index = imagesArray.indexOf(imageCopy);
//create AddEdit instance of type UndoableEdit.
UndoableEdit edit = new Painter.AddEdit(imagesArray, imageCopy,index);
// notify the listeners
undoSupport.postEdit( edit );
//UNDO SYSTEM END//
}
}
}//end MouseHandler class
private class AddEdit extends AbstractUndoableEdit
{
private ArrayList<BufferedImage> undoableImagesArray;
private BufferedImage undoableImage;
int undoableIndex;
public AddEdit(ArrayList<BufferedImage> v, BufferedImage img, int i)
{
undoableImagesArray = v;
undoableImage = img;
undoableIndex = i;
}
public void undo() throws CannotUndoException
{
undoableImagesArray.remove(undoableImage);
if(!undoableImagesArray.isEmpty())
image = (BufferedImage)undoableImagesArray.get(undoableImagesArray.size()-1);
canvas.repaint();
}
public void redo() throws CannotRedoException
{
undoableImagesArray.add(undoableImage);
image = (BufferedImage)undoableImagesArray.get(undoableImagesArray.size()-1);
canvas.repaint();
}
public boolean canUndo() { return true; }
public boolean canRedo() { return true; }
}//end class AddEdit
public static void main(String[] s)
{
Painter p = new Painter();
p.setSize(800, 500);
p.setVisible(true);
p.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}//END CLASS Painter
You don't use undoableIndex. In fact you should not remove the image from array but instead move the pointer.
canUndo() should return true if the list is not empty and the pointer >0
canRedo() should return true if the pointer!= size() of the list
I'm new to java, and have been working on creating a code to get a small picture of a car to move using the keys. My problem is when I add more than 1 buttons to the panel. The function of the button in the code I post is nothing, just prints "button [i] clicked" message. The purpose is for them to read a file and update the locations of the car from the data in the file. This is supposed to be a piece on my Reinforcement Learning project. I thought it would be a good opportunity to learn java since this "graphics package" is not necessary for the project, just a "nice" addition. The code is here:
package graphics;
public class Board extends JPanel implements ActionListener {
private Timer timer;
private Agent agent;
private String button = "button.png";
private Image image;
protected JButton b1;
protected JButton b2;
protected JButton b3;
public Board() {
//Keylistener added for the agent to respond to arrow keys
addKeyListener(new TAdapter());
agent = new Agent();
timer = new Timer(10, this); //10ms timer calls action performed
timer.start();
//This part for the button.
ImageIcon i = new ImageIcon(this.getClass().getResource(button));
image = i.getImage();
b1 = new JButton("1", i);
b1.setVerticalTextPosition(AbstractButton.CENTER);
b1.setHorizontalTextPosition(AbstractButton.LEADING);
b1.setActionCommand("Active1");
b2 = new JButton("2", i);
b2.setVerticalTextPosition(AbstractButton.CENTER);
b2.setHorizontalTextPosition(AbstractButton.LEADING);
b2.setActionCommand("Active2");
b3 = new JButton("3", i);
b3.setVerticalTextPosition(AbstractButton.CENTER);
b3.setHorizontalTextPosition(AbstractButton.LEADING);
b3.setActionCommand("Active3");
b1.addActionListener(this);
b2.addActionListener(this);
b3.addActionListener(this);
add(b1); add(b2); add(b3);
setFocusable(true);
setBackground(Color.BLACK);
setDoubleBuffered(true);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
//Transformations for the agent to be painted based upon its position and orientation
AffineTransform trans = new AffineTransform();
trans.rotate(Math.toRadians(agent.getTh()), agent.getX()+64, agent.getY()+64);
trans.translate(agent.getX(), agent.getY());
g2d.drawImage(agent.getImage(), trans, this); // Draws agent with said transformations
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void actionPerformed(ActionEvent ae) {
b1.setEnabled(true);
b2.setEnabled(true);
b3.setEnabled(true);
if (ae.getActionCommand()=="Active1") {
b1.setEnabled(false);
b2.setEnabled(true);
b3.setEnabled(true);
System.out.println("Clicked 1");
agent.reset();
}
if(ae.getActionCommand()=="Active2") {
b1.setEnabled(true);
b2.setEnabled(false);
b3.setEnabled(true);
System.out.println("Clicked 2");
agent.reset();
}
if (ae.getActionCommand()=="Active3") {
b1.setEnabled(true);
b2.setEnabled(true);
b3.setEnabled(false);
System.out.println("Clicked 3");
agent.reset();
}
agent.move();
repaint();
}
private class TAdapter extends KeyAdapter {
public void keyReleased(KeyEvent e) {
agent.keyReleased(e);
}
public void keyPressed(KeyEvent e) {
agent.keyPressed(e);
}
}
}
Now, the problem is this. If I click button 1, or button 2, the other buttons are disabled and it says "Clicked 1 (or 2)" which is fine. But the agent.move() and repaint() aren't invoked. The car doesn't move when I press the keys. If I then click button 3, the other two buttons are disabled and the car moves with the keys.
If I add buttons in a different order add(b3); add(b2); add(b1); then the same happens but this time its button 1 that works fine.
Problems:
Your main problem is one of focus -- When a JButton gets focus and the JPanel loses focus, the JPanel's KeyListener won't work, since the KeyListener requires that the listened to component have the focus, no exception.
A bad solution is to force the JPanel to retain the focus at all times. This will be bad if your window has JButtons and will be disaster if you need to display JTextFields, JTextAreas, or other text components.
A better solution is not to use a KeyListener as it has lots of problems with Swing applications and in particular it has focus issues as noted above. Use Key Bindings instead. Google the tutorial for the gory details on this.
Don't use == to compare Strings, use equals(...) or equalsIgnoreCase(...) instead. The problem is that == checks for object equality, is String A the same object as String B, and you don't care about this. You want to know if the two Strings hold the same chars in the same order which is where the two methods come in.
Don't dispose() of a Graphics object given you by the JVM as this may mess up the painting of a component's borders, children, and have even other side effects.
don't draw in the JPanel's paint(...) method but rather in its paintComponent(...) method just as the tutorials tell you to do. Drawing in paint(...) can have side effects on a component's borders and children if you're not careful, and also does not have the benefit of default double buffering which is important for smooth animation. paintComponent(...) fixes all these problems.
Speaking of which, you should Google and read the Swing graphics tutorials. You can't make stuff up and hope that it will work, and graphics programming will require a whole different approach from what you're used to.
Ignore Andromeda's threading suggestion. While he means well, I suggest that you don't do painting in a background thread. Just move the car with your Swing Timer as you're doing. Background threading has its uses, but not here, as a Timer will work just fine. You will need to use a background thread when you have a long-running process that blocks the calling thread, something that you don't have in your current code, and so a thread "fix" is not needed and in fact is a potential source of problems if you don't take extreme care to make your Swing calls on the Swing event thread. The unknown for us though is what your "agent" is doing. If it is calling long-running code or code with Thread.sleep(...) or wait()/notify() in it, then yes, you will need to use background threading.
But again, we know that's not your primary problem, since your problems began only after adding focus grabbers -- the JButtons. Again this is strong indication that your primary problem is not threading but is use of KeyListeners and their focus requirement.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.*;
import java.util.EnumMap;
import javax.swing.*;
#SuppressWarnings("serial")
public class KeyBindingPanel extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private static final Stroke THICK_STROKE = new BasicStroke(5f,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
private static final int OVAL_WIDTH = 30;
private static final int OVAL_HEIGHT = 30;
private static final Color OVAL_COLOR = Color.red;
private static final Color BKGRD_COLOR = Color.black;
private static final int TIMER_DELAY = 20;
public static final int STEP = 2;
private int myX = 0;
private int myY = 0;
private JButton[] buttons = new JButton[3];
private int condition = WHEN_IN_FOCUSED_WINDOW;
private InputMap inputMap = getInputMap(condition);
private ActionMap actionMap = getActionMap();
private EnumMap<Direction, Boolean> directionMap = new EnumMap<Direction, Boolean>(
Direction.class);
public KeyBindingPanel() {
for (int i = 0; i < buttons.length; i++) {
buttons[i] = new JButton(new ButtonAction());
add(buttons[i]);
}
setBackground(BKGRD_COLOR);
for (final Direction direction : Direction.values()) {
directionMap.put(direction, Boolean.FALSE);
Boolean[] onKeyReleases = { Boolean.TRUE, Boolean.FALSE };
for (Boolean onKeyRelease : onKeyReleases) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(
direction.getKeyCode(), 0, onKeyRelease);
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), new DirAction(direction,
onKeyRelease));
}
}
new Timer(TIMER_DELAY, new GameTimerListener()).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2b = (Graphics2D) g.create();
g2b.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2b.setStroke(THICK_STROKE);
g2b.setColor(OVAL_COLOR);
g2b.drawOval(myX, myY, OVAL_WIDTH, OVAL_HEIGHT);
g2b.dispose(); // since I created this guy
}
private class GameTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (Direction direction : Direction.values()) {
if (directionMap.get(direction)) {
myX += STEP * direction.getRight();
myY += STEP * direction.getDown();
}
}
repaint();
}
}
private class DirAction extends AbstractAction {
private Direction direction;
private boolean onRelease;
public DirAction(Direction direction, boolean onRelease) {
this.direction = direction;
this.onRelease = onRelease;
}
#Override
public void actionPerformed(ActionEvent evt) {
directionMap.put(direction, !onRelease); // it's the opposite!
}
}
private class ButtonAction extends AbstractAction {
public ButtonAction() {
super("Press Me!");
}
#Override
public void actionPerformed(ActionEvent e) {
JButton thisBtn = (JButton) e.getSource();
for (JButton btn : buttons) {
if (btn == thisBtn) {
btn.setEnabled(false);
} else {
btn.setEnabled(true);
}
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("KeyBindingPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new KeyBindingPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum Direction {
UP(KeyEvent.VK_UP, -1, 0), DOWN(KeyEvent.VK_DOWN, 1, 0), LEFT(
KeyEvent.VK_LEFT, 0, -1), RIGHT(KeyEvent.VK_RIGHT, 0, 1);
private int keyCode;
private int down;
private int right;
private Direction(int keyCode, int down, int right) {
this.keyCode = keyCode;
this.down = down;
this.right = right;
}
public int getKeyCode() {
return keyCode;
}
public int getDown() {
return down;
}
public int getRight() {
return right;
}
}
It seems you need to use Threading. You can make your class to implements Runnable and then override the public void run(){} method and do the painting there.