Beginner Java developer. Trying to make a Tetris applet as part of my personal projects.
I'm at the point were I can draw tetris blocks onto the screen but I cannot make it vertically go downwards every second.
Code:
public class InitialScreen extends JApplet implements ActionListener {
public JPanel cards = new JPanel();
private JPanel introPanel = new JPanel();
public CardLayout c1 = new CardLayout();
public void init() {
initiateIntroScreen();
//game();
add(cards, BorderLayout.NORTH);
setSize(500, 100);
}
private void initiateIntroScreen() {
Frame title = (Frame)this.getParent().getParent();
cards.setLayout(c1);
JLabel centralWords = new JLabel("Click the following button options: 'Play' or 'Instructions'.");
JButton playBtn = new JButton("Play!");
JButton instructionsBtn = new JButton("Instructions!");
introPanel.add(centralWords);
introPanel.add(playBtn);
introPanel.add(instructionsBtn);
cards.add(introPanel,"1");
playBtn.addActionListener(this);
playBtn.addActionListener(new MainGame(cards,c1));
}
#Override
public void actionPerformed(ActionEvent e) {
setSize(300,410);
getContentPane().setBackground(Color.BLACK);
}
So this is the initial screen for the JApplet. Has two buttons. When you press the 'Play' button it goes to the Main Game Screen.
public class MainGame extends JApplet implements ActionListener {
private JPanel cards;
private CardLayout c1;
private JPanel gamePanel = new JPanel();
public MainGame(JPanel cards, CardLayout c1) {
this.c1 = c1;
this.cards = cards;
gamePanel.add(new Tetris_Block(new int[10][20]));
}
#Override
public void actionPerformed(ActionEvent e) {
JLabel scoreLbl = new JLabel("Score:");
gamePanel.add(scoreLbl);
cards.add(gamePanel,"game");
c1.show(cards,"game");
}
This is the game screen were Tetris is played. In the constructor it calls a Tetris Block.
public class Tetris_Block extends JComponent implements ActionListener {
static Color[] colors =
{darkGray, green, blue, red,
yellow, magenta, pink, cyan};
int[][] a;
int w, h;
static int horizontalPos, verticalPos = 0;
static int size = 20;
private int verticalPos1 = 1;
public Tetris_Block(int[][] a) {
this.a = a;
w = a.length;
h = a[0].length;
square_Block();
startTimer();
}
private void nextMove() {
verticalPos++;
verticalPos1++;
}
public void square_Block(){ //Horizontal || Vertical || Colour
//Horizontal never changes for this as I just want the blocks to go down.
a[0][verticalPos] = 3;
a[0][verticalPos1] = 3;
a[1][verticalPos] = 3;
a[1][verticalPos1] = 3;
}
#Override
public void actionPerformed(ActionEvent e) {
nextMove();
square_Block();
System.out.println(verticalPos);
}
public void startTimer(){
Timer timer = new Timer(1000,this);
timer.start();
}
public void paintComponent(Graphics g) {
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
g.setColor(colors[a[i][j]]);
g.fill3DRect(i * size, j * size,
size, size, true);
}
}
}
public Dimension getPreferredSize() {
return new Dimension(w * size, h * size);
}
My aim is to make the vertical position increment by 1 every second (So it goes down the window in second intervals.
I don't think the Timer function is the problem. When I print verticalPos it prints out the incremented value every second, however it's just displaying the new location onto the screen- that is the problem.
Image of window right now.
[img]http://i.imgur.com/au5fceO.png?1[/img]
Start by adding a call to repaint in you actionPerformed method of your Tetris_Block
#Override
public void actionPerformed(ActionEvent e) {
nextMove();
square_Block();
System.out.println(verticalPos);
// This is important
repaint();
}
This will schedule a paint event on the event queue which will eventually call your paintComponent method (indirectly)
This will get the block to start moving. The next problem you will have is you're not actually "removing" the block from it's previous position, so it will continue to bleed/grow down the screen
You could solve this by passing in the color of the block to square_Block, for example...
public void square_Block(int color) { //Horizontal || Vertical || Colour
//Horizontal never changes for this as I just want the blocks to go down.
a[0][verticalPos] = color;
a[0][verticalPos1] = color;
a[1][verticalPos] = color;
a[1][verticalPos1] = color;
}
And then "rest" the blocks of the current position, update the position and then set the new block colors;
#Override
public void actionPerformed(ActionEvent e) {
square_Block(0);
nextMove();
square_Block(3);
System.out.println(verticalPos);
repaint();
}
Your design here may be faulty. You need to have a game loop that runs in a separate thread. It has to be a separate thread from the main thread so the user can still click buttons. Once you have a loop in the separate thread you need to have a method that you call for every game tick. It's in that method that you update the coordinates of the blocks.
Game loop works like this:
1. Read state of the game and draw the blocks
2. Process user input.
3. Update game state
I know this is abstract but I hope it helps. Google about java games and game loops.
Related
I'm trying to make a simple connect four GUI by using a grid of JPanels each of which paints a colored disk when the button below them is pressed and the panels under it are full. Before adding the game rules I'm trying to just make sure the buttons and display work properly. But it is not working - only the top left panel displays a disk (after pressing button 1 6 times). here is my code:
public class ConnectFourFrame extends JFrame {
private final JPanel gamePanelsPanel; // panel to hold the game panels
private final GamePanel[][] gamePanels; // a 2D array to hold the grid of panels to display the game disks
private final JPanel buttonsPanel; // panel to hold the buttons panels
private final JPanel gameButtonsPanel; // panel to hold the game buttons to add disk to a column
private final JButton[] gameButtons; // an array to hold the game buttons
private final JPanel clearButtonPanel; // panel to hold the clear button
private final JButton clearButton; // button to clear the game grid from disks
private enum Turn {RED_PLAYER, BLUE_PLAYER}; // keeps track of which players turn it is
private Turn turn;
// no argument constructor
public ConnectFourFrame() {
super("Connect Four");
this.setLayout(new BorderLayout());
//add panels to hold the game panel and the buttons
gamePanelsPanel = new JPanel();
add(gamePanelsPanel, BorderLayout.CENTER);
buttonsPanel = new JPanel();
buttonsPanel.setLayout(new BorderLayout());
add(buttonsPanel, BorderLayout.SOUTH);
//set up game panels
gamePanelsPanel.setLayout(new GridLayout(6,7,3,3));
gamePanelsPanel.setBackground(Color.BLACK);
gamePanels = new GamePanel[6][7];
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
gamePanels[i][j] = new GamePanel(false, Color.WHITE);
gamePanelsPanel.add(gamePanels[i][j]);
}
}
//set up game and clear buttons
gameButtonsPanel = new JPanel();
gameButtonsPanel.setLayout(new GridLayout(1,7));
clearButtonPanel = new JPanel();
gameButtons = new JButton[7];
for (int i = 0; i < 7; i++) {
gameButtons[i] = new JButton("" + (i+1));
gameButtonsPanel.add(gameButtons[i]);
}
clearButton = new JButton("CLEAR");
clearButtonPanel.add(clearButton);
buttonsPanel.add(gameButtonsPanel, BorderLayout.NORTH);
buttonsPanel.add(clearButtonPanel, BorderLayout.SOUTH);
add(buttonsPanel, BorderLayout.SOUTH);
// register event handlers
ClearButtonHandler clearButtonHandler = new ClearButtonHandler();
clearButton.addActionListener(clearButtonHandler);
GameButtonHandler gameButtonHandler = new GameButtonHandler();
for (int i = 0; i < 7; i++) {
gameButtons[i].addActionListener(gameButtonHandler);
}
turn = Turn.RED_PLAYER; //set first turn to player1
}
// inner class for game button event handling
private class GameButtonHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// get the number of the pressed button
int pressedButtonNum = Integer.parseInt(((JButton) e.getSource()).getActionCommand());
// display disk in top empty panel of the column
for (int i = 5; i >= 0; i--) {
if (!gamePanels[i][pressedButtonNum - 1].isFull()) {
if (turn == Turn.RED_PLAYER) {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.RED);
turn = Turn.BLUE_PLAYER;
}
else {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.BLUE);
turn = Turn.RED_PLAYER;
}
gamePanels[i][pressedButtonNum - 1].setFull(true);
gamePanels[i][pressedButtonNum - 1].repaint();
return;
}
}
// if column is full display message to try again
JOptionPane.showMessageDialog(gamePanelsPanel, "Column " + pressedButtonNum + " is full. Try again.");
}
}
public class GamePanel extends JPanel{
private boolean isFull; // true if the panel has a disk in it. default is empty (false).
private Color diskColor; //color of disks. default is white (same as background)
public GamePanel(boolean isFull, Color diskColor) {
super();
this.isFull = isFull;
this.diskColor = diskColor;
}
public Color getDiskColor() {
return diskColor;
}
public void setDiskColor(Color diskColor) {
this.diskColor = diskColor;
}
public boolean isFull() {
return isFull;
}
public void setFull(boolean isFull) {
this.isFull = isFull;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(diskColor);
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
}
}
The problem is right here...
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
The Graphics context passed to your paintComponent method has already been translated by the components x/y position, meaning that the top/left corner of the component is always 0x0
g.fillOval(this.getWidth()/4 , this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
will probably work better
Also, calling this.setBackground(Color.WHITE); inside paintComponent is unadvisable, as it will setup a situation where by a new paint cycle will be scheduled, over and over again. Don't change the state of the UI from within a paint method
I am trying to develop a very basic "Simon says" simulator using Java GUI. I have a method that generates and returns an int[] array; for each element in the array, the Timer computer should start, call the doClick() method for the specified JButton, and wait for 1/2 a second. Each JButton is connected to an ActionListener() that changes the color of the specific button to white, activates another Timer timer, and changes the button back to its original color.
Every time I call computer.start(); within the for-loop it runs the code within ComputerListener(), but it repeats endlessly. I have added print statements so that I can see what is going on via the output on Netbeans. I have looked at similar issues on the forum, but nothing has provided a viable solution.
My question: why is my ComputerListener class repeating when computer.start(); is called within the for-loop?
package simon;
// #jagged_prospect
import java.util.Random;
import java.util.Arrays;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.basic.BasicButtonUI;
public class SIMONPanel extends JPanel{
private static final int PANEL_W=300,PANEL_H=300;
private static final int PREF_W=500,PREF_H=500;
private static final String[] CARD_LABELS={"main","info","game"};
private final JPanel gameCard,infoCard,splashCard;
private final JButton rButton,yButton,gButton,bButton;
private final int lives=3;
private CardLayout cardlayout=new CardLayout();
private JPanel cards=new JPanel(cardlayout);
private Action[] actions={new ShowMainAction(),new ShowInfoAction(),
new ShowGameAction()};
private Object source;
private Timer timer,computer;
public SIMONPanel(){
setBackground(Color.BLACK);
setLayout(new BorderLayout());
gameCard=new JPanel();
infoCard=new JPanel();
splashCard=new JPanel();
// game card panel
gameCard.setLayout(new BorderLayout());
gameCard.setPreferredSize(new Dimension(PANEL_W,PANEL_H));
JPanel gameButtonPanel=new JPanel();
gameButtonPanel.setLayout(new GridLayout(2,2));
JButton startButton=new JButton("Start");
startButton.addActionListener(new StartListener());
rButton=new JButton("red");
rButton.addActionListener(new ColorButtonListener());
rButton.setSize(50,50);
rButton.setUI((ButtonUI)BasicButtonUI.createUI(rButton));
rButton.setBackground(Color.RED);
rButton.setForeground(Color.WHITE);
yButton=new JButton("yellow");
yButton.addActionListener(new ColorButtonListener());
yButton.setSize(50,50);
yButton.setUI((ButtonUI)BasicButtonUI.createUI(yButton));
yButton.setBackground(Color.YELLOW);
gButton=new JButton("green");
gButton.addActionListener(new ColorButtonListener());
gButton.setSize(50,50);
gButton.setUI((ButtonUI)BasicButtonUI.createUI(gButton));
gButton.setBackground(Color.GREEN);
bButton=new JButton("blue");
bButton.addActionListener(new ColorButtonListener());
bButton.setSize(50,50);
bButton.setUI((ButtonUI)BasicButtonUI.createUI(bButton));
bButton.setBackground(Color.BLUE);
bButton.setForeground(Color.WHITE);
gameButtonPanel.add(gButton);
gameButtonPanel.add(rButton);
gameButtonPanel.add(yButton);
gameButtonPanel.add(bButton);
gameCard.add(gameButtonPanel,BorderLayout.CENTER);
gameCard.add(startButton,BorderLayout.SOUTH);
// splash card panel
splashCard.setLayout(new BorderLayout());
splashCard.setPreferredSize(new Dimension(PANEL_W,PANEL_H));
splashCard.setBackground(Color.BLACK);
JLabel titleLabel=new JLabel("S I M O N",SwingConstants.CENTER);
titleLabel.setFont(new Font("Niagara Solid",Font.BOLD,84));
titleLabel.setForeground(Color.WHITE);
splashCard.add(titleLabel,BorderLayout.CENTER);
// info card panel
// nothing here yet
JPanel buttonPanel=new JPanel(new GridLayout(1,0,5,0));
for(Action action : actions){
buttonPanel.add(new JButton(action));
buttonPanel.setBackground(Color.BLACK);
}
cards.add(splashCard,CARD_LABELS[0]);
cards.add(infoCard,CARD_LABELS[1]);
cards.add(gameCard,CARD_LABELS[2]);
add(cards,BorderLayout.CENTER);
add(buttonPanel,BorderLayout.SOUTH);
}
// sets uniform panel size
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
// shows the Main Menu card
private class ShowMainAction extends AbstractAction {
public ShowMainAction() {
super("Main");
}
#Override
public void actionPerformed(ActionEvent e) {
cardlayout.show(cards,CARD_LABELS[0]);
}
}
// shows the Info card
private class ShowInfoAction extends AbstractAction {
public ShowInfoAction() {
super("Info");
}
#Override
public void actionPerformed(ActionEvent e) {
cardlayout.show(cards,CARD_LABELS[1]);
}
}
// show the Game card
private class ShowGameAction extends AbstractAction {
public ShowGameAction() {
super("Game");
}
#Override
public void actionPerformed(ActionEvent e) {
cardlayout.show(cards,CARD_LABELS[2]);
}
}
private class TimerListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent event){
if(source==gButton){
gButton.setBackground(Color.GREEN);
}
else if(source==rButton){
rButton.setBackground(Color.RED);
rButton.setForeground(Color.WHITE);
}
else if(source==yButton){
yButton.setBackground(Color.YELLOW);
}
else if(source==bButton){
bButton.setBackground(Color.BLUE);
bButton.setForeground(Color.WHITE);
}
}
}
private class ColorButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent event){
source=event.getSource();
int delay=300;
timer=new Timer(delay,new TimerListener());
if(source==gButton){
gButton.setBackground(Color.WHITE);
timer.setRepeats(false);
timer.start();
}
else if(source==rButton){
rButton.setBackground(Color.WHITE);
rButton.setForeground(Color.BLACK);
timer.setRepeats(false);
timer.start();
}
else if(source==yButton){
yButton.setBackground(Color.WHITE);
timer.setRepeats(false);
timer.start();
}
else if(source==bButton){
bButton.setBackground(Color.WHITE);
bButton.setForeground(Color.BLACK);
timer.setRepeats(false);
timer.start();
}
}
}
private class StartListener implements ActionListener{
public void actionPerformed(ActionEvent event){
// calls generateSequence() to make pattern for player to replicate
// for debugging in output
System.out.println(Arrays.toString(generateSequence()));
}
}
public int[] generateSequence(){
Random ran=new Random();
ComputerListener cpu=new ComputerListener();
computer=new javax.swing.Timer(500,cpu);
int seqLen=4;
int[] gameSequence=new int[seqLen];
for(int x=0;x<seqLen;x++){
int assign=ran.nextInt(4)+1;
gameSequence[x]=assign;
}
for(int y=0;y<seqLen;y++){ // print and wait 1/2 second, repeat 3 times
computer.start();
}
//computer.stop(); // should stop ComputerListener()???
return gameSequence;
}
private class ComputerListener implements ActionListener{
public void actionPerformed(ActionEvent event){
// for debugging in output
System.out.println("it worked");
}
}
}
You're calling the computer Swing Timer's start button multiple times in a for loop, and that is not what you want to do, and in fact, the whole purpose of the timer is to help you get rid of the for loop. Instead the Timer repeats an action, and changes a state, and keeps going until its done. Consider using an int array or better an ArrayList to hold the colors that the timer should iterate through, and within that ActionListener, do the action and advance a pointer to the next position in the array or List, using that pointer to decide what action to do next. Then when the pointer is completely through the collection, stop the Timer.
For an example of exactly what I'm describing, please check out my Timer's ActionListener for an incomplete Simon game here: Method keeps window from closing
The Timer's ActionListener, annotated, is below:
private class TimerListener implements ActionListener {
private SimonPanel simonPanel; // the Simon JPanel
private int colorListIndex = 0; // index into the ArrayList of MyColor objects
private int sliceCount = 0;
private List<MyColor> myColorList; // the MyColor ArrayList -- the random colors to press
private int maxCount;
public TimerListener(SimonPanel simonPanel, List<MyColor> myColorList) {
// pass in the key fields into the program via constructor parameter
this.simonPanel = simonPanel;
this.myColorList = myColorList; // again the ArrayList that holds random MyColor objects
maxCount = myColorList.size(); // size of my list
}
#Override
public void actionPerformed(ActionEvent evt) {
// if index at the end of the list -- get out and clean up
if (colorListIndex == maxCount) {
// clear the display of "pressed" colors
for (MyColor myColor : MyColor.values()) {
simonPanel.setMyColorPressed(myColor, false);
}
// stop this timer
((Timer) evt.getSource()).stop();
return;
}
// the listener is a little complex since it must turn on colors and turn them off
// which is why I use a sliceCount int counter variable here
if (sliceCount == 0) {
// turn on the next color in the list (using the index)
MyColor myColor = myColorList.get(colorListIndex);
simonPanel.setMyColorPressed(myColor, true);
sliceCount++;
} else if (sliceCount < TIME_SLICES - 1) {
sliceCount++;
return;
} else if (sliceCount == TIME_SLICES - 1) {
sliceCount = 0;
MyColor myColor = myColorList.get(colorListIndex);
simonPanel.setMyColorPressed(myColor, false); // turn off the color
colorListIndex++; // and increment the index
return;
}
}
}
I'm trying to make a snakes and ladders game. It doesn't done yet(no swap turn, no use of ladder and snake) and have so many bug.
But My point is that
I found a problem that make me very curious(Picture Below). It about making a token move. My strategy is that I add a[10][10] array of JPanal(I named it class as Cell) on a big JPanel(I named it class as Board) whose I set its bg as a picture of snakes and ladders game from google and set the layout to gridlayout(10,10). And on every Cell there's one token which is hiding and will only reveal when press the roll button and the output point to that Cell.
This is where the problem happened.
Image of the program when execute
When I press roll button for sometimes
There's a button appear every time I press!(They are not clickable though.)
I know that my start point doesn't even on the left bottom square but where is all that jbutton came from!
This is my main class
public class Main extends JFrame {
TextField text = new TextField();
Dice dice = new Dice();
int tempi = -1, tempj = -1,sum =0;
//Main Method
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Main mPage = new Main();
mPage.setVisible(true);
}
});
}
//Constructor
public Main(){
super("Snakes and Ladders");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(1280,768);
setLocation(400,150);
setLayout(new FlowLayout(FlowLayout.LEFT,30,100));
Board board = new Board();
getContentPane().add(board);
getContentPane().add(dice);
getContentPane().add(text);
//my problem is here.
dice.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int score = Dice.rollDice();
text.setText(String.valueOf(score));
if (tempi != -1 || tempj != -1){
board.cell[9-tempi][9-tempj].fade();
}
if (tempi == -1 && tempj == -1){
sum = sum + score - 1;
}
else sum = sum + score;
tempj = sum%10;
tempi = (sum - tempj)/10;
board.cell[9-tempi][9-tempj].reveal();
}
});
pack();
setMinimumSize(this.getSize());
}
}
This is Cell class
public class Cell extends JPanel implements Cloneable {
private Token pl1 = new Token();
//constructor
public Cell(){
setOpaque(true);
setBackground(new Color(0,0,0,0));
setLayout(new GridLayout(2,2));
this.fade();
this.add(pl1);
}
public void fade(){
pl1.setVisible(false);
}
public void reveal(){
pl1.setVisible(true);
}
}
This is Token class
public class Token extends JLabel {
private BufferedImage image = null;
public Token(){
try {
image = ImageIO.read(new File("C:\\Users\\myacc\\IdeaProjects\\Snakes and Ladders\\src\\Token.png"));
} catch (IOException e) {
e.printStackTrace();
}
Image player = image.getScaledInstance(20,20,Image.SCALE_SMOOTH);
this.setIcon(new ImageIcon(player));
}
}
setBackground(new Color(0,0,0,0));
Don't use backgrounds with transparency. Swing does not know how to paint transparent backgrounds properly.
For full transparency you just make the component non-opaque:
//setOpaque(true);
//setBackground(new Color(0,0,0,0));
setOpaque(false);
If you need semi-transparency, then you need to do custom painting yourself. Check out Background With Transparency for more information on this topic.
Also don't use a TextField. That is an AWT component. Use a JTextField which is the Swing component.
So I am creating the card game Gin Rumy. I am working on the graphics, specifically, creating a panel that updates so that it always displays the vector of Card objects in the player's hand. The Cards themselves are a component that extends the canvas class. What I am asking is how to create some function updatePanel() that updates the panel as the player's hand (the vector of Card objects) changes. All I can think of is to remove the old panel from the GUI frame, create a new panel, fill that new panel with the Cards in the player's hand, and then stick that new panel onto the GUI frame. I haven't even coded this because I worry that taking a component off of the GUI frame and then slapping it back on runs the risk of disrupting the layout, and my gut tells me that creating a new panel every time is just a bad idea. So I'm asking for a better plan.
You'll also notice that the selectButton's actionPerformed() method is incomplete. What I'm trying to do is remove the cards that have focus. I understand how focus and action events work, but what I don't understand is how to search through the panel and find the card's that are selected and remove them.
I'd greatly appreciate any help on on either the updatePanel() or selectButton's actionPerformed() method, or any other useful information about adding and removing objects from panels throughout the course of a program.
// GraphicGinRumy
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.Vector;
import java.util.Random;
public class GraphicGinRumy extends GUIFrame {
protected Card card;
protected Button selectButton, newGameButton;
protected Panel controlPanel, playerPanel;
protected RandomCardDeck deck;
protected Vector<Card> playerHand;
//Pardon this weird indentation...the code didn't copy to stack overflow correctly
public GraphicGinRumy() {
super("Gin Rumy");
deck = new RandomCardDeck();
playerHand = new Vector<Card>();
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
Card selectedCard = (Card) e.getSource();
if (selectedCard.getFocused()) {
selectedCard.setFocused(false);
} else {
selectedCard.setFocused(true);
}
}
};
deck.shuffle();
rumyDeal();
//Player Panel
playerPanel = new Panel();
for (int c = 0; c < playerHand.size(); c++) {
playerPanel.add(playerHand.elementAt(c));
}
add(playerPanel, BorderLayout.CENTER);
//Control Panel
controlPanel = new Panel();
selectButton = new Button("Select");
selectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
controlPanel.add(selectButton);
newGameButton = new Button("New Game");
newGameButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
deck.shuffle();
deck.deal();
updatePanel();
}
});
controlPanel.add(newGameButton);
add(controlPanel, BorderLayout.SOUTH);
setSize(500, 100);
setVisible(true);
//__________________________________________________________________
//Game Setup
//__________________________________________________________________
//Set card values:
//2 to 9 is worth 5 pts; 10 to K is worth 10 pts; A is worth 15 pts
for (int c = 0; c < deck.getNumCards(); c++) {
card = deck.getCard(c);
if (card.getFaceValue() < 10) {
card.setValue(5);
} else if (card.getFaceValue() < Card.ACE) {
card.setValue(10);
} else {
card.setValue(15);
}
}
}
public static void main(String args[]) {
GraphicGinRumy rumy = new GraphicGinRumy();
//rumy.play();
}
protected void rumyDeal() {
//Give player and CPU 7 cards and add 1 to the discardPile
Card card;
int numCards = 7;
for (int i = 0; i < numCards; i++) {
playerHand.add(deck.deal());
}
for (int i = 0; i < numCards; i++) {
card = deck.deal();
card.setVisible(true);
cpuHand.add(card);
}
discardPile.add(deck.deal());
}
protected void updatePanel() {
playerPanel = new Panel();
}
}
Right now i change the background color of a button by using
button.setBackground(Color.WHITE);
That being an example.
But when i have a massive grid out of jbuttons (1000+), just running a for loop to change every buttons background is very, very slow. You can see the grid slowly turning white, box by box. I really don't want this
Is there a better way of changing every JButton on the grid to the same color at the same time?
This is how i am making the grid, the numbers used are only for example...
grid = new JPanel(new GridLayout(64, 64, 0, 0));
That's 4096 buttons, takes about 30+ seconds to change every button to the same color.
Edit 1: I need the buttons to be clickable, like when i click a button it turns blue for example. when all of the buttons are clicked, change the color of every button to white. Right now i have that working fine, but it is just slow to change the color of every button.
Edit 2: this is how i am changing the buttons:
new javax.swing.Timer(300, new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent e) {
if (counter >= counterMax) {
((Timer) e.getSource()).stop();
}
Color bckgrndColor = (counter % 2 == 0) ? flashColor : Color.white;
for (JButton button : gridButton) {
button.setBackground(bckgrndColor);
}
counter++;
}
}).start();
The fact that you see the boxes being repainted individually indicates that either double buffering is turned off, or that the paint code in the button UI makes use of paintImmediately().
I tested your setup with 64x64 JButtons, an made sure that all UI operations were executed in the EDT (Event Dispatch Thread). I can confirm the effect you saw, changing the background of all buttons took about 1200 ms, with every box repainted immediately.
You can bypass the immediate repaints by setting the grid to non-visible before, and to visible after you changed the backgrounds:
grid.setVisible(false);
for (Component comp : grid.getComponents()) {
comp.setBackground(color);
}
grid.setVisible(true);
This caused the grid to do only one repaint, and reduced the time to ~300ms (factor 4).
This is still too slow for frequent updates, so you're better off with a custom component which draws the grid, or a flyweight container (what trashgod suggested in the comment to your question) if you want allow the grid cells to be arbitrary components.
You can get a considerable benefit if only visible buttons need to be repainted. In the MVC approach shown below, each button listens to a model that defines it's current state. Updating the model is quite fast compared to repainting. Although startup takes a few seconds, I see updates taking < 10 ms. in the steady-state. It's not as scalable as the flyweight pattern used by JTable, illustrated here, but it may serve.
import java.awt.*;
import java.awt.event.*;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import javax.swing.*;
/** #see https://stackoverflow.com/questions/6117908 */
public class UpdateTest {
private static final int ROW = 64;
private static final int COL = 64;
private static final int MAX = COL * ROW;
private final DataModel model = new DataModel(MAX);
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new UpdateTest().create();
}
});
}
void create() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(ROW, COL));
for (int i = 0; i < MAX; i++) {
panel.add(new ViewPanel(model, i));
}
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
model.update();
System.out.println(
(System.nanoTime() - start) / (1000 * 1000));
}
});
JFrame f = new JFrame("JTextTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(panel), BorderLayout.CENTER);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
timer.start();
}
private static class ViewPanel extends JPanel implements Observer {
private final JButton item = new JButton();
private DataModel model;
private int index;
public ViewPanel(DataModel model, int i) {
this.model = model;
this.index = i;
this.add(item);
item.setText(String.valueOf(i));
item.setOpaque(true);
item.setBackground(new Color(model.get(index)));
model.addObserver(this);
}
#Override
public void update(Observable o, Object arg) {
int value = model.get(index);
item.setBackground(new Color(value));
}
}
private static class DataModel extends Observable {
private final Random rnd = new Random();
private final int[] data;
public DataModel(int n) {
data = new int[n];
fillData();
}
public void update() {
fillData();
this.setChanged();
this.notifyObservers();
}
public int get(int i) {
return data[i];
}
private void fillData() {
for (int i = 0; i < data.length; i++) {
data[i] = rnd.nextInt();
}
}
}
}