问题
So I have a class where I have to make a program to make Simon. I know the way I'm doing it is not necessarily the best way, However, he had some obscure requirements so that is why I'm doing it this way.
My program is close to finished, but I have one MAJOR issue. When I press the reset button I call a method called reset which in turn sets the computer to play their first move.
During this, there are graphical updates.
When I call the reset method by itself it works as expected When I press the reset button it wates to do all graphical updates until after it is complete. Is there a way to have the method run after the button has ben pressed?
My Main program
package game;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import components.*;
@SuppressWarnings("serial")
public class Simonish extends JFrame implements ActionListener, MouseListener {
private Color[][] ColorSwatch = {{Color.RED,Color.PINK},{Color.GREEN,Color.YELLOW}};
private int width = 2;
private int height = 2;
private int panSize = 200;
private SPane[][] panBoard;
private int[] Sequence;
private int CurrentSequenceLeingth = 0;
private int SequenceLeingth = 10000;
private Random r = new Random();
boolean LastButtonClicked = false;
private int LastButtonPressed = 0;
private int sequencePart = 0;
private boolean turn = false; //f=computer t=player
Container pane;
JPanel boardPanel;
ScoreBoard scorePanel;
private Simonish(){
scorePanel = new ScoreBoard(0,width,panSize);
scorePanel.getResetBtn().addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
resetGame();
}
});
this.setTitle("Simonish");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
pane = this.getContentPane();
pane.setLayout(null);
resetGame();
}
private void play(){
if(!turn)
ComputerTurn();
else
PlayerTurn();
}
private void ComputerTurn(){
CurrentSequenceLeingth++;
scorePanel.AddScore(1);
PlaySequenc();
turn = true;
}
private void PlayerTurn(){
if((LastButtonPressed == Sequence[sequencePart]))
{
sequencePart++;
LastButtonClicked = false;
}else
{
loose();
return;
}
if(sequencePart >= CurrentSequenceLeingth)
{
sequencePart = 0;
turn = false;
play();
}
}
private void loose(){
System.out.println("you loose");
resetGame();
}
private void PlaySequenc(){
for(int i = 0 ; i < CurrentSequenceLeingth ; i++)
{
panBoard[Sequence[i]/2][Sequence[i]%2].pressPane();
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
private void resetGame() {
pane.removeAll();
pane.setPreferredSize(new Dimension(width*panSize, (height) * panSize + 75));
pane.add(scorePanel);
initPanes();
LastButtonClicked = false;
LastButtonPressed = 0;
initBtns();
turn = false;
CurrentSequenceLeingth = 3;
Sequence = new int[SequenceLeingth];
initSeq();
pane.update(pane.getGraphics());
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
play();
}
private void initSeq() {
for(int i = 0 ; i < SequenceLeingth ; i++)
{
Sequence[i] = r.nextInt(4);
}
}
private void initBtns() {
this.panBoard = new SPane[width][height];
for(int w = 0; w < width; w++) {
for(int h = 0; h < height; h++) {
panBoard[w][h] = new SPane(w, h, panSize,ColorSwatch[w][h]);
panBoard[w][h].addMouseListener(this);
pane.add(panBoard[w][h]);
pane.addMouseListener(this);
}
}
}
public static void main(String[] args){
new Simonish();
}
private void initPanes() {
//TODO
}
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
}
The ScoreBoard Class
package components;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
@SuppressWarnings({ "serial", "unused" })
public class ScoreBoard extends JPanel {
private int score;
private JLabel SimonNumberLabel;
private JLabel timerLabel;
private JButton resetBtn;
public ScoreBoard(int bombsCnt,int w,int w2) {
score = bombsCnt;
SimonNumberLabel = new JLabel("Sequence Leingth: " + Integer.toString(bombsCnt), SwingConstants.CENTER);
resetBtn = new JButton("reset");
setBounds(0, 0, w*w2, 3*25);
this.setLayout(new GridLayout(1,3));
add(SimonNumberLabel);
add(resetBtn);
}
public void AddScore(int update) {
score += update;
SimonNumberLabel.setText("Sequence Leingth: " + Integer.toString(score));
}
public JButton getResetBtn() {
return resetBtn;
}
}
The SPane Class
package components;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;
@SuppressWarnings("serial")
public class SPane extends JPanel{
Color C;
Color DC;
int x;
int y;
public SPane(int x, int y, int size, Color colorSwatch) {
this();
this.x = x;
this.y = y;
this.setLayout(new GridLayout(x+1,y+1));
this.setBounds(x*size, y*size+75, size, size);
C = colorSwatch;
DC = C;
DC = DC.darker();
this.setBackground(DC);
this.setVisible(true);
}
public SPane() {
this.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
}
public void resetSPane() {
// TODO Auto-generated method stub
}
public void pressPane()
{
this.setBackground(C);
System.out.println("dsfdsfsdfsdf");
try{
Thread.sleep(1000);
}catch (Exception e) {}
this.setBackground(DC);
}
public void clicked() {
this.setBackground(Color.GREEN);
}
}
回答1:
This is the first culprite...
private void PlaySequenc(){
for(int i = 0 ; i < CurrentSequenceLeingth ; i++)
{
panBoard[Sequence[i]/2][Sequence[i]%2].pressPane();
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
This is been called from within the content of the Event Dispatching Thread, which is preventing it from processing any new updates or updating the UI.
Have a look at Concurrency in Swing for more details and consider using a Swing Timer
instead, see How to use Swing Timers for more details
pane.update(pane.getGraphics());
ranks very, very highly among some of the worst things you could ever do in Swing (Thread.sleep
is up there with it).
Take a look at Painting in AWT and Swing and Performing Custom Painting to understand how painting works in Swing
Avoid using null
layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
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
For example...
So, this example is really basic. It has three buttons...
- The "wrong way", which is basically what you're doing
- The "timer way", which uses a Swing
Timer
- The "worker way", which uses a
SwingWorker
In your case, I still think a Swing Timer
is the best choice, as it's possible for a SwingWorker
to get backed up with updates, spitting them all out at once, which defeats the purpose of why you might want to use it.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Sequence {
public static void main(String[] args) {
new Sequence();
}
public Sequence() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<SequencePane> panels = new ArrayList<>(4);
private Timer timer;
private int sequenceIndex;
public TestPane() {
setLayout(new BorderLayout());
Color colors[] = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW};
for (int index = 0; index < 4; index++) {
panels.add(new SequencePane(colors[index]));
}
JPanel content = new JPanel(new GridLayout(2, 2));
for (SequencePane pane : panels) {
content.add(pane);
}
add(content);
JButton wrong = new JButton("The wrong way");
JButton timerButton = new JButton("The Timer way");
JButton workerButton = new JButton("The Worker way");
JPanel actions = new JPanel();
actions.add(wrong);
actions.add(timerButton);
actions.add(workerButton);
add(actions, BorderLayout.SOUTH);
wrong.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Collections.shuffle(panels);
for (SequencePane pane : panels) {
try {
pane.setHighlighted(true);
Thread.sleep(250);
pane.setHighlighted(false);
} catch (InterruptedException ex) {
}
}
}
});
timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (sequenceIndex > 0) {
panels.get(sequenceIndex - 1).setHighlighted(false);
}
if (sequenceIndex < panels.size()) {
panels.get(sequenceIndex).setHighlighted(true);
sequenceIndex++;
} else {
timer.stop();
// All done, call some "play" method to begin playing
}
}
});
timerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
timer.stop();
Collections.shuffle(panels);
sequenceIndex = 0;
timer.start();
}
});
SwingWorker worker = new SwingWorker<Object, SequenceState>() {
@Override
protected Object doInBackground() throws Exception {
for (SequencePane pane : panels) {
publish(new SequenceState(pane, true));
Thread.sleep(100);
publish(new SequenceState(pane, false));
}
return null;
}
@Override
protected void process(List<SequenceState> chunks) {
SequenceState state = chunks.get(chunks.size() - 1);
state.applyState();
}
@Override
protected void done() {
// Back in the EDT, call what ever "play" method you need
}
};
workerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Collections.shuffle(panels);
SequenceWorker worker = new SequenceWorker();
worker.execute();
}
});
}
public class SequenceWorker extends SwingWorker<Object, SequenceState> {
@Override
protected Object doInBackground() throws Exception {
for (SequencePane pane : panels) {
publish(new SequenceState(pane, true));
Thread.sleep(250);
publish(new SequenceState(pane, false));
}
return null;
}
@Override
protected void process(List<SequenceState> chunks) {
for (SequenceState state : chunks) {
state.applyState();
}
}
@Override
protected void done() {
// Back in the EDT, call what ever "play" method you need
}
}
public class SequenceState {
private SequencePane sequencePane;
private boolean highlighted;
public SequenceState(SequencePane sequencePane, boolean highlighted) {
this.sequencePane = sequencePane;
this.highlighted = highlighted;
}
public SequencePane getSequencePane() {
return sequencePane;
}
public boolean isHighlighted() {
return highlighted;
}
public void applyState() {
getSequencePane().setHighlighted(isHighlighted());
}
}
}
public class SequencePane extends JPanel {
private boolean highlighted;
public SequencePane(Color color) {
setOpaque(false);
setBackground(color);
}
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
repaint();
}
public boolean isHighlighted() {
return highlighted;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (!isHighlighted()) {
g2d.setComposite(AlphaComposite.SrcOver.derive(0.25f));
}
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
}
}
来源:https://stackoverflow.com/questions/33727830/java-button-pausing-graphical-updates