It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Okay so I have been working on this code for awhile that shows how the sorting algorithms work. Right now I have it working where it sorts multiple graphs with the same sort but I need each graph to do a different sort at the same time. I have been researching and trying to solve this for days and now I just have tunnel vision. I'll post my code in case my explanation was confusing. I feel like this could benefit a lot of people working with java graphics and any help would be appreciated.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.Scanner;
public class Sort extends Applet {
/** Constructor. Only for starting the sorting animation as applet. */
public Sort() {}
/** For starting the sorting animation as application. */
public static void main(String[] argv) {
Frame _aFrame = new Frame("Sort Animations");
_aFrame.setBackground(Color.white);
_aFrame.setPreferredSize(new Dimension(2700,1000));
Scanner in = new Scanner(System.in);
int sizeOfArray;
System.out.println("How many numbers do you want to sort?");
sizeOfArray = in.nextInt();
_aFrame.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
}
);
_aFrame.setLayout(new BorderLayout());
_aFrame.add(new SortPanel(sizeOfArray));
_aFrame.pack();
_aFrame.setVisible(true);
}
}
class SortPanel extends Panel implements Runnable {
/** button triggering the sort animation */
private Button aSortButton_ = new Button("sort");
/** choice item for selecting the sort algorithm */
private Choice aChoice_ = new Choice();
/** component for handling the animation */
private ArrayCanvas anArrayCanvas_;
/////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*
* #param length no of elements of the array
* #param aBarColor the color the elements representing bars will be drawn in
*
* #exception IllegalArgumentException if the array <code>length</code> is
* to big to display (ie <code>length</code> is bigger than
* <code>BAR_WIDTH</code> or <code>BAR_HEIGHT</code>).
*/
public SortPanel(int arraySize) {
setLayout(new BorderLayout());
aSortButton_.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Thread(SortPanel.this).start();
}
}
);
anArrayCanvas_ = new ArrayCanvas(arraySize);
for (int i=0; i<ArrayCanvas.SORT_NAMES.length; ++i)
aChoice_.add(ArrayCanvas.SORT_NAMES[i]);
// add buttons at top:
Panel _aTopPanel = new Panel();
_aTopPanel.add(aSortButton_);
add(_aTopPanel, BorderLayout.NORTH);
// add choice and ArrayCanvas below:
Panel _aPanel = new Panel();
_aPanel.setLayout(new BorderLayout());
Panel _aChoicePanel = new Panel();
_aChoicePanel.add(aChoice_);
_aPanel.add(_aChoicePanel, BorderLayout.NORTH);
_aPanel.add(anArrayCanvas_, BorderLayout.CENTER);
add(_aPanel, BorderLayout.CENTER);
Panel _aBottomPanel = new Panel();
add(_aBottomPanel, BorderLayout.SOUTH);
}
/////////////////////////////////////////////////////////////////////////////
/** Runs the sorting animation. */
public void run() {
aSortButton_.setEnabled(false);
double time = System.currentTimeMillis();
anArrayCanvas_.sort(aChoice_.getSelectedItem());
aSortButton_.setEnabled(true);
}
}
class ArrayCanvas extends Canvas {
/** Labels of available sorting algorithms. */
public final static String[] SORT_NAMES = { "bubble sort", "insertion sort", "shell sort", "heap sort", "merge sort", "quick sort",};
/** offset between bars and border in x-directions (left and right) */
public final static int OFFSET_X = 5;
/** offset between bars and border in y-directions (top and bottom) */
public final static int OFFSET_Y = 5;
/** horizontal size of all bars together */
public final static int BAR_WIDTH = 350;
/** (max) vertical horizontal size of bars together */
public final static int BAR_HEIGHT = 250;
/** milliseconds to sleep after a swap in the sorting animation */
public final static int SLEEP_AFTER_SWAP = 20;
/** used for random permutation of the array elements */
private static Random aRandom_ = new Random(System.currentTimeMillis());
/** the array to display */
private int[] anArrayOfInt_;
/** offscreen buffer */
private Image image_;
/** graphics of the offscreen buffer */
private Graphics offscreenGraphics_;
/////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*
* #param length no of elements of the array
*
* #exception IllegalArgumentException if the array <code>length</code> is
* to big to display (ie <code>length</code> is bigger than
* <code>BAR_WIDTH</code> or <code>BAR_HEIGHT</code>).
*/
public ArrayCanvas(int length) {
if (length > BAR_WIDTH || length > BAR_HEIGHT)
throw new IllegalArgumentException("array to big: "+length);
anArrayOfInt_ = new int[length];
for (int i=0; i<length; ++i)
anArrayOfInt_[i] = i+1;
permute();
addMouseListener(
new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
repaint();
}
}
);
}
/////////////////////////////////////////////////////////////////////////////
// overloaded for double buffering
public void update(Graphics aGraphics) {
paint(aGraphics);
}
/** displays the array */
public void paint(Graphics aGraphics) {
int _deltaX = 0;
int w = BAR_WIDTH / anArrayOfInt_.length;
if (w > 1) {
--w;
++_deltaX;
}
int _heightFactor = BAR_HEIGHT / anArrayOfInt_.length;
if (offscreenGraphics_ == null) {
image_ = createImage(getSize().width, getSize().height);
offscreenGraphics_ = image_.getGraphics();
}
offscreenGraphics_.setColor(getBackground());
offscreenGraphics_.fillRect(0, 0, getSize().width-1, getSize().height-1);
offscreenGraphics_.setColor(Color.black);
//offscreenGraphics_.drawRect(0, 0, getSize().width-1, getSize().height-1);
offscreenGraphics_.translate(OFFSET_X, OFFSET_Y);
for (int i=0; i<anArrayOfInt_.length; ++i) {
int h = _heightFactor*anArrayOfInt_[i];
offscreenGraphics_.setColor(Color.blue);
offscreenGraphics_.fillRect((w+_deltaX)*i, BAR_HEIGHT-h, w, h);
if(anArrayOfInt_[i]==(i+1)){
offscreenGraphics_.setColor(Color.red);
offscreenGraphics_.fillRect((w+_deltaX)*i, BAR_HEIGHT-h, w, _heightFactor);
}
}
offscreenGraphics_.translate(-OFFSET_X, -OFFSET_Y);
aGraphics.drawImage(image_, 0, 0, this);
aGraphics.drawImage(image_, 475, 0, this);
aGraphics.drawImage(image_, 950, 0, this);
aGraphics.drawImage(image_, 0, 350, this);
aGraphics.drawImage(image_, 475, 350, this);
aGraphics.drawImage(image_, 950, 350, this);
}
public Dimension getMinimumSize() {
return new Dimension(BAR_WIDTH+2*OFFSET_X, BAR_HEIGHT+2*OFFSET_Y);
}
public Dimension getPreferredSize() {
return getMinimumSize();
}
///////////////////////////////////////////////////
/** random permutation of array entries */
public void permute() {
for (int i=anArrayOfInt_.length-1; i>0; --i) {
int j = Math.abs(aRandom_.nextInt()) % (i+1);
swap(anArrayOfInt_,i,j);
}
}
/** animated sort */
public void sort(String aSortNameString) {
mySort(aSortNameString);
}
///////////////////////////////////////////////////
private void mySort(String aSortNameString) {
if (aSortNameString.equals("bubble sort")) {
bubbleSort(anArrayOfInt_);
}
if (aSortNameString.equals("insertion sort")) {
insertionSort(anArrayOfInt_);
}
if (aSortNameString.equals("selection sort")) {
selectionSort(anArrayOfInt_);
}
if (aSortNameString.equals("shell sort")) {
shellSort(anArrayOfInt_);
}
if (aSortNameString.equals("heap sort")) {
heapSort(anArrayOfInt_);
}
if (aSortNameString.equals("merge sort")) {
mergeSort(anArrayOfInt_, 0, anArrayOfInt_.length-1);
}
if (aSortNameString.equals("quick sort")) {
qSort(anArrayOfInt_, 0, anArrayOfInt_.length-1);
}
}
/**
* swaps the two array elements, redisplays the array in its canvas,
* and waits a moment.
*/
private void swap(int[] anArrayOfInt, int i, int j) {
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
repaint();
try { Thread.sleep(SLEEP_AFTER_SWAP); } catch (InterruptedException e) {}
}
/////////////////////////////////////////////////////////////////////////////
// SORTING ALGORITHMS //
/////////////////////////////////////////////////////////////////////////////
/** bubble sort */
private void bubbleSort(int[] anArrayOfInt) {
for (int i=0; i<anArrayOfInt.length; ++i)
for (int j=1; j<anArrayOfInt.length-i; ++j)
if (anArrayOfInt[j-1]>anArrayOfInt[j]) {
swap(anArrayOfInt, j-1, j);
}
}
/** insertion sort */
private void insertionSort(int[] anArrayOfInt) {
for (int i=0; i<anArrayOfInt.length; ++i)
for (int j=i-1; j>=0 && anArrayOfInt[j]>anArrayOfInt[j+1]; --j)
swap(anArrayOfInt, j, j+1);
}
/** selection sort */
private void selectionSort(int[] anArrayOfInt) {
for (int i=0; i<anArrayOfInt.length-1; ++i) {
for (int j=i+1; j<anArrayOfInt.length; ++j)
if (anArrayOfInt[j] < anArrayOfInt[i])
swap(anArrayOfInt, i, j);
}
}
/** shell sort */
private void shellSort(int[] anArrayOfInt) {
// TODO: calculate needed STEPS-elements instead of using an array
// (STEPS[i+1] = 3*STEPS[i]+1)
for (int i=0; i<STEPS.length; ++i) {
int _delta = STEPS[i];
if (_delta >= anArrayOfInt.length)
continue;
for (int j=_delta; j<anArrayOfInt.length; ++j)
for (int k=j; k-_delta>=0 && anArrayOfInt[k]<anArrayOfInt[k- _delta];
k-=_delta)
swap(anArrayOfInt, k, k-_delta);
}
}
/** used by shell sort */
private final static int[] STEPS = { 1093, 364, 121, 40, 13, 4, 1 };
/** heap sort */
private void heapSort(int[] anArrayOfInt) {
int r = anArrayOfInt.length-1;
for (int l = anArrayOfInt.length/2 ; l>=0; --l)
sift(anArrayOfInt, l, r);
while (r > 0) {
swap(anArrayOfInt, 0, r);
sift(anArrayOfInt, 0, --r);
}
}
/** auxiliary function for heap sort. */
private void sift(int[] anArrayOfInt, int l, int r) {
if (r==l)
return;
int i = l, j = 2*l;
int x = anArrayOfInt[i];
if (j<r && anArrayOfInt[j]<anArrayOfInt[j+1])
++j;
while (j<=r && x<=anArrayOfInt[j]) {
swap(anArrayOfInt, i, j);
i = j; j = 2*j;
if (j<r && anArrayOfInt[j]<anArrayOfInt[j+1])
++j;
}
}
/** quick sort (pivot=(l+r)/2)*/
private void qSort(int[] anArrayOfInt, int l, int r) {
if (l >= r)
return;
swap(anArrayOfInt, l, (l+r)/2); // TODO: more clever pivot
int _last = l;
for (int i=l+1; i<=r; ++i)
if (anArrayOfInt[i] < anArrayOfInt[l])
swap(anArrayOfInt, ++_last, i);
swap(anArrayOfInt, l, _last);
qSort(anArrayOfInt, l, _last-1);
qSort(anArrayOfInt, _last+1, r);
}
/** merge sort */
private void mergeSort(int[] anArrayOfInt, int l, int r) {
int[][] B = new int[2][r+1];
mergeSort16(anArrayOfInt, B, l, r);
}
private void mergeSort16(int[] anArrayOfInt, int[][] B, int l, int r) {
if (l >= r)
return;
int _last = (l+r)/2;
mergeSort16(anArrayOfInt, B, l, _last);
mergeSort16(anArrayOfInt, B, _last+1, r);
merge6(anArrayOfInt, B, l, _last, r);
}
/** auxiliary function for merge sort */
protected void merge6(int[] anArrayOfInt, int[][] B, int l, int q, int r) {
for (int i=l;i<=q;i++) {
B[0][i] = i;
B[1][i] = i;
}
for (int i=r;i>q;i--) {
B[0][i] = r+q+1-i;
B[1][i] = r+q+1-i;
}
int i = l;
int j = r;
for (int k=l; k<r;k++) {
int s = B[0][i];
int t = B[0][j];
if (anArrayOfInt[s]<=anArrayOfInt[t]) {
i++;
} else {
s = t;
j--;
}
swap(anArrayOfInt, s, k);
t = B[1][k];
B[0][t] = s;
B[1][s] = t;
}
}
}
For each sort graph you need some kind of model, which should hold the current state of each sort. This would probably mean adding your list of ints to a separate list per sort.
You would also need some kind of mechanism that would allow you to loop through each sort algorithm and tell it to move to the next step in its algorithm, thus allowing you to control when each sort algorithm update and therefore control when the screen is updated.
Updated
Based on a comment from the OP, basically, I've ripped out the sort algorithm as a separate interface. Each algorithm would need to extend from this interface, but it provides the basic requirements to allow the UI to render the sort animation.
Bellow is basic implementation, while it's based on Swing, if required, it wouldn't be a stretch to get it to work with AWT.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestSort {
public static void main(String[] args) {
new TestSort();
}
public TestSort() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
SortPane sortPane = new SortPane();
int values[] = new int[10];
for (int index = 0; index < values.length; index++) {
values[index] = (int)Math.round(Math.random() * 100f);
}
BubbleSort sorter = new BubbleSort(values);
sortPane.setSorter(sorter);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(sortPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
sorter.sort();
}
});
}
public class SortPane extends JPanel {
private Sorter sorter;
private ChangeHandler changeHandler;
private int maxValue;
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int values[] = getSorter().getValues();
int width = getWidth() - 1;
int height = getHeight() - 1;
int colWidth = Math.round((float)width / (float)values.length);
int x = 0;
Color fill = Color.YELLOW;
Color highlight = null;
switch (getSorter().getState()) {
case Sorting:
fill = Color.BLUE;
highlight = Color.RED;
break;
case Done:
fill = Color.GREEN;
break;
}
for (int index = 0; index < values.length; index++) {
g2d.setColor(fill);
int value = values[index];
int colHeight = (int)((float)height * ((float)value / (float)maxValue));
g2d.fillRect(x, height - colHeight, colWidth - 1, colHeight);
if (getSorter().isActiveIndex(index) && highlight != null) {
g2d.setColor(highlight);
g2d.drawRect(x, height - colHeight, colWidth - 1, colHeight);
}
x += colWidth;
}
g2d.dispose();
}
public Sorter getSorter() {
return sorter;
}
public void setSorter(Sorter value) {
if (sorter != value) {
if (sorter != null) {
sorter.removeChangeListener(getChangeHandler());
}
sorter = value;
if (sorter != null) {
sorter.addChangeListener(getChangeHandler());
maxValue = 0;
for (int intValue : sorter.getValues()) {
maxValue = Math.max(maxValue, intValue);
}
}
repaint();
}
}
public ChangeHandler getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
public class ChangeHandler implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}
}
public interface Sorter {
public enum State {
Waiting,
Sorting,
Done
}
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
public int[] getValues();
public void sort();
public State getState();
public boolean isActiveIndex(int index);
}
public abstract class AbstracSorter implements Sorter {
private List<ChangeListener> listeners;
private int[] values;
private State state = State.Waiting;
private List<Integer> activeIndices;
public AbstracSorter(int[] values) {
this.values = values;
listeners = new ArrayList<>(25);
activeIndices = new ArrayList<>(2);
}
#Override
public State getState() {
return state;
}
public void setState(State value) {
if (value != state) {
state = value;
fireStateChanged();
}
}
#Override
public int[] getValues() {
return values;
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected void fireStateChanged() {
if (listeners.size() > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
#Override
public boolean isActiveIndex(int index) {
return activeIndices.contains(index);
}
protected void setActiveIndicies(int lower, int upper) {
activeIndices.clear();
activeIndices.add(lower);
activeIndices.add(upper);
fireStateChanged();
}
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
fireStateChanged();
}
}
public class BubbleSort extends AbstracSorter {
private int outter = 0;
private int inner = 0;
public BubbleSort(int[] values) {
super(values);
}
#Override
public void sort() {
setState(State.Sorting);
outter = 0;
inner = 1;
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int[] values = getValues();
inner++;
if (inner >= values.length - outter) {
outter++;
inner = 1;
}
if (outter < values.length) {
if (values[inner - 1] > values[inner]) {
swap(values, inner - 1, inner);
} else {
setActiveIndicies(inner - 1, inner);
}
} else {
((Timer)e.getSource()).stop();
setState(State.Done);
}
}
});
timer.setRepeats(true);
timer.start();
}
}
}
Example using the source array
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestSort {
public static void main(String[] args) {
new TestSort();
}
private List<Sorter> sorters;
public TestSort() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
sorters = new ArrayList<>(2);
int values[] = new int[10];
for (int index = 0; index < values.length; index++) {
values[index] = (int) Math.round(Math.random() * 100f);
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 2));
frame.add(createBubbleSortPane(values));
frame.add(createBubbleSortPane(values));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
for (Sorter sorter : sorters) {
sorter.sort();
}
}
});
}
protected SortPane createBubbleSortPane(int[] values) {
SortPane sortPane = new SortPane();
BubbleSort sorter = new BubbleSort(values);
sortPane.setSorter(sorter);
sortPane.setBorder(new CompoundBorder(new LineBorder(Color.GRAY), new EmptyBorder(8, 8, 8, 8)));
sorters.add(sorter);
return sortPane;
}
public class SortPane extends JPanel {
private Sorter sorter;
private ChangeHandler changeHandler;
private int maxValue;
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int values[] = getSorter().getValues();
Insets insets = getInsets();
int width = getWidth() - 1 - (insets.left + insets.right);
int height = getHeight() - 1 - (insets.top + insets.bottom);
int colWidth = Math.round((float) width / (float) values.length);
int x = insets.left;
Color fill = Color.YELLOW;
Color highlight = null;
switch (getSorter().getState()) {
case Sorting:
fill = Color.BLUE;
highlight = Color.RED;
break;
case Done:
fill = Color.GREEN;
break;
}
for (int index = 0; index < values.length; index++) {
g2d.setColor(fill);
int value = values[index];
int colHeight = (int) ((float) height * ((float) value / (float) maxValue));
g2d.fillRect(x, insets.top + height - colHeight, colWidth - 1, colHeight);
if (getSorter().isActiveIndex(index) && highlight != null) {
g2d.setColor(highlight);
g2d.drawRect(x, insets.top + height - colHeight, colWidth - 1, colHeight);
}
x += colWidth;
}
g2d.dispose();
}
public Sorter getSorter() {
return sorter;
}
public void setSorter(Sorter value) {
if (sorter != value) {
if (sorter != null) {
sorter.removeChangeListener(getChangeHandler());
}
sorter = value;
if (sorter != null) {
sorter.addChangeListener(getChangeHandler());
maxValue = 0;
for (int intValue : sorter.getValues()) {
maxValue = Math.max(maxValue, intValue);
}
}
repaint();
}
}
public ChangeHandler getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
public class ChangeHandler implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}
}
public interface Sorter {
public enum State {
Waiting,
Sorting,
Done
}
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
public int[] getValues();
public void sort();
public State getState();
public boolean isActiveIndex(int index);
}
public abstract class AbstracSorter implements Sorter {
private List<ChangeListener> listeners;
private int[] values;
private State state = State.Waiting;
private List<Integer> activeIndices;
public AbstracSorter(int[] values) {
this.values = new int[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
listeners = new ArrayList<>(25);
activeIndices = new ArrayList<>(2);
}
#Override
public State getState() {
return state;
}
public void setState(State value) {
if (value != state) {
state = value;
fireStateChanged();
}
}
#Override
public int[] getValues() {
return values;
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected void fireStateChanged() {
if (listeners.size() > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
#Override
public boolean isActiveIndex(int index) {
return activeIndices.contains(index);
}
protected void setActiveIndicies(int lower, int upper) {
activeIndices.clear();
activeIndices.add(lower);
activeIndices.add(upper);
fireStateChanged();
}
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
fireStateChanged();
}
}
public class BubbleSort extends AbstracSorter {
private int outter = 0;
private int inner = 0;
public BubbleSort(int[] values) {
super(values);
}
#Override
public void sort() {
setState(State.Sorting);
outter = 0;
inner = 1;
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int[] values = getValues();
inner++;
if (inner >= values.length - outter) {
outter++;
inner = 1;
}
if (outter < values.length) {
if (values[inner - 1] > values[inner]) {
swap(values, inner - 1, inner);
} else {
setActiveIndicies(inner - 1, inner);
}
} else {
((Timer) e.getSource()).stop();
setState(State.Done);
}
}
});
timer.setRepeats(true);
timer.start();
}
}
}
Example Insertion Sorter
This is an example of using a Thread as the primary sort engine instead of a Swing Timer
public class InsertionSorter extends AbstracSorter {
public InsertionSorter(int[] values) {
super(values);
}
#Override
public void sort() {
setState(State.Sorting);
new Thread(new SortRunnable()).start();
}
#Override
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
fireStateChanged();
}
});
} catch (InterruptedException | InvocationTargetException exp) {
exp.printStackTrace();
}
}
public class SortRunnable implements Runnable {
#Override
public void run() {
int[] values = getValues();
for (int i = 0; i < values.length; ++i) {
for (int j = i - 1; j >= 0 && values[j] > values[j + 1]; --j) {
try {
Thread.sleep(250);
} catch (InterruptedException ex) {
}
swap(values, j, j + 1);
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setState(State.Done);
}
});
}
}
}
Related
I don't really understand why this program draws three pawns instead of one and two of which seem to have a random(probably not so random) positions. enter image description here
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GamePanel extends JPanel implements MouseListener {
static final int SCREEN_EDGE = 800;
static final int GAME_UNITS = 64;
static final int UNIT_SIZE = 100;
final int[] x = new int[GAME_UNITS];
final int[] y = new int[GAME_UNITS];
boolean running = false;
public GamePanel() {
this.setPreferredSize(new Dimension(SCREEN_EDGE, SCREEN_EDGE));
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
int[] position = {0,0};
int[] position1 = {1, 0};
new Pawn(position,-1);
}
private void startGame() {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g){
int counter = 1;
for (int y = 0; y < SCREEN_EDGE/UNIT_SIZE; y++) {
// 1 == "white" 2 == "black"
int color = (y % 2 == 0) ? 1 : 2;
for (int x = 0; x < SCREEN_EDGE/UNIT_SIZE; x++) {
g.setColor(color == 1 ? new Color(239,217, 181) : new Color(180, 136,98));
g.fillRect(x*UNIT_SIZE, y*UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
color = color == 1 ? 2 : 1;
}
}
for (int i = 0; i < Figure.figures.length; i++) {
JLabel figureSprite = new JLabel(Figure.figures[i].image, JLabel.CENTER);
figureSprite.setSize(90,90);
figureSprite.setLocation(Figure.figures[i].position[0] + 5,Figure.figures[i].position[1] + 5);
this.add(figureSprite);
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public class MyKeyAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
}
}
}
import javax.swing.*;
public abstract class Figure{
protected int value;
protected ImageIcon image;
protected int[][] possibleMoves;
public int[] position;
// white = -1 black = 1
protected int whiteOrBlack;
protected static int figureCount = 1;
// int[figureCount][0 = x][1 = y][2 = color]
public static Figure[] figures = new Figure[figureCount];
public Figure(int value, int[] position, int[][] possibleMoves , int whiteOrBlack) {
this.value = value;
this.position = position;
this.possibleMoves = possibleMoves;
this.whiteOrBlack = whiteOrBlack;
Figure[] oldFigures = figures;
figures = new Figure[figureCount];
for (int i = 0; i < oldFigures.length; i++) {
figures[i] = oldFigures[i];
}
figures[figureCount - 1] = this;
figureCount++;
}
public abstract void move(int[] coordinates);
}
import javax.swing.*;
import java.awt.*;
import java.io.*;
public class Pawn extends Figure{
public Pawn(int[] position, int whiteOrBlack) {
super(1, position, new int[3][2], whiteOrBlack);
super.image = new ImageIcon(getClass().getClassLoader().getResource("graphics/" + (whiteOrBlack == -1 ? "whitePawn.png" : "blackPawn.png")));
Image newImage = super.image.getImage().getScaledInstance(90,90, Image.SCALE_AREA_AVERAGING);
super.image = new ImageIcon(newImage);
}
public void checkMoves(){
for (int i = 0; i < figures.length; i++) {
if((position[0] - 1) == figures[i].position[0] && (position[1] + this.whiteOrBlack) == figures[i].position[1] && figures[i].whiteOrBlack != this.whiteOrBlack) {
possibleMoves[0][0] = position[0] - 1;
possibleMoves[0][1] = position[1] + this.whiteOrBlack;
}else possibleMoves[0] = new int[2];
if((position[0]) != figures[i].position[0] && (position[1]) != figures[i].position[1]){
possibleMoves[1][0] = position[0];
possibleMoves[1][1] = position[1] + 1;
}else possibleMoves[1] = new int[2];
if((position[0] + 1) == figures[i].position[0] && (position[1] + this.whiteOrBlack) == figures[i].position[1] && figures[i].whiteOrBlack != this.whiteOrBlack) {
possibleMoves[2][0] = position[0] + 1;
possibleMoves[2][1] = position[1] + this.whiteOrBlack;
}else possibleMoves[2] = new int[2];
}
}
#Override
public void move(int[] coordinates) {
}
}
import javax.swing.*;
public class GameFrame extends JFrame {
public GameFrame(){
this.add(new GamePanel());
this.setTitle("Chess");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setResizable(false);
this.setVisible(true);
this.setLocationRelativeTo(null);
}
}
public class ChessGame {
public static void main(String[] args) {
new GameFrame();
}
}
I tried few thing like changing JLabel to BufferedImage but it would've generate other problems down the line like not being able to use MouseListener so i feel stuck. I would love to know why this code generates 3 textures too.
It looks like you are adding FigureSprites from within the drawComponent() method. The more often you draw the window, the more figures you have to draw.
Instead within drawComponent() just draw the current state but do not modify it. Adding figures has to come from somewhere else. For example, you could create the necessary pawns in the constructor. Or in extra methods that might get triggered based on user input.
In case you modify the GamePanel's state enough so that it should get painted freshly, just invoke repaint(). Swing will mark this component for repainting and decide on it's own when to run the paint() method, which in turn will run paintComponent().
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 months ago.
Improve this question
I'm trying to draw a diagram which exceeds the Raster class' limit of 46340 x 46340. My diagram is 2030000 x 240000 (with the help of JScrollPane). Is it possible to create several split up BufferedImages so that I can draw my diagram onto them piece by piece?
So, based on you previous code, encapsulate the data model into a stand alone class, then create a "view model" which takes the data model and generates the "virtual" representation and caches this information.
Then, simple render the "view model" - this way all the computation overhead is done and not repeated.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.LinkedList;
import java.util.Objects;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new JScrollPane(new TestPane(new ViewModel(new DataModel()))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Scrollable {
private ViewModel viewModel;
public TestPane(ViewModel viewModel) {
this.viewModel = viewModel;
Font currentFont = getFont();
Font newFont = currentFont.deriveFont(currentFont.getSize() * 1F);
setFont(newFont);
setBackground(Color.WHITE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Coord coord : viewModel.getNodeCoords()) {
g2d.setColor(Color.RED);
g2d.fillOval(coord.getXCoord() - 2, coord.getYCoord() - 2, 10, 10);
if (coord.getName() != null) {
g2d.setColor(Color.BLACK);
g2d.drawString(coord.getName(), coord.getXCoord(), coord.getYCoord());
}
}
for (Connection connection : viewModel.getConnections()) {
Point from = connection.getFrom();
Point to = connection.getTo();
g.drawLine(from.x, from.y, to.x, to.y);
}
g2d.dispose();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(2030000, 200000);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(400, 400);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 32;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 32;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
public class DataModel {
private LinkedList<Integer> vNodes = new LinkedList<>();
private LinkedList<Integer> vLevelWidth = new LinkedList<>();
private LinkedList<Integer> potentMoveSize = new LinkedList<>();
public DataModel() {
for (int i = 0; i < 10270; i++) {
vNodes.add(i);
}
vLevelWidth.add(10);
vLevelWidth.add(180);
vLevelWidth.add(1440);
vLevelWidth.add(8640);
for (int i = 0; i < 10; i++) {
potentMoveSize.add(18);
}
for (int i = 0; i < 180; i++) {
potentMoveSize.add(8);
}
for (int i = 0; i < 1440; i++) {
potentMoveSize.add(6);
}
potentMoveSize.add(10);
}
public LinkedList<Integer> getPotentMoveSize() {
return potentMoveSize;
}
public LinkedList<Integer> getVLevelWidth() {
return vLevelWidth;
}
public LinkedList<Integer> getVNodes() {
return vNodes;
}
}
public class Connection {
private Point from;
private Point to;
public Connection(Point from, Point to) {
this.from = from;
this.to = to;
}
public Point getFrom() {
return from;
}
public Point getTo() {
return to;
}
}
public class Coord {
private String name;
private Integer x, y, value;
public Coord(String name, Integer x, Integer y) {
this.x = x;
this.y = y;
}
public String getName() {
return name;
}
public Integer getXCoord() {
return x;
}
public Integer getYCoord() {
return y;
}
public Integer getValue() {
return value;
}
}
public class ViewModel {
private LinkedList<Coord> nodeCoords = new LinkedList<>();
private LinkedList<Connection> connections = new LinkedList<>();
public ViewModel(DataModel model) {
LinkedList<Integer> potentMoveSize = new LinkedList<>(model.getPotentMoveSize());
LinkedList<Integer> vLevelWidth = new LinkedList<>(model.getVLevelWidth());
LinkedList<Integer> vNodes = new LinkedList<>(model.getVNodes());
nodeCoords.add(new Coord("0", 2030000 / 2, 10));
Integer height = 4;
Integer heightIntervals = 200000 / (height + 1);
for (int i = 0; i < height; i++) {
for (int j = 0; j < vLevelWidth.size(); j++) {
Integer fullWidth = vLevelWidth.get(j);
Integer nodeCount = 0;
Integer widthInterval = (2030000 - 150) / fullWidth;
for (int k = 0; k < vNodes.size(); k++) {
nodeCoords.add(new Coord(
"k" + 0 + 0 + "R" + 0 + 0 + "K" + 0 + 0 + ";",
widthInterval * (k + 1),
heightIntervals * (i + 1))
);
nodeCount++;
if (Objects.equals(nodeCount, fullWidth)) {
break;
}
}
for (int k = 0; k < nodeCount; k++) {
vNodes.removeFirst();
}
if (Objects.equals(nodeCount, fullWidth)) {
vLevelWidth.removeFirst();
break;
}
}
}
LinkedList<Point> froms = new LinkedList<>();
LinkedList<Point> tos = new LinkedList<>();
for (int i = 0; i < nodeCoords.size() - (model.getVLevelWidth().getLast()); i++) // everything except bottom row of nodes
{
froms.add(new Point(nodeCoords.get(i).getXCoord(), nodeCoords.get(i).getYCoord()));
}
for (int i = 1; i < nodeCoords.size(); i++) {
tos.add(new Point(nodeCoords.get(i).getXCoord(), nodeCoords.get(i).getYCoord()));
}
Integer connectedCount;
for (int i = 0; i < froms.size(); i++) {
connectedCount = 0;
for (int j = 0; j < tos.size(); j++) {
connections.add(new Connection(froms.get(i), tos.get(j)));
connectedCount++;
if (j + 1 == potentMoveSize.get(0)) {
potentMoveSize.removeFirst();
for (int k = 0; k < connectedCount; k++) {
tos.removeFirst();
}
break;
}
}
}
}
public LinkedList<Coord> getNodeCoords() {
return nodeCoords;
}
public LinkedList<Connection> getConnections() {
return connections;
}
}
}
The above had no issues with scrolling for me, although I had many issues with actually trying to view the output - personally, I think the distance between nodes is WAY to large, but that's something you need to work out - I'd start with calculating the model and then working out the size you need to display instead of starting with the size you want and trying to fit the model into it.
If you have issues scrolling, then you could look at reducing the amount your are rendering by looking at the current clip rectangle and only rendering those elements which are actually currently displayed.
LinkedList is also not very good at "random" access, it's best for linear access, so keep that in mind as well.
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 make an animated numeric counter, like this one:
I want to be able to input the value and have the counter update with animation.
I can find how to do this on Android from Google, but I cannot find any information on how to make it in Java Swing. How would I make something like this in Swing?
This isn't a complete answer, but this is a working example of a sliding JPanel. This code could be modified to create the display in the question.
Here's the complete runnable example.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SlidingDigitGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new SlidingDigitGUI());
}
private SlidingPanel secondPanel;
#Override
public void run() {
JFrame frame = new JFrame("Sliding Digit");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createSlidingPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Animation animation = new Animation();
new Thread(animation).start();
}
public JPanel createSlidingPanel() {
String[] digitValues = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 4, false));
panel.setPreferredSize(new Dimension(300, 100));
Font font = panel.getFont();
Font derivedFont = font.deriveFont(Font.BOLD, 48F);
secondPanel = new SlidingPanel(digitValues, derivedFont);
secondPanel.setPanelValue("0");
panel.add(secondPanel);
return panel;
}
public class SlidingPanel extends JPanel {
private static final long serialVersionUID = 661553022861652947L;
private static final int MARGIN = 4;
private int imageY;
private BufferedImage slidingImage;
private Dimension characterDimension;
private final Font font;
private String currentValue;
private final String[] panelValues;
public SlidingPanel(String[] panelValues, Font font) {
this.panelValues = panelValues;
this.font = font;
this.characterDimension = calculateFontSize();
this.slidingImage = generateSlidingImage();
this.setPreferredSize(characterDimension);
}
private Dimension calculateFontSize() {
int maxWidth = 0;
int maxHeight = 0;
FontRenderContext frc = new FontRenderContext(null, true, true);
for (String s : panelValues) {
Rectangle2D r2D = font.getStringBounds(s, frc);
int rWidth = (int) Math.round(r2D.getWidth());
int rHeight = (int) Math.round(r2D.getHeight());
maxWidth = Math.max(maxWidth, rWidth);
maxHeight = Math.max(maxHeight, rHeight);
}
return new Dimension(maxWidth, maxHeight);
}
private BufferedImage generateSlidingImage() {
int height = calculateStringHeight() * (panelValues.length + 1);
BufferedImage slidingImage = new BufferedImage(characterDimension.width,
height, BufferedImage.TYPE_INT_RGB);
Graphics g = slidingImage.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, characterDimension.width, height);
g.setColor(Color.BLACK);
g.setFont(font);
int y = characterDimension.height - MARGIN;
for (String s : panelValues) {
g.drawString(s, 0, y);
y += calculateStringHeight();
}
g.drawString(panelValues[0], 0, y);
g.dispose();
return slidingImage;
}
public void setPanelValue(String value) {
int index = getValueIndex(value);
this.currentValue = value;
this.imageY = calculateStringHeight() * index;
repaint();
}
public void updatePanelValue(String value) {
if (!currentValue.equals(value)) {
int index = getValueIndex(value);
int finalY = calculateStringHeight() * index;
SliderAnimation sliderAnimation = new SliderAnimation(imageY, finalY);
new Thread(sliderAnimation).start();
this.currentValue = value;
}
}
private int getValueIndex(String value) {
for (int index = 0; index < panelValues.length; index++) {
if (value.equals(panelValues[index])) {
return index;
}
}
return -1;
}
private int calculateStringHeight() {
return characterDimension.height + MARGIN;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage subImage = slidingImage.getSubimage(0, imageY,
characterDimension.width,
characterDimension.height);
g.drawImage(subImage, 0, 0, this);
}
public class SliderAnimation implements Runnable {
private int originalY;
private int finalY;
public SliderAnimation(int originalY, int finalY) {
this.originalY = originalY;
this.finalY = finalY;
}
#Override
public void run() {
int differenceY = finalY - originalY;
if (finalY == 0) {
differenceY = characterDimension.height + MARGIN;
}
int steps = 10;
double difference = (double) differenceY / steps;
for (int index = 1; index <= steps; index++) {
imageY = (int) Math.round(difference * index + originalY);
update();
sleep(120L);
}
if (finalY == 0) {
imageY = 0;
update();
} else {
imageY = finalY;
}
}
private void update() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SlidingPanel.this.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Animation implements Runnable {
#Override
public void run() {
while (true) {
update("3");
sleep(2000L);
update("8");
sleep(2000L);
}
}
private void update(final String value) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
secondPanel.updatePanelValue(value);
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Animation {
boolean loop=true;//true //loop
static int start=1;//1 //start
static int end=10+1;//20 // end + 1
int delay1=1000;//1s // delay
int delay2=1;//1ms // delay
Font font=new Font("Helvetica",Font.BOLD,25); //Font
Timer timer=new Timer(delay1, e -> move());JPanel panel = new JPanel();int[] size = {50,100};Point point = new Point(210,size[1]);
Timer timer1=null;int value=start-1;int i;
public static void main(String[] args) {if (start!=end) {new Animation();}}
public void move() {
timer.stop();value ++;
if (!loop && value==end) {timer.stop();}i=0;
timer1 = new Timer(delay2, e -> {
if (i==100) {
timer1.stop();
timer.start();
}
point.setLocation(point.getX(), point.getY()-1);
panel.setLocation(point);
panel.revalidate();
i++;
});timer1.start();
if (loop && value==end-1) {point.setLocation(point.getX(),size[1]+size[1]);value = start-1;}else if (value==end-1 && !loop){System.exit(0);}
}
public Animation() {
JFrame frame = new JFrame();frame.setSize(500,300);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(panel);frame.setLocationRelativeTo(null);
panel.setLocation(point);frame.setLayout(null);frame.revalidate();panel.setSize(new Dimension(size[0],size[1]*Math.abs(end-(start-1))));
panel.setLayout(new GridLayout(Math.abs(end-start)+start,1));if (!(end-start<0)) {for (int i=start;i!=(end-start)+start;i++) {JLabel la = new JLabel(String.valueOf(i));la.setFont(font);panel.add(la);
}}timer.start();frame.revalidate();frame.setVisible(true);
}
}
I just wrote some code to make my player move in my little maze game, but nothing happens. Also my maze is not drawn correct as in the matrix input. I don't figure out why is wrong this code...any help is well appeciated.
Thank you!
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.swing.*;
public class Main extends JPanel {
private static Image white;
private static Image black;
private static Image finish;
private static Image player;
private static int x = 1;
private static int y = 1;
private String matrix[][];
public Main() {
addKeyListener(new Keys());
setFocusable(true);
}
public static String[][] load(String input) {
List<String[]> rows = new ArrayList<>();
try (Scanner scanner = new Scanner(input)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] cols = new String[line.length()];
for (int i = 0; i < cols.length; i++) {
cols[i] = line.substring(i, i + 1);
}
rows.add(cols);
}
}
return rows.toArray(new String[rows.size()][]);
}
public static JFrame buildFrame() {
JFrame frame = new JFrame("Labyrinth Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(900, 950);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
return frame;
}
public void moveUp() {
x += 0;
y += -1;
}
public void moveLeft() {
x += -1;
y += 0;
}
public void moveDown() {
x += 0;
y += 1;
}
public void moveRight() {
x += 1;
y += 0;
}
public class Keys extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
// repaint();
if (keycode == KeyEvent.VK_W) {
if (!matrix[getX()][getY() - 1].equals("1")) {
moveUp();
}
}
if (keycode == KeyEvent.VK_A) {
if (!matrix[getX() - 1][getY()].equals("1")) {
moveLeft();
}
}
if (keycode == KeyEvent.VK_S) {
if (!matrix[getX()][getY() + 1].equals("1")) {
moveDown();
}
}
if (keycode == KeyEvent.VK_D) {
if (!matrix[getX() + 1][getY()].equals("1")) {
moveRight();
}
}
}
#Override
public void keyReleased(KeyEvent event) {
}
}
public static void main(String[] args) {
String input = "1111111111111111111111111111111111111111111\n"
+ "1000000010001000001000000010000000100000001\n"
+ "1010111010101010101111101011111010111111101\n"
+ "1010001010100010100000001010000010000010001\n"
+ "1011101010111110101111111010111111111010111\n"
+ "1000101010100000101000001000100010000010001\n"
+ "1011101011101011111011101111111010111110101\n"
+ "1010001000001010000010100000001010000010101\n"
+ "1010111111111010111110111111101011111011101\n"
+ "1010100000100010100000000000101000000000101\n"
+ "1110101111101110111110111011101011111110101\n"
+ "1000100000000010000010100010001000100010001\n"
+ "1011111111111111111011101010111111101011101\n"
+ "1000000000000000100010001010000000001010001\n"
+ "1011111111111011101110111011111111111010111\n"
+ "1000100010001000001010001000100000001010101\n"
+ "1110101011101111111010101110111110111010101\n"
+ "1000101010001000100000101000100000100010001\n"
+ "1011101010111010101111101011101110101111111\n"
+ "1000001010000010000000101000001000100010001\n"
+ "1111111011111110111111101111111011111010101\n"
+ "1000001010000010100010001000000010000010101\n"
+ "1011111010111011101010111011111110101110101\n"
+ "1010000010001010001010001000100000101010101\n"
+ "1010111111101010111011101111101111101011101\n"
+ "1000100000001010101010001000100010101000101\n"
+ "1011111011111010101010111010111010101011101\n"
+ "1010000010001000101010000010001010001000001\n"
+ "1010101110101111101011101111101011111010101\n"
+ "1010101000101000001000101000001000000010101\n"
+ "1011101011111010111110111011101111111110111\n"
+ "1000001000000010000000000010000000000010021\n"
+ "1111111111111111111111111111111111111111111\n";
String[][] matrix = load(input);
JFrame frame = buildFrame();
ImageIcon img = new ImageIcon("C:/Users/Desktop/black20.png");
black = img.getImage();
img = new ImageIcon("C:/Users/Desktop/gri20.png");
white = img.getImage();
img = new ImageIcon("C:/Users/Desktop/finish20.png");
finish = img.getImage();
img = new ImageIcon("C:/Users/Desktop/smiley20.png");
player = img.getImage();
// frame.add(new Player());
JPanel pane = new JPanel() {
#Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j].equals("1")) {
g.drawImage(black, i * 20, j * 20, null);
}
if (matrix[i][j].equals("0")) {
g.drawImage(white, i * 20, j * 20, null);
}
if (matrix[i][j].equals("2")) {
g.drawImage(finish, i * 20, j * 20, null);
}
}
}
g.drawImage(player, x * 20, y * 20, null);
}
};
frame.add(pane);
frame.add(new Main());
}
}
It should look like:
Problems/Suggestions:
You're adding more than one component to the JFrame in a default fashion. Since the JFrame's contentPane uses BorderLayout, only one component will display, the last one added, and the other will be completely covered and will remain invisible.
Your Main JPanel is not being used as a true JPanel. Nothing is being added to it, and in fact it looks like it should be a logical class and not a component class.
Rather than using KeyListeners, which are very fidgety when it comes to focus problems, use Key Bindings which will allow you to get around focus issues in a clean and higher level way.
Don't override the JPanel's paint method but rather its paintComponent method as this will give you several advantages, including default use of double buffering for your animation.
Don't give component classes a public int getX() and public int getY() method without care since these override key methods that place the component within its container. Since the Main class shouldn't even extend JPanel, this will end up to be a non-issue for your code, but in the future, it would mess your program up.
When you run into similar problems, such as a KeyListener not working, remove the issue from your big program and try to reproduce it in a small separate program. This will give you a much cleaner environment for helping you to isolate and understand your problem and thereby help you fix it.
Your program is mis-using the static modifier. Your x and y fields should not be static and should not be accessed in a static way.
You've got way too much code within your main method, which is one of the reasons why you likely made x and y static, because you were forced to do so since you're trying to access them within main. The solution is not to make the fields static but to get all that code out of the static world and into the instance world.
Edit
For some reason the question intrigued me, and so I decided to try to M-V-C or Model-View-Controller it if possible and see what I could come up with. So here goes a bunch of classes that sort of work, beginning with a text file that holds the data.
The GUI looks like:
It must be in the same location as the class files since it is obtained as a resource and must have the file name "input.txt"
1111111111111111111111111111111111111111111
1000000010001000001000000010000000100000001
1010111010101010101111101011111010111111101
1010001010100010100000001010000010000010001
1011101010111110101111111010111111111010111
1000101010100000101000001000100010000010001
1011101011101011111011101111111010111110101
1010001000001010000010100000001010000010101
1010111111111010111110111111101011111011101
1010100000100010100000000000101000000000101
1110101111101110111110111011101011111110101
1000100000000010000010100010001000100010001
1011111111111111111011101010111111101011101
1000000000000000100010001010000000001010001
1011111111111011101110111011111111111010111
1000100010001000001010001000100000001010101
1110101011101111111010101110111110111010101
1000101010001000100000101000100000100010001
1011101010111010101111101011101110101111111
1000001010000010000000101000001000100010001
1111111011111110111111101111111011111010101
1000001010000010100010001000000010000010101
1011111010111011101010111011111110101110101
1010000010001010001010001000100000101010101
1010111111101010111011101111101111101011101
1000100000001010101010001000100010101000101
1011111011111010101010111010111010101011101
1010000010001000101010000010001010001000001
1010101110101111101011101111101011111010101
1010101000101000001000101000001000000010101
1011101011111010111110111011101111111110111
1000001000000010000000000010000000000010021
1111111111111111111111111111111111111111111
Next the main program, the one that creates the model, the view, and the controller, hooks them all together, and then displays the GUI:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
* link: http://stackoverflow.com/a/41418250/522444
*
* #author Pete
*
*/
#SuppressWarnings("serial")
public class Main2 extends JPanel {
private View mainPanel;
public Main2(MatrixModel matrixModel) {
mainPanel = new View(matrixModel);
new Controller(matrixModel, mainPanel);
setLayout(new BorderLayout());
add(mainPanel, BorderLayout.CENTER);
}
private static void createAndShowGui(MatrixModel model) {
Main2 mainPanel = new Main2(model);
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
final MatrixModel model = MatrixUtil.getInput(MatrixUtil.PATH_TO_RSC);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui(model);
}
});
}
}
Next a utility class with static methods for reading in the text file as a resource and converting it into a Model object:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class MatrixUtil {
// again this text file must be in the jar file or the code base
// at the same location as the class / java files
public static final String PATH_TO_RSC = "input.txt";
public static MatrixModel getInput(String resourcePath) {
InputStream is = MatrixUtil.class.getResourceAsStream(resourcePath);
if (is == null) {
String text = "resourcePath is not found and not loading text: " + resourcePath;
throw new IllegalArgumentException(text);
}
return getInput(is);
}
public static MatrixModel getInput(InputStream is) {
MatrixModel model = null;
try (Scanner scan = new Scanner(is)) {
List<List<MatrixPosition>> listOfLists = new ArrayList<>();
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.trim().isEmpty()) {
continue;
}
List<MatrixPosition> list = new ArrayList<>();
for (char c : line.toCharArray()) {
list.add(MatrixPosition.getMatrixPosition(String.valueOf(c)));
}
listOfLists.add(list);
}
MatrixPosition[][] grid = new MatrixPosition[listOfLists.size()][];
for (int i = 0; i < grid.length; i++) {
List<MatrixPosition> list = listOfLists.get(i);
grid[i] = list.toArray(new MatrixPosition[] {});
}
model = new MatrixModel(grid, new SpritePosition(1, 1));
}
return model;
}
}
Basic enum to represent direction:
public enum Direction {
UP, DOWN, LEFT, RIGHT
}
Another enum to represent a location point in the grid, and whether it is a wall, a coridor or the end as well as static methods to convert from number to MatrixPosition:
public enum MatrixPosition {
WALL(1), CORRIDOR(0), END(2);
private int value;
private MatrixPosition(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static MatrixPosition getMatrixPosition(int value) {
for (MatrixPosition position : MatrixPosition.values()) {
if (value == position.getValue()) {
return position;
}
}
String text = "value of " + value;
throw new IllegalArgumentException(text);
}
public static MatrixPosition getMatrixPosition(String strValue) {
int value = -1;
try {
value = Integer.parseInt(strValue);
} catch (NumberFormatException e) {
String text = "NumberFormatException for strValue " + strValue;
throw new IllegalAccessError(text);
}
return getMatrixPosition(value);
}
}
A class to represent a position of our sprite, similar to the java.awt.Point class but with row and column fields instead of x and y:
public class SpritePosition {
int row;
int column;
public SpritePosition(int row, int column) {
this.row = row;
this.column = column;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getColumn() {
return column;
}
public void setColumn(int column) {
this.column = column;
}
public void setRowColumn(int row, int column) {
this.row = row;
this.column = column;
}
}
The model has property change support code so that it can notify any classes listening to it of any changes in its state. The controller will be the class listening to the model
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class MatrixModel {
public static final String SPRITE_POINT = "sprite point";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private MatrixPosition[][] grid;
private SpritePosition spritePosition;
public MatrixModel(MatrixPosition[][] grid, SpritePosition spritePosition) {
this.grid = grid;
this.spritePosition = spritePosition;
}
public int getRows() {
return grid.length;
}
public int getColumns() {
return grid[0].length;
}
public MatrixPosition getPosition(SpritePosition p) {
return getPosition(p.row, p.column);
}
public MatrixPosition getPosition(int row, int col) {
return grid[row][col];
}
public void setSpritePoint(SpritePosition spritePosition) {
SpritePosition oldValue = this.spritePosition;
SpritePosition newValue = spritePosition;
this.spritePosition = spritePosition;
pcSupport.firePropertyChange(SPRITE_POINT, oldValue, newValue);
}
public boolean isPointValid(SpritePosition p) {
if (p.column < 0 || p.row < 0) {
return false;
}
if (p.column >= grid[0].length || p.row >= grid.length) {
return false;
}
return grid[p.row][p.column] == MatrixPosition.CORRIDOR;
}
public boolean isMoveValid(Direction direction) {
int row = spritePosition.row;
int column = spritePosition.column;
switch (direction) {
case UP:
return isPointValid(new SpritePosition(row - 1, column));
case DOWN:
return isPointValid(new SpritePosition(row + 1, column));
case LEFT:
return isPointValid(new SpritePosition(row, column - 1));
case RIGHT:
return isPointValid(new SpritePosition(row, column + 1));
default:
return false;
}
}
public void move(Direction direction) {
if (!isMoveValid(direction)) {
String text = "For move to " + direction + "spritePosition: " + spritePosition;
throw new IllegalArgumentException(text);
}
int row = spritePosition.row;
int column = spritePosition.column;
switch (direction) {
case UP:
setSpritePoint(new SpritePosition(row - 1, column));
break;
case DOWN:
setSpritePoint(new SpritePosition(row + 1, column));
break;
case LEFT:
setSpritePoint(new SpritePosition(row, column - 1));
break;
case RIGHT:
setSpritePoint(new SpritePosition(row, column + 1));
break;
default:
break;
}
}
public SpritePosition getSpritePosition() {
return spritePosition;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(name, listener);
}
public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(name, listener);
}
}
Controller class, one that sets up key bindings on the view so that it can listen for key presses, checks if they represent a valid move, and if so then tells the model to make the move. It also adds a listener to the model so that when its state changes, it will tell the view to move the sprite
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class Controller {
private MatrixModel model;
private View view;
private Map<Direction, KeyStroke> dirKeyMap = new EnumMap<>(Direction.class);
public Controller(MatrixModel model, View view) {
this.model = model;
this.view = view;
dirKeyMap.put(Direction.DOWN, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
dirKeyMap.put(Direction.UP, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
dirKeyMap.put(Direction.LEFT, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0));
dirKeyMap.put(Direction.RIGHT, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));
model.addPropertyChangeListener(new ModelListener());
setUpKeyBindings(view);
}
private void setUpKeyBindings(View view) {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = view.getInputMap(condition);
ActionMap actionMap = view.getActionMap();
for (Direction dir : Direction.values()) {
KeyStroke keyStroke = dirKeyMap.get(dir);
hookUp(inputMap, actionMap, dir, keyStroke);
}
}
private void hookUp(InputMap inputMap, ActionMap actionMap, Direction dir, KeyStroke key) {
inputMap.put(key, key.toString());
actionMap.put(key.toString(), new MoveAction(dir, model));
}
public MatrixModel getModel() {
return model;
}
public View getView() {
return view;
}
class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MatrixModel.SPRITE_POINT.equals(evt.getPropertyName())) {
SpritePosition p = model.getSpritePosition();
view.setSpritePoint(p);
}
}
}
}
#SuppressWarnings("serial")
class MoveAction extends AbstractAction {
private Direction dir;
private MatrixModel model;
public MoveAction(Direction dir, MatrixModel model) {
super(dir.toString());
this.dir = dir;
this.model = model;
}
public void actionPerformed(ActionEvent e) {
if (model.isMoveValid(dir)) {
model.move(dir);
}
}
}
Finally the View class, that extends JPanel, that displays the maze and the sprite:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class View extends JPanel {
private static final int CELL_WIDTH = 20;
private static final Color CORRIDOR_COLOR = Color.LIGHT_GRAY;
private static final Color WALL_COLOR = Color.DARK_GRAY;
private static final Color END_COLOR = Color.ORANGE;
private static final Color SPRITE_COLOR = Color.RED;
private static final int GAP = 1;
private BufferedImage gridImg = null;
private SpritePosition spritePosition;
private JPanel mainPanel = new JPanel();
public View(MatrixModel matrixModel) {
gridImg = createImg(matrixModel);
spritePosition = matrixModel.getSpritePosition();
}
public JPanel getMainPanel() {
return mainPanel;
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || gridImg == null) {
return super.getPreferredSize();
}
int prefW = gridImg.getWidth();
int prefH = gridImg.getHeight();
return new Dimension(prefW, prefH);
}
public void setSpritePoint(SpritePosition spritePosition) {
this.spritePosition = spritePosition;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (gridImg != null) {
g.drawImage(gridImg, 0, 0, this);
}
g.setColor(SPRITE_COLOR);
int y = spritePosition.row * CELL_WIDTH + GAP;
int x = spritePosition.column * CELL_WIDTH + GAP;
g.fillRect(x, y, CELL_WIDTH - 2 * GAP, CELL_WIDTH - 2 * GAP);
}
private BufferedImage createImg(MatrixModel matrixModel) {
BufferedImage img = null;
if (matrixModel != null && matrixModel.getRows() > 0) {
int w = matrixModel.getColumns() * CELL_WIDTH;
int h = matrixModel.getRows() * CELL_WIDTH;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
for (int row = 0; row < matrixModel.getRows(); row++) {
for (int col = 0; col < matrixModel.getColumns(); col++) {
MatrixPosition position = matrixModel.getPosition(row, col);
Color c = null;
switch (position) {
case CORRIDOR:
c = CORRIDOR_COLOR;
break;
case WALL:
c = WALL_COLOR;
break;
case END:
c = END_COLOR;
break;
}
g2.setColor(c);
int x = col * CELL_WIDTH;
int y = row * CELL_WIDTH;
g2.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
}
}
g2.dispose();
}
return img;
}
}