I was making a sorting visualizer and everything worked fine until I tried implementing the sort function on a button onClick. The issue is that when the sort function is called from a button, the screen doesn't refresh until the for loop is finished (normally, the sort function works perfectly).
MAIN:
public class Main {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
}
}
TEST CLASS:
public class Test implements ActionListener {
public static int WIN_WIDTH = 1500;
public static int WIN_HEIGHT = 750;
JButton shuffleButton;
JButton bubbleSort;
JFrame window;
SortArray sortArray;
public Test() throws InterruptedException {
shuffleButton = new JButton();
shuffleButton.setBounds(WIN_WIDTH + 10, 0, 160, 100);
shuffleButton.setText("Shuffle!");
bubbleSort = new JButton();
bubbleSort.setBounds(WIN_WIDTH + 10, 200, 160, 100);
bubbleSort.setText("Bubble sort!");
window = new JFrame("Sort Visualizer");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WIN_WIDTH + 200, WIN_HEIGHT);
window.setVisible(true);
sortArray = new SortArray();
window.add(shuffleButton);
window.add(bubbleSort);
window.add(sortArray);
sortArray.repaint();
sortArray.bubble_sort();
shuffleButton.addActionListener(this);
bubbleSort.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == shuffleButton){
sortArray.shuffleArray();
}else if(e.getSource() == bubbleSort){
try {
sortArray.bubble_sort();
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
}
}
}
SORT ARRAY CLASS:
public class SortArray extends JPanel {
private static int NM_NM = WIN_WIDTH;
public static int BAR_WIDTH = WIN_WIDTH / NM_NM;
private int[] array;
public SortArray() throws InterruptedException {
shuffleArray();
}
public void shuffleArray(){
Random rng = new Random();
array = new int[NM_NM];
for (int i = 0; i < array.length; i++) {
repaint();
array[i] = rng.nextInt(WIN_HEIGHT);
}
}
public void bubble_sort() throws InterruptedException {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
//bubble sort
System.out.println("sorting...");
Boolean not_sorted = true;
while (not_sorted) {
TimeUnit.MICROSECONDS.sleep(1);
not_sorted = false;
for (int i = 1; i < array.length; i++) {
if (array[i] > array[i - 1]) {
int temp = array[i];
array[i] = array[i-1];
array[i-1] = temp;
not_sorted = true;
repaint();
}
}
if (!not_sorted) {
break;
}
}
return null;
}
};
worker.execute();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D graphics2D = (Graphics2D) g;
super.paintComponent(graphics2D);
for (int j = 0; j < array.length; j++) {
graphics2D.setColor(Color.BLACK);
graphics2D.fillRect(j * BAR_WIDTH, 0, BAR_WIDTH, array[j]);
}
}
}
Ok, I solved it, basically all I did was surround all the commands inside the bubbleSort() function with a SwingWorker and then executed it. (This creates a background thread). I edited the code to the right one.
Related
I have been making a sorting algorithm visualizer in Java using Java Swing and AWT and coded insertion sort to see if it would be able to work. The algorithm works in the sense that when this program is run you can shuffle and sort the algorithm using insertion short but you don't actually see the algorithm in action. It just happens instantly and have been looking for ways to add some sort of delay but I can't find any sources to help me.
I set it up with 4 java classes, the main which just init's the window, the window class, array visualizer class, and the insertion sort class.
Window.java:
public class Window implements ActionListener {
//Window width
protected static int WINDOW_WIDTH = 1980;
//Window height
protected static int WINDOW_HEIGHT = 1080;
private int delay = 100;
//This will draw all our rectangles
protected static ArrayVisualizer arrayVisualizer;
//Make a new JFrame
JFrame window = new JFrame();
JButton shuffleBut = new JButton("Shuffle");
JButton startSort = new JButton("Start Sorting");
public Window() {
initWindow();
}
private void addButtons() {
//Shuffle Button
shuffleBut.setBounds(100, 50, 100, 100);
shuffleBut.setBackground(Color.white);
shuffleBut.addActionListener(this);
startSort.setBounds(500, 50, 100, 100);;
startSort.setBackground(Color.white);
startSort.addActionListener(taskPerformer);
window.add(shuffleBut);
window.add(startSort);
}
private void initWindow() {
ImageIcon logo = new ImageIcon();
window = new JFrame();
window.setTitle("JAlgorithms");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
window.setResizable(false);
addButtons();
arrayVisualizer = new ArrayVisualizer();
window.add(arrayVisualizer);
arrayVisualizer.repaint(); //Will call paint component method
window.pack();
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == shuffleBut) {
arrayVisualizer.shuffle(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent event) {
if(event.getSource() == startSort) {
arrayVisualizer.sort(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
};
}
ArrayVisualizer.java:
public class ArrayVisualizer extends JPanel {
protected static int[] array;
private final int REC_WIDTH = 1; //1980 rectangles
private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH;
public ArrayVisualizer() {
array = new int[NUMBER_OF_RECS];
generateRandom(array);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g.create();
graphics.setColor(Color.orange);
for(int i = 0; i < NUMBER_OF_RECS; i++) {
int height = array[i] * 4; //Done for scaling
int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation
int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation
graphics.fillRect(recX, recY, REC_WIDTH, height);
}
}
//This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript
#Override
public Dimension getPreferredSize() {
return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT);
}
//Creates a random unsorted array with numbers 1-200
protected void generateRandom(int[] array) {
Random number = new Random();
for(int i = 0; i < NUMBER_OF_RECS; i++) {
array[i] = number.nextInt(200);
}
}
protected void shuffle(int[] array) {
generateRandom(array);
}
protected void sort(int[] array) {
InsertionSort.insertionSort(array);
}
}
InsertionSort.java:
public class InsertionSort {
public static void insertionSort(int arr[])
{
int n = arr.length;
for (int i = 1; i < n; ++i) {
int key = arr[i];
int j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
}
UPDATE (Here is the modified code, I added selection sort as well and heavily modified the suggestion I checkmarked):
Main.java:
public class Main {
public static void main(String[] args) {
//Opens window
Window window = new Window();
}
}
Window.java:
import java.awt.Color;
import java.awt.Image;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.EventQueue;
import javax.swing.ImageIcon;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Window implements ActionListener {
//Window width
protected static int WINDOW_WIDTH = 1440;
//Window height
protected static int WINDOW_HEIGHT = 1080;
//This will draw all our rectangles
protected static ArrayVisualizer arrayVisualizer;
//Make a new JFrame
JFrame window = new JFrame();
JButton shuffleBut = new JButton("Shuffle");
JButton startSort = new JButton("Start Sorting");
public Window() {
initWindow();
}
private void addButtons() {
//Shuffle Button
shuffleBut.setBounds(100, 50, 100, 100);
shuffleBut.setBackground(Color.white);
shuffleBut.addActionListener(this);
startSort.setBounds(500, 50, 100, 100);;
startSort.setBackground(Color.white);
window.add(shuffleBut);
window.add(startSort);
}
private void initWindow() {
ImageIcon logo = new ImageIcon();
window = new JFrame();
window.setTitle("JAlgorithms");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
window.setResizable(false);
addButtons();
startSort.addActionListener(taskPerformer);
arrayVisualizer = new ArrayVisualizer();
window.add(arrayVisualizer);
arrayVisualizer.repaint(); //Will call paint component method
window.pack();
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == shuffleBut) {
arrayVisualizer.shuffle(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if(event.getSource() == startSort) {
if(!timer.isRunning()) { //If timer is not running
timer.setInitialDelay(0); //Set initial delay
timer.start(); //Start the timer
}
}
}
};
ActionListener sortWithDelay = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(sorted(ArrayVisualizer.array)) { //If it is sorted
timer.stop(); //Stop the timer
return;
} else {
arrayVisualizer.sort(ArrayVisualizer.array); //If it is not sorted continue the sort
arrayVisualizer.repaint(); //Called after each swap
}
}
};
private int delay = 10; //Milliseconds
private Timer timer = new Timer(delay, sortWithDelay);
private boolean sorted(int[] array) {
for(int i = 0; i < array.length - 1; i++) {
if(array[i] > array[i+1]) {
return false;
}
}
return true;
}
}
ArrayVisualizer.java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Array;
import javax.swing.*;
import java.util.*;
public class ArrayVisualizer extends JPanel {
private static final long serialVersionUID = 1L;
private InsertionSort insertionSort = new InsertionSort();
private SelectionSort selectionSort = new SelectionSort();
protected static int[] array;
private final int REC_WIDTH = 1; //1980 rectangles
private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH;
private final int[] barColors;
public ArrayVisualizer() {
setBackground(Color.black);
array = new int[NUMBER_OF_RECS];
barColors = new int[NUMBER_OF_RECS];
generateRandom(array);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g.create();
graphics.setColor(Color.white);
for(int i = 0; i < NUMBER_OF_RECS; i++) {
int height = array[i] * 4; //Done for scaling
int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation
int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation
graphics.fillRect(recX, recY, REC_WIDTH, height);
}
}
//This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript
#Override
public Dimension getPreferredSize() {
return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT);
}
//Creates a random unsorted array with numbers 1-200
protected void generateRandom(int[] array) {
Random number = new Random();
for(int i = 0; i < NUMBER_OF_RECS; i++) {
array[i] = number.nextInt(200);
}
}
protected void shuffle(int[] array) {
generateRandom(array);
}
protected void sort(int[] array) {
selectionSort.sortWithDelay(array);
}
}
InsertionSort.java:
public class InsertionSort {
private int i = 1;
private int j = 0;
public void sortWithDelay(int arr[])
{
int n = arr.length;
if(i < n) {
int key = arr[i];
j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while(j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
++i;
}
}
}
SelectionSort.java:
public class SelectionSort {
private int i = 1;
private int j = 0;
public void sortWithDelay(int arr[])
{
int n = arr.length - 1;
if(i < n) {
int min_index = i;
for(int j = i + 1; j < n; j++) {
if(arr[j] < arr[min_index]) {
min_index = j;
}
}
int temp = arr[min_index];
arr[min_index] = arr[i];
arr[i] = temp;
++i;
}
}
}
Call an action with a swing timer every second that will return an intermediate array. Then paint this intermediate array. Repeat until the array is sorted.
To obtain an intermediate array, modify the InsertionSort from a static method to a class method that will store the variable i so that we can resume sorting when called again.
public class Window implements ActionListener {
//Window width
protected static int WINDOW_WIDTH = 1980;
//Window height
protected static int WINDOW_HEIGHT = 1080;
//This will draw all our rectangles
protected static ArrayVisualizer arrayVisualizer;
//Make a new JFrame
JFrame window = new JFrame();
JButton shuffleBut = new JButton("Shuffle");
JButton startSort = new JButton("Start Sorting");
public Window() {
initWindow();
}
private void addButtons() {
//Shuffle Button
shuffleBut.setBounds(100, 50, 100, 100);
shuffleBut.setBackground(Color.white);
shuffleBut.addActionListener(this);
startSort.setBounds(500, 50, 100, 100);;
startSort.setBackground(Color.white);
startSort.addActionListener(taskPerformer);
window.add(shuffleBut);
window.add(startSort);
}
private void initWindow() {
ImageIcon logo = new ImageIcon();
window = new JFrame();
window.setTitle("JAlgorithms");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
window.setResizable(false);
addButtons();
arrayVisualizer = new ArrayVisualizer();
window.add(arrayVisualizer);
arrayVisualizer.repaint(); //Will call paint component method
window.pack();
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == shuffleBut) {
arrayVisualizer.shuffle(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
}
private int delay = 1000; // delay is 1s
private Timer timer = new Timer(delay, sortWithDelay);
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if(event.getSource() == startSort) {
// Start the timer that shows a swap every second
if(!timer.isRunning()) {
timer.setInitialDelay(0);
timer.start();
}
}
}
};
ActionListener sortWithDelay = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if(isSorted(ArrayVisualizer.array)) {
timer.stop();
return;
}
arrayVisualizer.sort(ArrayVisualizer.array);
arrayVisualizer.repaint();
}
};
private boolean isSorted(int[] arr) {
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
return false;
}
}
return true;
}
}
public class ArrayVisualizer extends JPanel {
private InsertionSort insertionSort = new InsertionSort();
protected static int[] array;
private final int REC_WIDTH = 1; //1980 rectangles
private final int NUMBER_OF_RECS = Window.WINDOW_WIDTH / REC_WIDTH;
public ArrayVisualizer() {
array = new int[NUMBER_OF_RECS];
generateRandom(array);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics = (Graphics2D) g.create();
graphics.setColor(Color.orange);
for(int i = 0; i < NUMBER_OF_RECS; i++) {
int height = array[i] * 4; //Done for scaling
int recX = i + (REC_WIDTH - 1) * i; //Read fillRect documentation
int recY = Window.WINDOW_HEIGHT - height; //Read fillRect documentation
graphics.fillRect(recX, recY, REC_WIDTH, height);
}
}
//This will return the Dimension of the actual rectangles. i.e the rectangles will only exist when this exists, almost like a canvas in javascript
#Override
public Dimension getPreferredSize() {
return new Dimension(Window.WINDOW_WIDTH, Window.WINDOW_HEIGHT);
}
//Creates a random unsorted array with numbers 1-200
protected void generateRandom(int[] array) {
Random number = new Random();
for(int i = 0; i < NUMBER_OF_RECS; i++) {
array[i] = number.nextInt(200);
}
}
protected void shuffle(int[] array) {
generateRandom(array);
}
protected void sort(int[] array) {
insertionSort.sortWithDelay(array);
}
}
public class InsertionSort {
private int i = 1;
public void sortWithDelay(int arr[])
{
int n = arr.length;
if(i < n) {
int key = arr[i];
int j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while(j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
++i;
}
}
}
I want to create a bunch of dots and move each of them in a random direction. Problem is that all dots are heading the same direction, so they look like a single dot even if there are a hundred of them. I tried to google it but found nothing. I bet it's just my stupidity, but I really don't know what to do with it.
Thanks for helping me.
Dot class:
public class Dot extends JPanel {
private PVector pos;
private PVector vel;
private PVector acc;
private Brain brain;
private boolean dead = false;
public Dot(){
this.brain = new Brain(400);
this.pos = new PVector(Main.width/2, Main.height/2);
this.vel = new PVector(0, 0);
this.acc = new PVector(0, 0);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval((int)pos.x, (int)pos.y, 4, 4);
}
public void show(JFrame frame) {
frame.add(this);
}
public void move() {
if(brain.directions.length > brain.step) {
acc = brain.directions[brain.step];
brain.step++;
} else {
dead = true;
}
vel.add(acc);
vel.limit(5);
pos.add(vel);
}
public void update() {
if(!dead) {
move();
if(pos.y < 2 || pos.y < 2 || pos.x > Main.width - 2 || pos.y > Main.height - 2) {
dead = true;
}
}
}
}
Brain class:
public class Brain {
public PVector[] directions;
public int step = 0;
public Brain(int size) {
directions = new PVector[size];
randomize();
}
private void randomize() {
for(int i = 0; i < directions.length; i++) {
Random random = new Random();
float randomAngle = (float)(random.nextFloat() * 2 * Math.PI);
directions[i] = PVector.fromAngle(randomAngle);
}
}
}
Population class:
public class Population {
public Dot[] dots;
public Population(int size) {
dots = new Dot[size];
for(int i = 0; i < size; i++) {
dots[i] = new Dot();
}
}
public void show(JFrame frame) {
for(int i = 0; i < dots.length; i++) {
dots[i].show(frame);
}
}
public void update() {
for(int i = 0; i < dots.length; i++) {
dots[i].update();
}
}
}
Gui class:
public class Gui {
JFrame frame = new JFrame();
Population test = new Population(100);
public Gui() {
startup();
}
private void startup() {
frame.setSize(Main.width, Main.height);
frame.setLocation(200, 200);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
ScheduledExecutorService executor = Executors
.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
update();
}
}, 0, 1000 / 60, TimeUnit.MILLISECONDS);
test.show(frame);
}
private void update() {
frame.repaint();
test.update();
}
}
Each "Dot" instance is a separate panel. You have a 100 Dots, so you have 100 panels. But: the layout manager set on JFrame only shows one of the Dots (probably the last Dot).
There may be a way to make this work so that the 100 panels are all painted on top of each other, by playing around with layout managers, but the "proper" (in my opinion) way to fix this is to make Population extend JPanel, not Dot. That way, Population is a single component that displays the entire population.
These are the changes you need to make:
// add "extends JPanel"
public class Population extends JPanel {
// Change "show" into "paintComponent"
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i = 0; i < dots.length; i++) {
dots[i].show(g);
}
}
}
// Remove "extends JPanel"
public class Dot {
// Change "paintComponent" into "show"
public void show(Graphics g) {
g.fillOval((int)pos.x, (int)pos.y, 4, 4);
}
}
public class Gui {
private void startup() {
// remove this:
// test.show(frame);
// add this:
frame.add(test);
}
}
When I use normal JPanel initialized inside main instead of extended class. The button is added to a panel without problem and after launch is displayed in a center of a frame (default layout).
I would like to be able to add buttons inside an extended class. This problem occurs in Screen class aswell, where I need a Play Again button or Next Level button. The Screen is class extended by a JPanel too and JButtons are initialized inside the constructor aswell.
I'm not sure if the wrong part is in way of adding the components or in writing a code for a JPanel.
Here is the code:
Main:
public static void main(String[] args) {
// window - class JFrame
theFrame = new JFrame("Brick Breaker");
// game panel initialization
GamePanel gamePanel = new GamePanel(1);
theFrame.getContentPane().add(gamePanel);
// base settings
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.setLocationRelativeTo(null);
theFrame.setResizable(false);
theFrame.setSize(WIDTH, HEIGHT);
theFrame.setVisible(true);
}
GamePanel Class:
public class GamePanel extends JPanel {
// Fields
boolean running;
boolean clicked = false;
private int rows = 8, colms = 11, N = rows * colms, level; // numbers of rows and columns
private int colmsC, rowsC;
private BufferedImage image;
private Graphics2D g;
private MyMouseMotionListener theMouseListener;
private MouseListener mouseListener;
private int counter = 0;
// entities
Ball theBall;
Paddle thePaddle;
Bricle[] theBricks;
Screen finalScreen;
public GamePanel(int level) {
// buttons
JButton pause = new JButton(" P ");
add(pause);
this.level = level;
init(level);
}
public void init(int level) {
// level logic
this.rowsC = level + rows;
this.colmsC = level + colms;
int count = rowsC * colmsC;
thePaddle = new Paddle();
theBall = new Ball();
theBall.setY(thePaddle.YPOS - thePaddle.getHeight() + 2);
theBall.setX(thePaddle.getX() + thePaddle.getWidth() / 2 - 5);
theBricks = new Bricle[count];
theMouseListener = new MyMouseMotionListener();
addMouseMotionListener(theMouseListener);
mouseListener = new MyMouseListener();
addMouseListener(mouseListener);
// make a canvas
image = new BufferedImage(BBMain.WIDTH, BBMain.HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// specific Bricks initialized
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k] = new Bricle(row, col);
k++;
}
}
running = true;
}
public void playGame() {
while (running) {
//update
if (clicked) {
update();
}
// draw
draw();
// display
repaint();
// sleep
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// update loop ( like playGame )
public void update() {
// ball moving
checkCollisions();
theBall.update();
}
public void draw() {
// background
g.setColor(Color.WHITE);
g.fillRect(0,0, BBMain.WIDTH, BBMain.HEIGHT-20);
// the bricks
int k = 0;
for (int row = 0; row < rowsC; row++) {
for (int col = 0; col < colmsC; col++) {
theBricks[k].draw(g, row, col);
k++;
}
}
// the ball and the paddle
theBall.draw(g);
thePaddle.draw(g);
// counter
String countString = new Integer(this.counter).toString();
g.drawString(countString, 20, 20);
// WIN / LOOSE SCREEN
if (this.counter == this.N * 20) {
win();
}
if (theBall.getRect().getY() + theBall.getRect().getHeight() >= BBMain.HEIGHT) {
loose();
}
}
public void paintComponent(Graphics g) {
// retype
Graphics2D g2 = (Graphics2D) g;
// draw image
g2.drawImage(image, 0, 0, BBMain.WIDTH, BBMain.HEIGHT, null);
// dispose
g2.dispose();
}
public void pause() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"GAME PAUSED");
}
public void win() {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU WIN");
}
public void loose () {
this.running = false;
finalScreen = new Screen(this.level, counter);
finalScreen.draw(g,"YOU LOOSE");
}
public void addScore() {
this.counter += 20;
theBall.setDY(theBall.getDY() - 0.001);
}
// Mouse Listeners
private class MyMouseListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
clicked = true;
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
private class MyMouseMotionListener implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if (clicked)
thePaddle.mouseMoved(e.getX());
}
}
}
http://www.youtube.com/watch?v=M0cNsmjK33E
I want to develop something similar to the link above using Java Swing. I have the sorting method and did while repaint but when I triggered the sorting, instead of showing the bars slowly sorting itself, it freezes and then unfreezes when the array has been fully sorted.
How do I fix this?
Edit: sorry forgot about the codes. its a very simple gui. and another class for sorting which sorts the whole array
public class SortGUI {
JFrame frame;
int frameWidth = 1000, frameHeight = 1000;
int panelWidth, panelHeight;
DrawPanel panel;
JPanel panel2;
JScrollPane scroll;
JViewport view;
static int[] S = new int[50000];
public static void main(String[] args) throws InterruptedException {
SortGUI app = new SortGUI();
initializeArray();
app.go();
}
public static void initializeArray()
{
for (int i = 0; i < S.length; i++) {
S[i] = (int) (Math.random() * 16581375);
}
}
public void go() throws InterruptedException {
//Frame
frame = new JFrame();
frame.setSize(frameWidth, frameHeight);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//panel
panel = new DrawPanel();
scroll = new JScrollPane(panel,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
//Layout
frame.add(scroll);
frame.addKeyListener(new keyListener());
while(true)
{
panel.repaint();
}
}
public class DrawPanel extends JPanel
{
public DrawPanel()
{
this.setPreferredSize(new Dimension(50000,930));
}
public void paintComponent(Graphics g)
{
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
for(int i = 0; i < S.length; i++)
{
int red = S[i] / 65025;
int green = (S[i] > 65025)? S[i] % 65025 : 0;
int blue = green;
blue %= 255;
green /= 255;
g.setColor(new Color(red,green,blue));
g.fillRect(i, 900 - (S[i] / 18500), 1, S[i] / 18500);
}
}
}
public class keyListener implements KeyListener{
public void keyTyped(KeyEvent ke) {
}
public void keyPressed(KeyEvent ke) {
if(ke.getKeyChar() == '1')
{
sorter.bubbleSort(S);
}
}
public void keyReleased(KeyEvent ke) {
}
}
}
Note: I started writing this before the question was deleted
Most likely your using some looping mechanism and praying that each iteration, the ui with be updated. That's a wrong assumption. The UI will not be update until the loop is finished. What you are doing is what we refer to as blocking the Event Dispatch Thread(EDT)
See How to use a Swing Timer. Make "iterative" updates in the ActionListener call back. For instance, if you want to animate a sorting algorithm, you need to determine what needs to be updated per "iteration" of the timer callback. Then each iteration repaint the ui.
So your Timer timer could look something like
Timer timer = new Timer(40, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if (sortingIsDone()) {
((Timer)e.getSource()).stop();
} else {
sortOnlyOneItem();
}
repaint();
}
});
Your sortOnlyOneItem method should only, well, perform a sort for just one item. And have some sort of flag to check if the sorting is done, then stop the timer.
Other notes:
You should be calling super.paintComponent in the paintComponent method, if you aren't going to paint the background yourself. Generally I always do though.
Here's a complete example. I'm glad you figured it out on your own. I was working on this example before I saw that you got it.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Collections;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SelectionSortAnimate extends JPanel {
private static final int NUM_OF_ITEMS = 20;
private static final int DIM_W = 400;
private static final int DIM_H = 400;
private static final int HORIZON = 350;
private static final int VERT_INC = 15;
private static final int HOR_INC = DIM_W / NUM_OF_ITEMS;
private JButton startButton;
private Timer timer = null;
private JButton resetButton;
Integer[] list;
int currentIndex = NUM_OF_ITEMS - 1;
public SelectionSortAnimate() {
list = initList();
timer = new Timer(200, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (isSortingDone()) {
((Timer) e.getSource()).stop();
startButton.setEnabled(false);
} else {
sortOnlyOneItem();
}
repaint();
}
});
startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
resetButton = new JButton("Reset");
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
list = initList();
currentIndex = NUM_OF_ITEMS - 1;
repaint();
startButton.setEnabled(true);
}
});
add(startButton);
add(resetButton);
}
public boolean isSortingDone() {
return currentIndex == 0;
}
public Integer[] initList() {
Integer[] nums = new Integer[NUM_OF_ITEMS];
for (int i = 1; i <= nums.length; i++) {
nums[i - 1] = i;
}
Collections.shuffle(Arrays.asList(nums));
return nums;
}
public void drawItem(Graphics g, int item, int index) {
int height = item * VERT_INC;
int y = HORIZON - height;
int x = index * HOR_INC;
g.fillRect(x, y, HOR_INC, height);
}
public void sortOnlyOneItem() {
int currentMax = list[0];
int currentMaxIndex = 0;
for (int j = 1; j <= currentIndex; j++) {
if (currentMax < list[j]) {
currentMax = list[j];
currentMaxIndex = j;
}
}
if (currentMaxIndex != currentIndex) {
list[currentMaxIndex] = list[currentIndex];
list[currentIndex] = currentMax;
}
currentIndex--;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < list.length; i++) {
drawItem(g, list[i], i);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(DIM_W, DIM_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Sort");
frame.add(new SelectionSortAnimate());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
So I have been coding this little UI for a while and for some reason the repaint() method seems to cause the image generated on the screen to flicker and flash. Is there a way to prevent this?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Timer;
public class userInterfaceSimple extends JFrame implements ActionListener
{
static ArrayList<Creature> list = new ArrayList<Creature>();
static JButton next = new JButton("Begin");
static Timer timer = new Timer();
static JFrame frame = new JFrame();
final static JPanel pane = new JPanel();
public static void main(String[] args)
{
new userInterfaceSimple();
}
public userInterfaceSimple()
{
super("Toy Planet: Life, a Simulation");
setSize(1000,1000);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(pane, BorderLayout.CENTER);
next.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if(next.getText().equals("Begin"))
{
next.setText("Stop");
timer.scheduleAtFixedRate(new TimerTask()
{
#Override
public void run()
{
checkRules();
repaint();
}
}, 250, 250);
}
else
{
next.setText("Begin");
timer.cancel();
timer = new Timer();
}
}
});
add(next, BorderLayout.SOUTH);
for(int j = 0; j < 3; j ++)
{
Creature orgArc = new Creature();
for(int i = 0; i < 20; i++)
{
list.add(new Creature(orgArc.gene));
}
}
sortList();
}
public void paint(Graphics g)
{
super.paint(g);
for(int i = 0; i < list.size(); i++)
{
g.setColor(list.get(i).getColor());
int size = list.get(i).getSize();
g.fillOval(list.get(i).getX() - size/2, list.get(i).getY() - size/2,size,size);
}
}
public static void checkRules()
{
int size = list.size();
for(int i = 0; i < size; i++)
{
try
{
for(int j = 0; j < size; j++)
{
boolean alreadyMoved = false;
if(!(list.get(i)).equals(list.get(j)))
{
int x = list.get(j).getX() - (list.get(i)).getX();
int y = list.get(j).getY() - (list.get(i)).getY();
double z = Math.sqrt(x*x + y*y);
//Passive Interaction Rules
//Sight Check
if(list.get(j).getSense() > z)
{
list.get(j).moveTo(list.get(i));
alreadyMoved = true;
}
if(z <= 1.5)
{
//Active Interaction Rules
//Breeding Check
if(list.get(j).isCompatible(list.get(i)))
{
list.add(list.get(j).breed(list.get(i)));
sortList();
}
//Eating Check
else if(list.get(j).getAggro() > 15 || list.get(j).getDiet() > 3)
{
list.get(j).eat(list.get(i));
}
}
}
//If no checks fire then move randomly
if(alreadyMoved == false)
{
Organism temp = new Organism();
list.get(j).moveTo(temp);
}
}
//Hunger Check
list.get(i).hungerCheck();
}
catch(Exception e)
{
}
list.get(i).validate();
size = size - validateList();
}
/*for(int i = 0; i < list.size(); i ++) //for debugging.
{
System.out.println(list.get(i).getInfo());
}
System.out.println("----");*/
}
public static int getClosestCreature(Organism org)
{
int x,y;
double z;
double tempZ = 100000;
int closest = 0;
for(int i = 0; i < list.size(); i++)
{
if(!(list.get(i)).equals(org))
{
x = org.getX() - (list.get(i)).getX();
y = org.getY() - (list.get(i)).getY();
z = Math.sqrt(x*x + y*y);
if(z < tempZ)
{
closest = i;
tempZ = z;
}
}
}
return (closest);
}
public static int validateList()
{
int netLoss = 0;
for(int i = 0; i < list.size(); i++)
{
if((list.get(i)).getAlive() == false)
{
list.remove(i);
netLoss = netLoss + 1;
}
}
return netLoss;
}
public static void sortList()
{
Creature temp;
for(int i = 0; i < list.size(); i ++)
{
for(int j = 1; j < list.size() - i; j++)
{
if(list.get(j - 1).getSpeed() > list.get(j).getSpeed())
{
temp = list.get(j - 1);
list.set(j - 1, list.get(j));
list.set(j, temp);
}
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
Thanks for you help!
Yes, it is simple: Double Buffering should fix it. For more reference, visit http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
First you should create private BufferedImage bufferImage;
In your public void paint(Graphics g) you should paint stuff into this bufferImage, and then, when bufferImage is filled, paint it g.drawImage(bufferImage, 0, 0, null);
public void paint(Graphics g) {
Graphics2D gr = bufferImage.createGraphics();
for(int i = 0; i < list.size(); i++)
{
gr.setColor(list.get(i).getColor());
int size = list.get(i).getSize();
gr.fillOval(list.get(i).getX() - size/2, list.get(i).getY() - size/2,size,size);
}
super(g);
g.drawImage(bufferImage, 0, 0, null);
}
Not compiled, but something like this should work.