I´m trying to do a simple 7 segment display and to test it I want to make a simple animation which displays the numbers in order with a separation between number draw of 1 second and this must run indefinitely but I cannot figure out how to draw the display itself (Implementing the animation, without the animation I know how to print the numbers one by one).
PD: I give you all the source code just for you to have it if anyone needs it but the "animation" is located on line 120 where I define the Thread to run the animation.
Source code:
public class MainWindow extends JFrame {
private JPanel contentPane;
private CustomRectangle custRect = null;
private JButton btnCancel;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow frame = new MainWindow();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MainWindow() {
getContentPane().setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setUndecorated(true);
setSize(400, 400);
contentPane = new JPanel();
contentPane.setLayout(null);
custRect = new CustomRectangle(getContentPane().getSize());
getContentPane().add(custRect);
getContentPane().add(getBtnCancel(), BorderLayout.SOUTH);
}
private Color complementaryColor(Color background) {
int alpha = background.getAlpha();
int red = background.getRed();
int blue = background.getBlue();
int green = background.getGreen();
// find compliments
red = (~red) & 0xff;
blue = (~blue) & 0xff;
green = (~green) & 0xff;
return new Color(red, green, blue, alpha);
}
private JButton getBtnCancel() {
if (btnCancel == null) {
btnCancel = new JButton("Cancel");
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
btnCancel.setBackground(null);
btnCancel.setBorder(null);
btnCancel.setForeground(complementaryColor(getContentPane().getBackground()));
btnCancel.setFocusPainted(false);
}
return btnCancel;
}
private class CustomRectangle extends JComponent {
private byte[] nums = new byte[] { 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B };
private double mainWidth = 0.0;
private double mainHeight = 0.0;
private Graphics2D g2d = null;
public CustomRectangle(Dimension size) {
mainWidth = size.getWidth();
mainHeight = size.getHeight();
}
public byte[] getNums() {
return nums;
}
#Override
public void paintComponent(Graphics g) {
g2d = (Graphics2D) g;
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
int seconds = calendar.get(Calendar.SECOND);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Thread th = new Thread() {
public void run() {
while(true) {
for(int i = 0; i < nums.length; i++)
generalCall(nums[i]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};th.start();
}
public void generalCall(byte val) {
drawAHoursHigh(val);
drawBHoursHigh(val);
drawCHoursHigh(val);
drawDHoursHigh(val);
drawEHoursHigh(val);
drawFHoursHigh(val);
drawGHoursHigh(val);
}
private Color getColor(byte val, int shift) {
int r = 255;
int g = 0;
int b = 0;
int a = 255 * (val >> shift) & 1;
System.out.println("R: " + r + " G: " + g + " B: " + b + " Alpha: " + a);
if (a == 0)
return new Color(15, 15, 15);
else
return new Color(r, g, b);
}
private void drawAHoursHigh(byte val) {
System.out.print("A: ");
Shape a = new RoundRectangle2D.Double(60, 20, 78, 18, 10, 10);
g2d.setColor(getColor(val, 6));
g2d.fill(a);
g2d.draw(a);
}
private void drawBHoursHigh(byte val) {
System.out.print("B: ");
Shape b = new RoundRectangle2D.Double(140, 40, 18, 98, 10, 10);
g2d.setColor(getColor(val, 5));
g2d.fill(b);
g2d.draw(b);
}
private void drawCHoursHigh(byte val) {
System.out.print("C: ");
Shape c = new RoundRectangle2D.Double(140, 160, 18, 98, 10, 10);
g2d.setColor(getColor(val, 4));
g2d.fill(c);
g2d.draw(c);
}
private void drawDHoursHigh(byte val) {
System.out.print("D: ");
Shape d = new RoundRectangle2D.Double(60, 260, 78, 18, 10, 10);
g2d.setColor(getColor(val, 3));
g2d.fill(d);
g2d.draw(d);
}
private void drawEHoursHigh(byte val) {
System.out.print("E: ");
Shape e = new RoundRectangle2D.Double(40, 160, 18, 98, 10, 10);
g2d.setColor(getColor(val, 2));
g2d.fill(e);
g2d.draw(e);
}
private void drawFHoursHigh(byte val) {
System.out.print("F: ");
Shape f = new RoundRectangle2D.Double(40, 40, 18, 98, 10, 10);
g2d.setColor(getColor(val, 1));
g2d.fill(f);
g2d.draw(f);
}
private void drawGHoursHigh(byte val) {
System.out.print("G: ");
Shape g = new RoundRectangle2D.Double(60, 140, 78, 18, 10, 10);
g2d.setColor(getColor(val, 0));
g2d.fill(g);
g2d.draw(g);
}
}
}
First, go read:
Performing Custom Painting
Painting in AWT and Swing
for more information on how painting works in Swing.
Next, go read:
Concurrency in Swing
to get a better understanding of the concurrency issue in Swing - the short version, Swing is NOT thread safe and is single threaded. This raises a number of issues when you want to perform repeated actions that update the UI.
Then, go read How to Use Swing Timers for a feasible solution.
Let's start identifying the core problems...
This...
contentPane = new JPanel();
contentPane.setLayout(null);
is generally a bad idea, but you're not using it anyway, so it's just white noise
This...
custRect = new CustomRectangle(getContentPane().getSize());
is erroneous, because at the time you request the size of he contentPane, it's not been realised or layout on the screen, so it's result is 0x0. You shouldn't be relying on this anyway and if you want to know the size of the container, request it when you actually need it.
Next...
private Graphics2D g2d = null;
You should never maintain a reference to any graphics context you didn't create. In Swing, the context is shared amongst all the components been painted, meaning that it could be dirty, with other content, or clipped to exclude the area you want painted.
And finally....
#Override
public void paintComponent(Graphics g) {
g2d = (Graphics2D) g;
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
int seconds = calendar.get(Calendar.SECOND);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Thread th = new Thread() {
public void run() {
while(true) {
for(int i = 0; i < nums.length; i++)
generalCall(nums[i]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};th.start();
}
😱
paintComponent will be called when ever Swing thinks your component needs to be repainted, when first realised, this could be a number of times in quick succession, meaning you now have a number of Threads running.
Paint should do one job, paint the current context of the component, nothing else. In your case, a Swing Timer would be a more viable and safer solution.
And, cleaned up, it might look something like...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class MainWindow extends JFrame {
private CustomRectangle custRect = null;
private JButton btnCancel;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow frame = new MainWindow();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MainWindow() {
getContentPane().setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
custRect = new CustomRectangle();
getContentPane().add(custRect);
getContentPane().add(getBtnCancel(), BorderLayout.SOUTH);
}
private Color complementaryColor(Color background) {
int alpha = background.getAlpha();
int red = background.getRed();
int blue = background.getBlue();
int green = background.getGreen();
// find compliments
red = (~red) & 0xff;
blue = (~blue) & 0xff;
green = (~green) & 0xff;
return new Color(red, green, blue, alpha);
}
private JButton getBtnCancel() {
if (btnCancel == null) {
btnCancel = new JButton("Cancel");
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
btnCancel.setBackground(null);
btnCancel.setBorder(null);
btnCancel.setForeground(complementaryColor(getContentPane().getBackground()));
btnCancel.setFocusPainted(false);
}
return btnCancel;
}
private class CustomRectangle extends JComponent {
private byte[] nums = new byte[]{0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B};
public CustomRectangle() {
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
}
public byte[] getNums() {
return nums;
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < nums.length; i++) {
generalCall(g2d, nums[i]);
}
}
public void generalCall(Graphics2D g2d, byte val) {
drawAHoursHigh(g2d, val);
drawBHoursHigh(g2d, val);
drawCHoursHigh(g2d, val);
drawDHoursHigh(g2d, val);
drawEHoursHigh(g2d, val);
drawFHoursHigh(g2d, val);
drawGHoursHigh(g2d, val);
}
private Color getColor(byte val, int shift) {
int r = 255;
int g = 0;
int b = 0;
int a = 255 * (val >> shift) & 1;
System.out.println("R: " + r + " G: " + g + " B: " + b + " Alpha: " + a);
if (a == 0) {
return new Color(15, 15, 15);
} else {
return new Color(r, g, b);
}
}
private void drawAHoursHigh(Graphics2D g2d, byte val) {
System.out.print("A: ");
Shape a = new RoundRectangle2D.Double(60, 20, 78, 18, 10, 10);
g2d.setColor(getColor(val, 6));
g2d.fill(a);
g2d.draw(a);
}
private void drawBHoursHigh(Graphics2D g2d, byte val) {
System.out.print("B: ");
Shape b = new RoundRectangle2D.Double(140, 40, 18, 98, 10, 10);
g2d.setColor(getColor(val, 5));
g2d.fill(b);
g2d.draw(b);
}
private void drawCHoursHigh(Graphics2D g2d, byte val) {
System.out.print("C: ");
Shape c = new RoundRectangle2D.Double(140, 160, 18, 98, 10, 10);
g2d.setColor(getColor(val, 4));
g2d.fill(c);
g2d.draw(c);
}
private void drawDHoursHigh(Graphics2D g2d, byte val) {
System.out.print("D: ");
Shape d = new RoundRectangle2D.Double(60, 260, 78, 18, 10, 10);
g2d.setColor(getColor(val, 3));
g2d.fill(d);
g2d.draw(d);
}
private void drawEHoursHigh(Graphics2D g2d, byte val) {
System.out.print("E: ");
Shape e = new RoundRectangle2D.Double(40, 160, 18, 98, 10, 10);
g2d.setColor(getColor(val, 2));
g2d.fill(e);
g2d.draw(e);
}
private void drawFHoursHigh(Graphics2D g2d, byte val) {
System.out.print("F: ");
Shape f = new RoundRectangle2D.Double(40, 40, 18, 98, 10, 10);
g2d.setColor(getColor(val, 1));
g2d.fill(f);
g2d.draw(f);
}
private void drawGHoursHigh(Graphics2D g2d, byte val) {
System.out.print("G: ");
Shape g = new RoundRectangle2D.Double(60, 140, 78, 18, 10, 10);
g2d.setColor(getColor(val, 0));
g2d.fill(g);
g2d.draw(g);
}
}
}
Related
I am making a Color game in which when I click on start a chain of colors will be shown. Now I have to click on my 4 color buttons and match the shown chain.
If I win, the chain adds a color (Level up) if I lose the chain loses a color (Level down).
My problems right now :
If I overlap the shown colors to match, I will only see the first index of the Array. I need to make it to show all Indexes of the JButton [] like every 2 seconds. I tried it with timer but it still show only the first one.
If I level up, I can add a color to the need to match array, but If I level down, I cant delete the last added color. I know there is no way to remove something from an Array, but is there a workaround. Right now I am making the player lose completely if he doesnt match the scheme.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class Farbgedaechtnisspiel {
private static int startLevel = 1;
private static int gameLevel = 3;
private static List<Integer> gameArray = new ArrayList<>();
private static List<Integer> userArray = new ArrayList<>();
private static int save = 0;
private static int count = 0;
private static JButton [] b;
private static final Color red = new Color(255, 0, 0);
private static final Color green = new Color(0, 255, 0);
private static final Color blue = new Color(0, 0, 255);
private static final Color yellow = new Color(255, 255, 0);
private static JFrame frame = new JFrame("Farbgedächtnisspiel");
private static JLabel levelTxt;
public static void main(String[] args) {
// create mainframe
new Farbgedaechtnisspiel();
}
public Farbgedaechtnisspiel () {
createFrame(frame);
levelTxt = new JLabel("Level: " + startLevel);
frame.add(levelTxt);
levelTxt.setFont(new Font("Times New Roman", Font.ITALIC, 40));
// create 4 color buttons + start/exit
b = new JButton[7];
for(int i = 0;i<b.length;i++){
b[i] = new JButton();
frame.add(b[i]);
}
// create button layout
createButtons();
// button listeners
b[4].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// start Game -> show taskArray
levelTxt.setBounds(550, 200, 200, 200);
startGame(gameLevel);
}});
b[5].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
b[0].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
userArray.add(1);
save++;
}
}
);
b[1].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
userArray.add(2);
save++;
}
});
b[2].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
userArray.add(3);
save++;
}
});
b[3].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
userArray.add(4);
save++;
}
});
b[6].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
boolean result = compareArrays(userArray, gameArray);
if (result){
gameLevel++;
save = 0;
startLevel++;
levelTxt.setText("Level: " + startLevel);
} else {
JOptionPane.showMessageDialog(frame, "Sie haben verloren");
new Farbgedaechtnisspiel();
System.exit(1);
}
startGame(gameLevel);
}
});
// make frame visible
frame.setVisible(true);
}
public static void startGame(int gameLevel){
int x = 100;
JButton [] tmp = new JButton[gameLevel];
for(int i = 0;i<tmp.length;i++) {
tmp[i] = new JButton();
tmp[i].setBackground(randomColor());
tmp[i].setOpaque(true);
tmp[i].setBorderPainted(false);
frame.add(tmp[i]);
if (tmp[i].getBackground() == red) {
gameArray.add(1);
} else if (tmp[i].getBackground() == blue) {
gameArray.add(2);
} else if (tmp[i].getBackground() == green) {
gameArray.add(3);
} else if (tmp[i].getBackground() == yellow) {
gameArray.add(4);
}
}
for (int i = 0; i < tmp.length; i++) {
tmp[i].setBounds(x, 50, 100, 100);
x+=100;
}
// for (int i = 0; i < tmp.length; i++) {
// tmp[i].setBounds(450, 50, 300, 299);
// x += 100;
// }
}
public static void createButtons() {
int x = 0;
for (int i = 0; i < b.length; i++) {
b[i].setBounds(x, 0, 200, 200);
x += 200;
}
b[0].setBackground(red);
b[0].setOpaque(true);
b[0].setBorderPainted(false);
b[1].setBackground(blue);
b[1].setOpaque(true);
b[1].setBorderPainted(false);
b[2].setBackground(green);
b[2].setOpaque(true);
b[2].setBorderPainted(false);
b[3].setBackground(yellow);
b[3].setOpaque(true);
b[3].setBorderPainted(false);
b[4].setBounds(150,600,300,200);
b[4].setText("Start");
b[5].setBounds(450, 600, 300, 200);
b[5].setText("Beenden");
b[6].setBounds(750, 600, 300, 200);
b[6].setText("Continue");
b[0].setBounds(200, 400, 200, 200);
b[1].setBounds(400, 400, 200, 200);
b[2].setBounds(600, 400, 200, 200);
b[3].setBounds(800, 400, 200, 200);
}
public static boolean compareArrays(List<Integer> userArray, List<Integer> gameArray){
return userArray.equals(gameArray);
}
public static void createFrame(JFrame frame){
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1200,800);
frame.setLocationRelativeTo(null);
frame.setLayout(null);
frame.setResizable(false);
}
public static Color randomColor () {
Random rand = new Random();
int randomNum = rand.nextInt((4 - 1) + 1) + 1;
return switch (randomNum) {
case 1 -> red;
case 2 -> green;
case 3 -> blue;
case 4 -> yellow;
default -> null;
};
}
}```
In my code i generate randoms integer between 0 and 60 and i draw lines based on these.
I just want my lines fit the ordinate vertical line without touching my randoms integer... I guess it's kind of a mathematics problem but i'm really stuck here!
Here's my code first:
Windows.java:
public class Window extends JFrame{
Panel pan = new Panel();
JPanel container, north,south, west;
public JButton ip,print,cancel,start,ok;
JTextArea timeStep;
JLabel legend;
double time=0;
double temperature=0.0;
Timer chrono;
public static void main(String[] args) {
new Window();
}
public Window()
{
System.out.println("je suis là");
this.setSize(1000,400);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setTitle("Assignment2 - CPU temperature");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
container = new JPanel(new BorderLayout());
north = new JPanel();
north.setLayout(new BorderLayout());
ip = new JButton ("New");
north.add(ip, BorderLayout.WEST);
print = new JButton ("Print");
north.add(print,BorderLayout.EAST);
JPanel centerPanel = new JPanel();
centerPanel.add(new JLabel("Time Step (in s): "));
timeStep = new JTextArea("0.1",1,5);
centerPanel.add(timeStep);
start = new JButton("OK");
ListenForButton lForButton = new ListenForButton();
start.addActionListener(lForButton);
ip.addActionListener(lForButton);
print.addActionListener(lForButton);
centerPanel.add(start);
north.add(centerPanel, BorderLayout.CENTER);
west = new JPanel();
JLabel temp = new JLabel("°C");
west.add(temp);
container.add(north, BorderLayout.NORTH);
container.add(west,BorderLayout.WEST);
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.setVisible(true);
}
private class ListenForButton implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==start)
{
time=Double.parseDouble(timeStep.getText());
System.out.println(time);
chrono = new Timer((int)(1000*time),pan);
chrono.start();
}
if(e.getSource()==ip)
{
JPanel options = new JPanel();
JLabel address = new JLabel("IP Address:");
JTextField address_t = new JTextField(15);
JLabel port = new JLabel("Port:");
JTextField port_t = new JTextField(5);
options.add(address);
options.add(address_t);
options.add(port);
options.add(port_t);
int result = JOptionPane.showConfirmDialog(null, options, "Please Enter an IP Address and the port wanted", JOptionPane.OK_CANCEL_OPTION);
if(result==JOptionPane.OK_OPTION)
{
System.out.println(address_t.getText());
System.out.println(port_t.getText());
}
}
if(e.getSource()==print)
{
chrono.stop();
}
}
}
}
Panel.java:
public class Panel extends JPanel implements ActionListener {
int rand;
int lastrand=0;
ArrayList<Integer> randL = new ArrayList<>();
ArrayList<Integer> tL = new ArrayList<>();
int lastT = 0;
Color red = new Color(255,0,0);
Color green = new Color(0,200,0);
Color blue = new Color (0,0,200);
Color yellow = new Color (200,200,0);
int max=0;
int min=0;
int i,k,inc = 0,j;
int total,degr,moyenne;
public Panel()
{
super();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1.8f));
g2.drawLine(20, 20, 20, this.getHeight()-50);
g2.drawLine(20, this.getHeight()-50, this.getWidth()-50, this.getHeight()-50);
g2.drawLine(20, 20, 15, 35);
g2.drawLine(20, 20, 25, 35);
g2.drawLine(this.getWidth()-50, this.getHeight()-50, this.getWidth()-65, this.getHeight()-45);
g2.drawLine(this.getWidth()-50, this.getHeight()-50, this.getWidth()-65, this.getHeight()-55);
g.drawString("10", 0, this.getHeight()-85);
g.drawString("20", 0, this.getHeight()-125);
g.drawString("30", 0, this.getHeight()-165);
g.drawString("40", 0, this.getHeight()-205);
g.drawString("50", 0, this.getHeight()-245);
g2.drawString("Maximum: ", 20, this.getHeight()-20);
g2.drawString(Integer.toString(max), 80, this.getHeight()-20);
g2.drawString("Minimum: ", 140, this.getHeight()-20);
g2.drawString(Integer.toString(min), 200, this.getHeight()-20);
g2.drawString("Average: ", 260, this.getHeight()-20);
g2.drawString(Integer.toString(moyenne), 320, this.getHeight()-20);
g2.setColor(red);
g2.drawLine(500, this.getHeight()-25, 540, this.getHeight()-25);
g2.setColor(new Color(0,0,0));
g2.drawString(": Maximum", 560, this.getHeight()-20);
g2.setColor(blue);
g2.drawLine(640, this.getHeight()-25, 680, this.getHeight()-25);
g2.setColor(new Color(0,0,0));
g2.drawString(": Minimum", 700, this.getHeight()-20);
g2.setColor(green);
g2.drawLine(780, this.getHeight()-25, 820, this.getHeight()-25);
g2.setColor(new Color(0,0,0));
g2.drawString(": Average", 840, this.getHeight()-20);
if(!randL.isEmpty()){
g2.setColor(red);
g2.drawLine(15, this.getHeight()-50-max, this.getWidth()-50,this.getHeight()-50-max);
g2.setColor(blue);
g2.drawLine(15, this.getHeight()-50-min, this.getWidth()-50,this.getHeight()-50-min);
g2.setColor(green);
g2.drawLine(15, this.getHeight()-50-moyenne, this.getWidth()-50,this.getHeight()-50-moyenne);
}
for(i = 0; i<tL.size(); i++){
int temp = randL.get(i);
int t = tL.get(i);
g2.setColor(new Color(0,0,0));
g2.drawLine(20+t, this.getHeight()-50-temp, 20+t, this.getHeight()-50);
// Ellipse2D circle = new Ellipse2D.Double();
//circle.setFrameFromCenter(20+t, this.getHeight()-50, 20+t+2, this.getHeight()-52);
}
for(j=0;j<5;j++)
{
inc=inc+40;
g2.setColor(new Color(0,0,0));
g2.drawLine(18, this.getHeight()-50-inc, 22, this.getHeight()-50-inc);
}
inc=0;
}
#Override
public void actionPerformed(ActionEvent e) {
rand = (int)(Math.random() * (60));
lastT += 80;
randL.add(rand);
tL.add(lastT);
Object obj = Collections.max(randL);
max = (int) obj;
Object obj2 = Collections.min(randL);
min = (int) obj2;
if(!randL.isEmpty()) {
degr = randL.get(k);
total += degr;
moyenne=total/randL.size();
}
k++;
if(randL.size()>=12)
{
randL.removeAll(randL);
tL.removeAll(tL);
lastT = 0;
k=0;
degr=0;
total=0;
moyenne=0;
}
repaint();
}
}
And here it what i gives me :
Sorry it's a real mess!
Any thoughts ?
Thanks.
You need to stop working with absolute/magical values, and start using the actual values of the component (width/height).
The basic problem is a simple calculation which takes the current value divides it by the maximum value and multiples it by the available width of the allowable area
int length = (value / max) * width;
value / max generates a percentage value of 0-1, which you then use to calculate the percentage of the available width of the area it will want to use.
The following example places a constraint (or margin) on the available viewable area, meaning all the lines need to be painted within that area and not use the entire viewable area of the component
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawLine {
public static void main(String[] args) {
new DrawLine();
}
public DrawLine() {
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 {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int margin = 20;
int width = getWidth() - (margin * 2);
int height = getHeight() - (margin * 2);
int x = margin;
for (int index = 0; index < 4; index++) {
int y = margin + (int)(((index / 3d) * height));
int length = (int)(((index + 1) / 4d) * width);
g2d.drawLine(x, y, x + length, y);
}
g2d.dispose();
}
}
}
Okay I have been working on this memory game applet in java for a while now and I have all the sorting and matching algorithms all figured out, I am just having a wretched time trying to get my GUI to function properly. Whenever I click on one of the "cards" to "flip", I end up with a column of the cards being created while their backs counterparts remain under the cards until you go over it with the cursor. It is all very frustrating as I am not quite sure why half of this is happening or how to stop it. Here is my Display class:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.*;
public class Display extends JPanel implements MouseListener {
private String[] Colors = Card.validColors();
private String[] Shapes = Card.validShapes();
private Component[] place;
private JButton[][] buttonGrid= new JButton[6][6];
private Rectangle[][] triggers = new Rectangle[6][6];
private Board game;
private Polygon star = new Polygon();
private Card pick;
private boolean turnPhase2 = false;
private Font serifNames = new Font(Font.SERIF, Font.PLAIN, 18);
private Font serifCards = new Font(Font.SERIF, Font.ROMAN_BASELINE, 36);
private JPanel panel = new JPanel();
public Display() {
this.setBackground(Color.BLACK);
this.setLayout(new GridBagLayout());
panel.setSize(590, 410);
panel.setLayout(new GridLayout(6, 6, 10, 10));
panel.setOpaque(false);
generateStar();
buildBoard();
fillButtonArray();
this.addMouseListener(this);
this.add(panel);
System.out.println(getFontMetrics(serifCards));
System.out.println(getFontMetrics(serifNames));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
place = panel.getComponents();
for (int i = 0; i < place.length; i++) {
paintBack(g, place[i].getX() + 25, place[i].getY() + 35);
}
displayNames(g);
displayTurn(g);
if (game.flippedCard() != null) {
int[] xy = game.flippedCardLocation();
paintCard(g, game.flippedCard(), xy[0], xy[1]);
}
}
/**
* This method builds the game board.
*
*
*/
private void buildBoard() {
game = new Board(buildDeck());
}
/**
* This method creates a "deck" of cards with which we can create the game board.
*
* #return deck Returns the deck of Card objects in the form of an ArrayList.
*/
private ArrayList<Card> buildDeck() {
ArrayList<Card> deck = new ArrayList<Card>();
for (int i = 0; i < Colors.length; i++) {
for (int s = 0; s < Shapes.length; s++) {
Card first = new Card(Shapes[s], Colors[i]);
Card second = new Card(Shapes[s], Colors[i]);
deck.add(first);
deck.add(second);
}
}
System.out.println(deck.size());
return deck;
}
private void fillButtonArray() {
for (int i = 0; i < 6; i++) {
for (int n = 0; n < 6; n++) {
JButton button = new JButton();
button.setPreferredSize(new Dimension(90, 60));
button.addMouseListener(this);
button.setOpaque(false);
button.setContentAreaFilled(false);
button.setBorderPainted(false);
buttonGrid[i][n] = button;
}
}
fillGrid();
}
private void fillGrid() {
panel.setBounds(25, 35, panel.getSize().width, panel.getSize().height);
int count = 0;
for (int i = 0; i < 6; i++) {
for (int s = 0; s < 6; s++) {
panel.add(buttonGrid[i][s]);
place = panel.getComponents();
int x = panel.getComponent(count).getBounds().x;
int y = panel.getComponent(count).getBounds().y;
Rectangle rect = new Rectangle(x, y, 90, 60);
triggers[i][s] = rect;
}
}
}
private void paintBack(Graphics g, int x, int y) {
g.setColor(Color.WHITE);
g.drawRoundRect(x, y, 90, 60, 2, 4);
g.fillRoundRect(x, y, 90, 60, 2, 4);
g.setColor(Color.BLACK);
g.setFont(serifCards);
g.drawString("M", x + 28, y + 42);
}
private void paintCard(Graphics g, Card card, int x, int y) {
g.setColor(Color.GRAY);
g.drawRoundRect(x, y, 90, 60, 2, 4);
g.fillRoundRect(x, y, 90, 60, 2, 4);
String color = card.getColor();
String shape = card.getShape();
if (shape.equals("Star")) {
g.setColor(pickColor(color));
star.translate(x + 25, y + 10);
g.drawPolygon(star);
g.fillPolygon(star);
}
else if (shape.equals("Circle")) {
g.setColor(pickColor(color));
g.drawOval(x + 25, y + 10, 40, 40);
g.fillOval(x + 25, y + 10, 40, 40);
}
else if (shape.equals("Square")) {
g.setColor(pickColor(color));
g.drawRect(x + 25, y + 10, 40, 40);
g.fillRect(x + 25, y + 10, 40, 40);
}
}
private void displayNames(Graphics g) {
g.setFont(serifNames);
int[] scores = game.getCurrentScores();
for (int i = 0; i < scores.length; i++) {
if (i == 0) {
g.setColor(Color.CYAN);
g.drawString("Cyan: " + scores[i], 10, 24);
}
else if (i == 1) {
g.setColor(Color.ORANGE);
g.drawString("Orange: " + scores[i], 560, 24);
}
else if (i == 2) {
g.setColor(Color.MAGENTA);
g.drawString("Magenta: " + scores[i], 10, 470);
}
else {
g.setColor(Color.WHITE);
g.drawString("White: " + scores[i], 569, 470);
}
}
}
private void displayTurn(Graphics g) {
int player = game.getCurrentPlayer();
if (player == 0) {
g.setColor(Color.CYAN);
String str = "Cyan's Turn";
g.drawString(str, 640 / 2 - 48, 24);
//System.out.println(getFontMetrics(serifNames).stringWidth(str) / 2 + " Cyan");
}
else if (player == 1) {
g.setColor(Color.ORANGE);
String str = "Orange's Turn";
g.drawString(str, 640 / 2 - 52, 24);
//System.out.println(getFontMetrics(serifNames).stringWidth(str) / 2 + " Orange");
}
else if (player == 2) {
g.setColor(Color.MAGENTA);
String str = "Magenta's Turn";
g.drawString(str, 640 / 2 - 57, 24);
//System.out.println(getFontMetrics(serifNames).stringWidth(str) / 2 + " Magenta");
}
else {
g.setColor(Color.WHITE);
String str = "White's Turn";
g.drawString(str, 640 / 2 - 47, 24);
//System.out.println(getFontMetrics(serifNames).stringWidth(str) / 2 + " White");
}
}
private void findTrigger(int x, int y) {
for (int i = 0; i < 6; i++) {
if () {
}
for (int s = 0; s < 6; s++) {
Rectangle rectTest = triggers[i][s];
if (x >= rectTest.getMinX() &&
x <= rectTest.getMaxX() &&
y >= rectTest.getMinY() &&
y <= rectTest.getMaxY()) {
Graphics g = getGraphics();
paintCard(g, game.flip(i,s), buttonGrid[i][s].getBounds().x, buttonGrid[i][s].getBounds().y);
break;
}
}
}
}
private void generateStar() {
star.addPoint(20, 0);
star.addPoint(25, 15);
star.addPoint(40, 15);
star.addPoint(28, 24);
star.addPoint(32, 40);
star.addPoint(20, 30);
star.addPoint(8, 40);
star.addPoint(12, 24);
star.addPoint(0, 15);
star.addPoint(15, 15);
}
private Color pickColor(String color) {
if (color.equals("Black")) {
return Color.BLACK;
}
if (color.equals("Yellow")) {
return Color.YELLOW;
}
if (color.equals("Green")) {
return Color.GREEN;
}
if (color.equals("Blue")) {
return Color.BLUE;
}
if (color.equals("Red")) {
return Color.RED;
}
if (color.equals("Purple")) {
return new Color(128, 0, 255);
}
return null;
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse Clicked");
int x = e.getX();
System.out.println(x + " is x");
int y = e.getY();
System.out.println(y + " is Y");
System.out.println(panel.getWidth() + 25);
System.out.println(panel.getHeight() + 35);
System.out.println("Finding the trigger rectangle");
findTrigger(x, y);
}
#Override
public void mouseEntered(MouseEvent e) {
//System.out.println("Mouse Entered");
}
#Override
public void mouseExited(MouseEvent e) {
//System.out.println("Mouse Exited");
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse Pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse Released");
}
}
Some side notes is that the actual game is handled by the board object and has all the methods needed to create and run a multiplayer memory game and the Card object only contains two strings of the shape and color of what is to be matched by the game. And finally the last class is the Memory class which I will provide:
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JApplet;
public class Memory extends JApplet {
private Display _theDisplay;
final int width = 640;
final int height = 480;
private Action reDraw = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
repaint();
}
};
public Memory() {
_theDisplay = new Display();
}
public void init() {
setSize(width, height);
setBackground(Color.BLACK);
this.add(_theDisplay);
}
}
Please any tips would be incredibly helpful, thanks in advance!
Your graphics appear messed. Problems I see:
You call getGraphics() on a component to draw with its Graphics context, but please understand that a Graphics object obtained in this way will not persist, and can thus mess up or even cause a NPE to be thrown.
Better to do all-passive Graphics via your paintComponent(...) method. If you need any pre-made drawings, do these in BufferedImages, and draw the BufferedImages in the JComponent's paintComponent(...) method.
Rather than have your Display JPanel do all the painting of the cards and the backs, I suggest that each Card be its own separate object with its own state, that paints itself correctly depending on its state. You may wish to have this extend a JComponent, or have it be a logical entity that is then painted by your Display JPanel, up to you, but separate out the logic from the display to simplify your coding and debugging.
A major problem looks to be in your findTrigger(...) method and that is where you should concentrate your efforts. You should use the mouseClick to change the state of the logical Card (as described above) that is clicked and then call repaint() on the Display JPanel (this) if the Cards are painted in paintComponent(...).
Else if the Cards paint themselves, consider having them be JLabels and simply swap ImageIcons, likely the easiest way to "flip" cards.
Your main problem is that of a program mis-behavior. I have not seen the cause of this on a quick overview of your code, and suggest that you use a debugger or println statements to first and foremost try to isolate the problem.
I'm trying to get the mouse position and light up a button when the curser is over it. However the mouse position isn't updating right. I think the y position is messed up because currently if i am far above the button it lights up.
Heres my code that is incorrect:
public void mouseMoved(MouseEvent e) {
Window.mse = new Point((e.getX()) + ((Frame.size.width - Window.myWidth)/2), (e.getY()) + (Frame.size.height - (Window.myHeight)/2));
}
This is the Window file:
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
public class Window extends JPanel implements Runnable {
public Thread thread = new Thread(this);
public static Image[] tileset_ground = new Image[100];
public static Image[] tileset_air = new Image[100];
public static Image[] tileset_resources = new Image[100];
public static int myWidth, myHeight;
public static boolean isFirst = true;
public static Point mse = new Point(0, 0);
public static Room room;
public static Levels levels;
public static Shop shop;
public Window(Frame frame) {
frame.addMouseListener(new KeyHandler());
frame.addMouseMotionListener(new KeyHandler());
thread.start();
}
public void define() {
room = new Room();
levels = new Levels();
shop = new Shop();
tileset_resources[0] = new ImageIcon("resources/cell.png").getImage();
levels.loadLevels(new File("levels/level1.level"));
for(int i=0;i<tileset_ground.length; i++) {
tileset_ground[i] = new ImageIcon("resources/tileset_ground.png").getImage();
tileset_ground[i] = createImage(new FilteredImageSource(tileset_ground[i].getSource(), new CropImageFilter(0, 32 * i, 32, 32)));
}
for(int i=0;i<tileset_air.length; i++) {
tileset_air[i] = new ImageIcon("resources/tileset_air.png").getImage();
tileset_air[i] = createImage(new FilteredImageSource(tileset_air[i].getSource(), new CropImageFilter(0, 32 * i, 32, 32)));
}
}
public void paintComponent(Graphics g) {
if(isFirst) {
myWidth = getWidth();
myHeight = getHeight();
define();
isFirst = false;
}
g.setColor(new Color(70, 70, 70));
g.fillRect(0, 0, myWidth, myHeight);
g.setColor(new Color(0, 0, 0));
g.drawLine(room.block[0][0].x, room.block[room.worldHeight - 1][0].y + room.blockSize, room.block[0][room.worldWidth - 1].x + room.blockSize, room.block[room.worldHeight - 1][0].y + room.blockSize);
g.drawLine(room.block[0][0].x, room.block[room.worldHeight - 1][0].y + 1 + room.blockSize, room.block[0][room.worldWidth - 1].x + room.blockSize, room.block[room.worldHeight - 1][0].y + 1 + room.blockSize);
room.draw(g); //Draws room
shop.draw(g); //Draws shop
}
public void run() {
while(true) {
if(!isFirst) {
room.physic();
}
repaint();
try {
Thread.sleep(1);
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
Simply add a MouseListener to the JButton. Or better still add a ChangeListener to the button's model, and in it, call isRollover() on the model.
I have a problem with my repaint in the method move. I dont know what to doo, the code is below
import java.awt.*;
import java.io.*;
import java.text.*;
import java.util.*;
import javax.sound.sampled.*;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.lang.*;
public class bbb extends JPanel
{
public Stack<Integer> stacks[];
public JButton auto, jugar, nojugar;
public JButton ok, ok2;
public JLabel info = new JLabel("Numero de Discos: ");
public JLabel instruc = new JLabel("Presiona la base de las torres para mover las fichas");
public JLabel instruc2 = new JLabel("No puedes poner una pieza grande sobre una pequenia!");
public JComboBox numeros = new JComboBox();
public JComboBox velocidad = new JComboBox();
public boolean seguir = false, parar = false, primera = true;
public int n1, n2, n3;
public int click1 = 0;
public int opcion = 1, tiempo = 50;
public int op = 1, continuar = 0, cont = 0;
public int piezas = 0;
public int posx, posy;
public int no;
public bbb() throws IOException
{
stacks = new Stack[3];
stacks[0] = new Stack<Integer>();
stacks[1] = new Stack<Integer>();
stacks[2] = new Stack<Integer>();
setPreferredSize(new Dimension(1366, 768));
ok = new JButton("OK");
ok.setBounds(new Rectangle(270, 50, 70, 25));
ok.addActionListener(new okiz());
ok2 = new JButton("OK");
ok2.setBounds(new Rectangle(270, 50, 70, 25));
ok2.addActionListener(new vel());
add(ok2);
ok2.setVisible(false);
auto = new JButton("Automatico");
auto.setBounds(new Rectangle(50, 80, 100, 25));
auto.addActionListener(new a());
jugar = new JButton("PLAY");
jugar.setBounds(new Rectangle(100, 100, 70, 25));
jugar.addActionListener(new play());
nojugar = new JButton("PAUSE");
nojugar.setBounds(new Rectangle(100, 150, 70, 25));
nojugar.addActionListener(new stop());
setLayout(null);
info.setBounds(new Rectangle(50, 50, 170, 25));
info.setForeground(Color.white);
instruc.setBounds(new Rectangle(970, 50, 570, 25));
instruc.setForeground(Color.white);
instruc2.setBounds(new Rectangle(970, 70, 570, 25));
instruc2.setForeground(Color.white);
add(instruc);
add(instruc2);
add(jugar);
add(nojugar);
jugar.setVisible(false);
nojugar.setVisible(false);
add(info);
info.setVisible(false);
add(ok);
ok.setVisible(false);
add(auto);
numeros.setBounds(new Rectangle(210, 50, 50, 25));
numeros.addItem(1);
numeros.addItem(2);
numeros.addItem(3);
numeros.addItem(4);
numeros.addItem(5);
numeros.addItem(6);
numeros.addItem(7);
numeros.addItem(8);
numeros.addItem(9);
numeros.addItem(10);
add(numeros);
numeros.setVisible(false);
velocidad.setBounds(new Rectangle(150, 50, 100, 25));
velocidad.addItem("Lenta");
velocidad.addItem("Intermedia");
velocidad.addItem("Rapida");
add(velocidad);
velocidad.setVisible(false);
}
public void Mover(int origen, int destino)
{
for (int i = 0; i < 3; i++)
{
System.out.print("stack " + i + ": ");
for (int n : stacks[i])
{
System.out.print(n + ";");
}
System.out.println("");
}
System.out.println("de <" + origen + "> a <" + destino + ">");
stacks[destino].push(stacks[origen].pop());
System.out.println("");
this.validate();
this.repaint();
}
public void hanoi(int origen, int destino, int cuantas)
{
while (parar)
{
}
if (cuantas <= 1)
{
Mover(origen, destino);
}
else
{
hanoi(origen, 3 - (origen + destino), cuantas - 1);
Mover(origen, destino);
hanoi(3 - (origen + destino), destino, cuantas - 1);
}
}
public void paintComponent(Graphics g)
{
ImageIcon fondo = new ImageIcon("fondo.jpg");
g.drawImage(fondo.getImage(), 0, 0, 1366, 768, null);
g.setColor(new Color((int) (Math.random() * 254),
(int) (Math.random() * 255),
(int) (Math.random() * 255)));
g.fillRect(0, 0, 100, 100);
g.setColor(Color.white);
g.fillRect(150, 600, 250, 25);
g.fillRect(550, 600, 250, 25);
g.fillRect(950, 600, 250, 25);
g.setColor(Color.red);
g.fillRect(270, 325, 10, 275);
g.fillRect(270 + 400, 325, 10, 275);
g.fillRect(270 + 800, 325, 10, 275);
int x, y, top = 0;
g.setColor(Color.yellow);
x = 150;
y = 580;
for (int ii : stacks[0])
{
g.fillRect(x + ((ii * 125) / 10), y - (((ii) * 250) / 10), ((10 - ii) * 250) / 10, 20);
}
x = 550;
y = 580;
for (int ii : stacks[1])
{
g.fillRect(x + ((ii * 125) / 10), y - (((ii) * 250) / 10), ((10 - ii) * 250) / 10, 20);
}
x = 950;
y = 580;
for (int ii : stacks[2])
{
g.fillRect(x + ((ii * 125) / 10), y - (((ii) * 250) / 10), ((10 - ii) * 250) / 10, 20);
}
System.out.println("ENTRO");
setOpaque(false);
}
private class play implements ActionListener //manual
{
public void actionPerformed(ActionEvent algo)
{
parar = false;
if (primera = true)
{
hanoi(0, 2, no);
primera = false;
}
}
}
private class stop implements ActionListener //manual
{
public void actionPerformed(ActionEvent algo)
{
parar = true;
}
}
private class vel implements ActionListener //manual
{
public void actionPerformed(ActionEvent algo)
{
if (velocidad.getSelectedItem() == "Lenta")
{
tiempo = 150;
}
else if (velocidad.getSelectedItem() == "Intermedia")
{
tiempo = 75;
}
else
{
tiempo = 50;
}
ok2.setVisible(false);
jugar.setVisible(true);
nojugar.setVisible(true);
}
}
private class a implements ActionListener //auto
{
public void actionPerformed(ActionEvent algo)
{
auto.setVisible(false);
info.setVisible(true);
numeros.setVisible(true);
ok.setVisible(true);
op = 3;
}
}
private class okiz implements ActionListener //ok
{
public void actionPerformed(ActionEvent algo)
{
no = Integer.parseInt(numeros.getSelectedItem().toString());
piezas = no;
if (no > 0 && no < 11)
{
info.setVisible(false);
numeros.setVisible(false);
ok.setVisible(false);
for (int i = no; i > 0; i--)
{
stacks[0].push(i);
}
opcion = 2;
if (op == 3)
{
info.setText("Velocidad: ");
info.setVisible(true);
velocidad.setVisible(true);
ok2.setVisible(true);
}
}
else
{
}
repaint();
}
}
}
the code of the other class that calls the one up is below:
import java.awt.*;
import java.io.*;
import java.net.URL;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.border.*;
import java.lang.*;
import java.awt.event.*;
public class aaa extends JPanel
{
private ImageIcon Background;
private JLabel fondo;
public static void main(String[] args) throws IOException
{
JFrame.setDefaultLookAndFeelDecorated(true);
final JPanel cp = new JPanel(new BorderLayout());
JFrame frame = new JFrame ("Torres de Hanoi");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setSize(550,550);
frame.setVisible(true);
bbb panel = new bbb();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
Assuming that you see the progress in the println statements but not on the screen, it is because a call to repaint is not synchronous. Swing has a special thread for handling UI - called Event Dispatch Thread. A call to repaint is handled by that thread, but asynchronously - after all the current events scheduled on that thread have been processed.
When you call hanoi in your actionPerformed, that is done on the same UI thread. What happens is that until your recursion is fully done, repaint() calls are just queued. Once the recursion completes (and all stacks have been moved in the model), the UI thread processes all repaint() requests, painting - what i assume - the final state.
What you need to do is to separate the model processing into a separate worker thread. On every recursion step, issue a repaint() call and sleep for a few hundred milliseconds. This will allow the UI thread to repaint the current state of the model and let the user actually trace the progress.