So I am making a space invaders clone. Originally I had no problem getting my game to work with a simple main class that created the frame, created the gameplay and started the thread.
But then I tried to implement a start menu and it all went to crap. The menu appears with success but the gameplay does not appear when I press start.
I am running out of ideas and I am completely stumped. I am somewhat new as well to SO, so if there is anything I left out, I appreciate any help.
Here is the original with no menu that worked fine:
public static void main(String[] args) {
JFrame frame = new JFrame("SpaceRaiders");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Gameplay gameplay = new Gameplay();
frame.add(gameplay);
frame.setVisible(true);
Thread t1 = new Thread(gameplay);
t1.start();
}
However, the moment I tried to implement a menu to then play the game, I am running into all sorts of trouble. I created a UI class as well as an actual "game" class like so:
public class UI {
JFrame frame, f2;
JPanel titrePanel, startButtonPanel, loadButtonPanel, p2;
JLabel nomJeu;
JButton startButton, loadButton;
Font fontTitre, fontStart;
Gameplay gameplay;
public void createUI(ChoixJeu cj) {
frame = new JFrame("SpaceRaiders");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setLayout(null);
frame.getContentPane().setBackground(Color.black);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//------------------ECRAN MENU---------------------
//Titre
titrePanel = new JPanel();
titrePanel.setBounds(100, 100, 400, 100);
titrePanel.setBackground(Color.BLUE);
Font fontTitre = new Font("Times New Roman", Font.BOLD, 50);
Font fontStart = new Font("Times New Roman", Font.PLAIN, 20);
nomJeu = new JLabel("SpaceRaiders");
nomJeu.setForeground(Color.white);
nomJeu.setFont(fontTitre);
titrePanel.add(nomJeu);
//Start button
startButtonPanel = new JPanel();
startButtonPanel.setBounds(200, 400, 200, 40);
startButtonPanel.setBackground(Color.BLACK);
startButton = new JButton("START");
startButton.setBackground(Color.BLACK);
startButton.setForeground(Color.WHITE);
startButton.setFont(fontStart);
startButton.setFocusPainted(false);
startButton.addActionListener(cj);
startButton.setActionCommand("start");
startButtonPanel.add(startButton);
//Load Button
loadButtonPanel = new JPanel();
loadButtonPanel.setBounds(200, 440, 200, 100);
loadButtonPanel.setBackground(Color.BLACK);
loadButton = new JButton("LOAD");
loadButton.setBackground(Color.BLACK);
loadButton.setForeground(Color.WHITE);
loadButton.setFont(fontStart);
loadButton.setFocusPainted(false);
titrePanel.add(nomJeu);
loadButtonPanel.add(loadButton);
frame.add(startButtonPanel);
frame.add(titrePanel);
//------------------ECRAN MENU FIN---------------------
frame.setVisible(true);
}
And the game class...
public class Jeu {
ChoixJeu cj = new ChoixJeu();
UI ui = new UI();
Ecrans e = new Ecrans(ui);
Gameplay gp;
public static void main(String[] args) {
new Jeu();
}
public Jeu() {
ui.createUI(cj);
Gameplay gameplay = new Gameplay();
this.gp = gameplay;
}
public class ChoixJeu implements ActionListener {
#Override
public void actionPerformed(ActionEvent ae) {
String yourChoice = ae.getActionCommand();
switch (yourChoice) {
case "start":
e.montrerEcranJeu();
new Thread(gp).start();
ui.frame.add(gp);
break;
default:
break;
}
}
}
}
I also tried to make a class/method that hides the menu panels
public void montrerEcranJeu() {
//Cache Menu
ui.titrePanel.setVisible(false);
ui.startButtonPanel.setVisible(false);
//Montre Jeu
// ui.frame.add(gameplay);
}
And just in case the Gameplay class. The run() method is at the bottom
public class Gameplay extends JPanel implements KeyListener, ActionListener, Runnable {
private Ship player = new Ship(new Point(200, 555));
Timer t = new Timer(5, this);
private ArrayList<Laser> lasers = new ArrayList<Laser>();
private int laserNb;
private boolean readytofire;
private boolean shot = false;
private ArrayList<Invader> invaders = new ArrayList<Invader>();
private boolean pause;
public Gameplay() {
super();
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
for (int j = 0; j < 80; j += 20) {
for (int i = 0; i < 20; i++) {
invaders.add(new Invader(5 + i * 30, j));
}
}
}
public boolean addLaser(Laser a) {
lasers.add(a);
return true;
}
public boolean addPlayer(Ship p) {
this.player = p;
return true;
}
#Override
public void keyTyped(KeyEvent ke) {
}
public void keyPressed(KeyEvent e) {
if (KeyEvent.VK_RIGHT == e.getKeyCode()) {
moveRight();
}
if (KeyEvent.VK_LEFT == e.getKeyCode()) {
moveLeft();
}
if (KeyEvent.VK_SPACE == e.getKeyCode()) {
shoot();
System.out.println("Space Action from Gameplay is working");
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
public void moveRight() {
if (player.getCentre().getX() >= 580) {
player.setX(580);
} else {
double movement = player.getCentre().getX();
movement += 10;
player.setX(movement);
}
this.repaint();
}
public void moveLeft() {
if (player.getCentre().getX() <= 20) {
player.setX(20);
} else {
double movement = player.getCentre().getX();
movement -= 10;
player.setX(movement);
}
this.repaint();
}
public void shoot() {
shot = true;
if (readytofire) {
Point top = new Point(player.getTopX(), player.getTopY());
Laser laser = new Laser(top);
addLaser(laser);
}
}
public void moveShot() {
if (shot) {
for (Laser l : lasers) {
l.setY(l.getTopLeft().getY() - 1);
}
}
}
#Override
public void paint(Graphics g) {
setBackground(Color.black);
super.paint(g);
player.draw(g);
for (Laser l : lasers) {
l.draw(g);
}
for (Invader i : invaders) {
i.draw(g);
}
}
// public void paintComponent (Graphics g){
// Controle Thread
public void run() {
while (true) {
moveShot();
for (Invader i : invaders) {
i.moveAndUpdate();
}
// for (Invader i : invaders) {
// if (){
// System.out.println("YOU ARE DEAD!");
// }
// }
try {
Thread.sleep(10);
readytofire = true;
} catch (InterruptedException ex) {
Logger.getLogger(Gameplay.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
So, using null layouts is the beginning of your problems. I might recommend using CardLayout which is designed to help you dynamically switch between views. See How to Use CardLayout for more details. I'd also suggest taking the time to read through Laying Out Components Within a Container and finding one or more appropriate layouts to support your menu.
You're also making a lot of fundamental mistakes. Swing is not thread safe, so you should avoid updating the UI (or something the UI depends on) from outside the context of the EDT - see Concurrency in Swing for more information and How to Use Swing Timers for a possible solution.
As a general recommendation, you should avoid overriding paint and, in the case of classes which extend from JComponent, prefer paintComponent instead. You should also avoid call methods which might change the state of the component during a paint cycle, this can increase the number of repaint requests and degrade the performance of your program (ie, don't call setBackground inside paint).
Have a look at Performing Custom Painting and Painting in AWT and Swing for more details about how the paint system works and how best you can work with it.
You should also avoid KeyListener, this is likely to cause you issues when you introduce other, focusable, components into the picture. Instead, you should favour the Key bindings API instead
I've read through [insert link or tutorial], but it still doesn't help...
And forgive me if this doesn't happen all the time.
The point of providing you the tutorial links is to encourage you to learn something;
Learn where to find answers to your questions
Learn how the APIs work
Expand your knowledge and understanding of how the APIs work
Having said that, they're not always "obvious" as to the solution. What I do when I'm in this situation is start with one or more new projects, dedicated to just working on that aspect of the API I'm trying to understand. For here I can explore the concepts in isolation and when I "think" I understand them, try and implement them into the project I'm working on. This might take a number of iterations, but once it works, I have gained a much deeper understanding and appreciation of the API then I would have gained from a simple "copy-n-paste" solution
Related
I have a program that runs a basic algorithm for solving a puzzle. I am trying to slow it down so that the user can see the algorithm in action. I want the program to show the steps that it takes to solve the puzzle. I have tried to use a timer and even thread.sleep to try and slow it down. However, my GUI (JPanel) is not repainting before or after the delay statement. What is happening is the delay is happening and the algorithm is solving step by step, but the program GUI is not updating/repainting to show the steps. Here is the code for that section:
delay(System.currentTimeMillis(), 500);
repaint();
The delay part is all good. Can anybody tell me why the JPanel is not repainting?
Here is the delay method that I made:
public static void delay (long a, int x) //Simple Delay Method
{
while((System.currentTimeMillis()-a) < (x/10)){}
}
You can't just sleep the UI thread and wait for repaint to happen, that will stop all user and os interaction for that time (including repaint events). From the repaint documentation:
If this component is a lightweight component, this method causes a call to this component's paint method as soon as possible.
As soon as possible does not mean immediately, it will most likely schedule a repaint, which will run when the ui thread is released.
You have to change your algorithm tohold it's state in an object, and make it possible to execute step-by-step, and use a timer like in this answer.
Example:
public class SwingMain extends JFrame {
public class MyPanel extends JPanel {
public String state = "Initial";
private JButton btn_start;
public MyPanel() {
super();
btn_start = new JButton("Start");
add(btn_start);
btn_start.addActionListener(e -> doSolve());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(getFont());
g.drawString(state, 10, 20);
};
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
}
private List<String> states;
private int stateIndex;
protected Task token;
private MyPanel pnl_main;
public SwingMain() {
super("Step by step");
setupUi();
}
protected void setupUi() {
pnl_main = new MyPanel();
getContentPane().add(pnl_main);
}
private List<String> modelSolve() {
return Arrays.asList("First", "Second", "Third", "Fourth", "Fifth");
}
public void doSolve() {
this.stateIndex = 0;
this.states = modelSolve();
if (token != null)
token.cancel();
token = GuiTimer.scheduleAtFixedRate(() -> {
if (stateIndex >= states.size()) {
token.cancel();
} else {
pnl_main.state = states.get(stateIndex);
stateIndex++;
pnl_main.repaint();
}
}, 0, 500, TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SwingMain frame = new SwingMain();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
System.out.println(frame.pnl_main.getWidth());
System.out.println(frame.pnl_main.getHeight());
});
}
}
I extended a JFrame class and have my own model and JPanel extended classes as instance variables. I implemented KeyListener to my JFrame and it works with the arrow keys but my model moves extremely slow around the frame when I hold the keys down. My question is how do I attach the KeyListener methods to a timer or do something to make my model move faster when I hold the keys. Also if it is possible, how can the model move two directions at once, say left and up?
public class GameController extends JFrame implements KeyListener,ActionListener
{
private GamePieces p;
private GamePanel panel;
private Timer timer;
public GameController()
{
super("Balls");
setSize(800, 600);
timer = new Timer(10, this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new BorderLayout());
p = new GamePieces();
panel = new GamePanel();
p.addObserver(panel);
c.add(panel);
addKeyListener(this);
panel.update(p, null);
setResizable(false);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
String actionCommand = e.getActionCommand();
if (e.getSource() == timer)
{
p.checkEat();
p.moveOthers();
panel.update(p, null);
}
}
public void keyTyped(KeyEvent e)
{
int s = 0;
}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key==38)
{
p.up();
panel.update(p, null);
}
else if (key==40)
{
p.down();
panel.update(p, null);
}
else if (key==39)
{
p.right();
panel.update(p, null);
}
else if (key==37)
{
p.left();
panel.update(p, null);
}
}
public void keyReleased(KeyEvent e)
{
int o = 0;
}
public static void main(String[]args)
{
GameController a = new GameController();
a.setVisible(true);
}
}
You are on right way but it should be a little more sophisticated. Follow MVC pattern. You should have model which remembers where you panel should be - up(), down() methods only update this model. You should have viewer which shows panel at current position. panel.update() belongs to viewer. And then you should have controller which changes position (invoke these up and down, left and right) and invokes viewer when necessary - to show movement or just next frame.
Your KeyListener tells controller to move position in some intervals when key is pressed. And then it tells controller to stop doing so when key is released. See, all parts move smoothly and independently. Keylistener tells controller what to do, controller does - changes model and invokes viewer. Model does nothing - only holds data. Viewer does nothing only shows current data.
P.S. Don't forget thread safety since you will press multiple keys and they will invoke controller multiple times in the same thread.
I know there are a lot of answers for this but they don't seem to work. I don't know whether I am somehow using a different version of Java or the code for applet's is different but here's my code:
Snippet from HUD class:
public void paint (Graphics g)
{
if(g instanceof Graphics2D)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.drawString("This is gona be awesome", 200, 200);
}
Main Class:
import java.applet.Applet; (more, they just glitched with the code block)
public class Main extends Applet implements Runnable, KeyListener {
static ArrayList<Block> all_blocks = new ArrayList<Block>();
Player player;
HUD hud;
static int amount = 0;
public void init(){
setSize(700, 500);
addKeyListener(this);
}
public void start(){
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while (true){
player.update();
repaint();
camera_scroll(this.amount);
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop(){
}
public void camera_scroll(int amount){
int new_amount;
if (amount > 30){
new_amount = 30;
this.amount -= 30;
} else {
new_amount = amount;
this.amount = 0;
}
for (Block b: all_blocks){
b.x += new_amount;
b.update();
}
player.x += new_amount;
}
public void paint(Graphics g){
for (Block block: all_blocks){
block.paint(g);
}
hud.paint(g);
player.paint(g);
}
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
player.moveLeft();
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
player.moveRight();
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
player.moveUp();
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
player.stopLeft();
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
player.stopRight();
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
player.stopUp();
break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Drawing of the player and blocks work fine and their paint method is identical to HUD's but they draw images instead. Let me know if you need anything else. Thanks!
The Problem...
There are two main issues...
One...
You are creating instance of objects within the start method, when they should be created in your init method.
start can be called multiple times (normally after stop is called) to get the applet to continue. This is not a good place to try and initialize the state of your underlying applet, instead, it would be better used to restart the Thread (assuming you also stop it when stop is called)...
So, instead of...
public void init(){
setSize(700, 500);
addKeyListener(this);
}
public void start(){
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
Thread thread = new Thread(this);
thread.start();
}
you should be using something like...
public void init(){
setSize(700, 500);
addKeyListener(this);
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
}
public void start(){
Thread thread = new Thread(this);
thread.start();
}
Two...
You are creating a local instance of your objects, shadowing your instance fields, which is causing a NullPointerException (for me it was in paint...
So instead of
public void init(){
setSize(700, 500);
addKeyListener(this);
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
}
You should be using...
public void init(){
setSize(700, 500);
addKeyListener(this);
player = new Player(100, 100);
hud = new HUD();
// Notice that HUD has not been re-declared...
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
}
Side Notes
AWT was replaced by Swing some 15+ years ago, the API is no longer, generally, used. It would recomended to use JApplet instead...
Applets are notoriously difficult to develop and typically run within a very restrictive security sandbox. Unless you have a very particular need to use them, I'd recommend avoiding them for the time been and use a JFrame as you core, top level container.
It's generally a bad idea to paint directly to a top level container like an applet or frame, instead, it is generally encouraged that you move your core logic to something like a JPanel and override it's paintComponent method to perform custom painting. This is recommended for a number of reasons, including the fact that painting is actually quite involved and complicated and it is very easy to break the paint chain and top level containers aren't double buffered, which will cause flickering. Swing components (apart from there top level counterparts) are double buffered by default. Take a look at Painting in AWT and Swing and Performing Custom Painting for more details
KeyListeners are notorious for having issues related to focus. If you move to the Swing API, it is best to use the Key Bindings API
Also, Swing is not thread safe, just beware ;)
I am trying to to set up a KeyListener to respond to my keystrokes. I have already setup a mouselistener but for some reason I am un-able to get the keylistener to respond to any keystrokes.
I have created a class that implements KeyListener and overridden the functions. I then created an instance of the new class and added the handler to the JPanel and JFrame. Still no dice
public class main_program extends JFrame {
private int mX_cord, mY_cord,prior_selected_vertex, current_selected_vertex;
private int verticies_to_edge1, verticies_to_edge2;
private int radius =10;
private boolean vertex_selected1 = false, vertex_selected2 = false, edge_ready = false,delete_vertex_ready = false;
private Edge tempEdge = null;
private ArrayList<Integer> vertex_xcord = new ArrayList<Integer>();
private ArrayList<Integer> vertex_ycord = new ArrayList<Integer>();
private ArrayList<Edge> edge = new ArrayList<Edge>();
HandlerMouse handler = new HandlerMouse();
HandlerKey keyhand = new HandlerKey();
private JPanel masterPanel;
private JTextArea masterTextArea;
private JScrollPane masterScrollPane;
private Point point1, point2;
Graphics g;
public main_program(){
setTitle("Graph");
setSize(600, 400);
setDefaultCloseOperation(new JFrame().EXIT_ON_CLOSE);
//this must be set for custom layout of components
setLayout(null);
masterPanel = new JPanel();
masterPanel.setSize(600,300);
masterPanel.setLocation(0, 0);
masterPanel.setBackground(Color.WHITE);
masterPanel.addMouseListener(handler);
masterPanel.addMouseMotionListener(handler);
masterPanel.addKeyListener(keyhand);
masterTextArea = new JTextArea();
masterTextArea.setBackground(Color.green);
masterScrollPane = new JScrollPane();
masterScrollPane.add(masterTextArea);
masterScrollPane.setSize(600, 100);
masterScrollPane.setLocation(0, 300);
masterScrollPane.addMouseListener(handler);
masterScrollPane.addMouseListener(handler);
masterScrollPane.addKeyListener(keyhand);
add(masterPanel);
add(masterScrollPane);
setLocationRelativeTo(null);
setVisible(true);
}
public void paint(Graphics g){
super.paint(g);
for(int i = 0 ; i < vertex_xcord.size(); i++){
g.fillOval(vertex_xcord.get(i), vertex_ycord.get(i), radius, radius);
}
for(int i = 0 ; i<edge.size(); i++){
tempEdge = edge.get(i);
g.drawLine(vertex_xcord.get(tempEdge.vertex1), vertex_ycord.get(tempEdge.vertex1), vertex_xcord.get(tempEdge.vertex2), vertex_ycord.get(tempEdge.vertex2));
}
//g.fillOval(mX_cord, mY_cord, radius, radius);
//repaint();
}
private class HandlerKey implements KeyListener{
public void keyPressed(KeyEvent evt){
System.out.println("key pressed");
if(evt.getKeyCode() == KeyEvent.VK_ENTER && edge_ready){
edge.add(new Edge(prior_selected_vertex, current_selected_vertex));
edge_ready = false;
repaint();
}
}
public void keyReleased(KeyEvent evt){
System.out.println("key rel");
}
public void keyTyped(KeyEvent evt){
System.out.println("key type");
}
}
private class HandlerMouse implements MouseListener, MouseMotionListener{
public void mouseClicked(MouseEvent evt){
mX_cord = evt.getX()-5;
mY_cord = evt.getY()+15;
if( evt.getClickCount() == 1){
//mX_cord = evt.getX();
//mY_cord = evt.getY();
vertex_xcord.add(mX_cord);
vertex_ycord.add(mY_cord);
repaint();
}
else{
for(int i = 0 ; i < vertex_xcord.size(); i++){
if(Math.abs(vertex_xcord.get(i) - mX_cord ) < 15 && Math.abs(vertex_ycord.get(i) - mY_cord ) < 15 ){
if(vertex_selected1 == false){
prior_selected_vertex = i;
vertex_selected1 = true;
}
else{
current_selected_vertex = i;
vertex_selected2 = true;
}
System.out.println("YOU HAVE SELECTED A VERTEX: " + i);
break;
}
}
}
if(vertex_selected2 == true){
edge_ready = true;
verticies_to_edge1 = prior_selected_vertex;
verticies_to_edge2 = current_selected_vertex ;
vertex_selected1 = vertex_selected2 = false;
System.out.println("Ready for edge!");
}
else{
delete_vertex_ready = true;
}
}
public void mouseEntered(MouseEvent arg0)
{
}
public void mouseExited(MouseEvent arg0)
{
}
public void mousePressed(MouseEvent evt)
{
}
public void mouseReleased(MouseEvent arg0)
{
}
public void mouseDragged(MouseEvent e)
{
}
public void mouseMoved(MouseEvent e)
{
}
}
class Edge {
int vertex1, vertex2;
public Edge(int v1, int v2){
vertex1 = v1;
vertex2 = v2;
}
}
public static void main(String[] args){
main_program circle = new main_program();
}
}
You've got several problems with that program, and it suggests that you'd do well to read many of the Swing Q&A's on this site, because these problems (and your main problem) are quite common, and solutions are often posted.
As to your main problem, the problem again is very common: KeyListeners only work if the listened to component has focus. If it loses focus or is not focusable, then you're out of luck. The best solution is often not to use a KeyListener but rather the higher level and more flexible Key Bindings. Google will find the tutorials for you for this.
As for other problems with your code:
You're using null layout, a layout that leads to inflexible GUI's that are very difficult to upgrade and enhance, and that look terrible on all but your current platform and screen resolution. Solution: study and use the layout managers.
You're drawing directly into a JFrame's paint(Graphics g) method, which has risks as you risk messing up the painting of any and all of the JFrame's constituents by doing this. Much better to draw in a JPanel's paintComponent(Graphics g) method, and gain the benefit of Swing (rather than AWT) graphics including automatic double buffering.
Your class has a Graphics variable, g, which suggests that you're contemplating using a stored Graphics object either from a component or from the JVM. This increases the risk of your program throwing a NullPointerException when that Graphics object no longer exists, either that or drawing with it and but not seeing any effect. Solution: draw inside the painting method (again better with a JComponent's paintComponent method) only, or with the Graphics object from a BufferedImage.
You're adding a MouseListener directly to a JPanel but are drawing in a different component, the JFrame, and by doing so without taking insets into account, are placing points in the wrong location. Use the MouseListener on the same component that you're drawing on.
Again, please have a look around here, as I think you'll find a lot of treasures on this site that can be used to enhance your program.
I am trying to flash the background colour in a textfield. My timer setup is as follows:
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
tmr.setInitialDelay(0);
tmr.setRepeats(true);
tmr.start();
My actionListener is as follows:
static class Flash implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.white);
}
else
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
Now, when I put this in debug and follow the action, the program does repeatedly step through flash and toggle between the two alternatives. But onscreen, only the first toggle
occurs. After that, no action, although flash is still functioning.
What is wrong here?
Thanks in advance for any help.
This example continually varies the saturation of a panel's background color:
import java.awt.*;
import java.awt.event.*;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
public class FlashTest extends JPanel {
private static final Font font = new Font("Serif", Font.PLAIN, 32);
private static final String s = "Godzilla alert!";
FlashTest() {
this.setPreferredSize(new Dimension(256, 96));
this.setBackground(Color.red);
Timer t = new Timer(50, new Flash(this));
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
int xx = this.getWidth();
int yy = this.getHeight();
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getDescent();
g.setColor(Color.black);
g.drawString(s, xx / 2 - w2, yy / 2 + h2);
}
private static class Flash implements ActionListener {
private final float N = 32;
private final JComponent component;
private final Queue<Color> clut = new LinkedList<Color>();
public Flash(JComponent component) {
this.component = component;
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, 1 - (i / N), 1));
}
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, i / N, 1));
}
}
#Override
public void actionPerformed(ActionEvent e) {
component.setBackground(clut.peek());
clut.add(clut.remove());
}
}
static public void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new FlashTest());
f.pack();
f.setVisible(true);
}
});
}
}
There are a couple of problems here.
The first obvious thing is that you appear to be using mutable statics. This is a really bad idea and indicates (and causes!) confusion. In this particular case, one of the problems caused is that the flasher static is shared.
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
We are adding two Flash actions. Ordinarily this would be bad, but just produce an undetectable "bug". The colour would be set twice.
Bring these two things together, and we have two actions without a break that perform the same toggle. Two toggles. The state does not change (although there are repaint, property change events, etc.).
So, don't use mutable statics, and keep the code clean.
tmr = new javax.swing.Timer(1000, flash);
I tried your code and it works fine.
Why do you use a static context for SpreademPanel.historyPnl.NameTxt?
EDIT
You might want to redesign your class to pass the component in the constructor.
private class Flash implements ActionListener
{
private boolean flasher = false;
private JComponent component;
public Flash(JComponent component) {
this.component = component;
}
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
component.setBackground(Color.white);
}
else
{
component.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
and then init it with
Flash flash = new Flash(SpreademPanel.historyPnl.NameTxt);
Timer tmr = new javax.swing.Timer(1000, flash);
tmr.start();