I am making a program where you choose a selection sort or a merge sort using JButtons and it sorts an int array in the form of a bar graph using Graphics where each element in the array is a bar.
but for some reason the compiler isn't receiving my button presses, i have tried to use
(selection.equals(e.getSource()) in the if statement but it isnt working, am i missing something obvious or what?
public class Animation extends Canvas implements ActionListener{
JPanel panel;
JButton Selection;
JButton Merge;
boolean selection, merge;
int[] random = new int[25];
Sorter sort = new Sorter();
public Animation(){
Selection = new JButton("Selection sort");
Selection.addActionListener(this);
Selection.setActionCommand("select");
Merge = new JButton("Merge sort");
Merge.addActionListener(this);
Merge.setActionCommand("merge");
panel = new JPanel();
panel.add(Selection);
panel.add(Merge);
setBackground(Color.WHITE);
selection=false;
merge=false;
}
public void actionPerformed(ActionEvent e) {
if("select".equals(e.getActionCommand())){
selection = true;
repaint();
}
else if("merge".equals(e.getActionCommand())){
merge = true;
repaint();
}
}
public void paint (Graphics window){
Random r = new Random();
for(int i=0; i<random.length; i++){
int randomInt = r.nextInt(100) + 1;
random[i] = randomInt;
}
window.setColor(Color.MAGENTA);
if(selection==true){
for(int i=0; i< random.length-1; i++){
int smallest = i;
for(int j = i+1; j< random.length; j++){
if(random[j] < random[smallest])
smallest = j;
}
if( smallest != i) {
int least = random[smallest];
random[smallest] = random[i];
random[i] = least;
drawIt(random, window);
window.setColor(Color.WHITE);
drawIt(random, window);
window.setColor(Color.MAGENTA);
}
}
}
drawIt(random, window);
}
public void drawIt (int[] a, Graphics window1){
int x=128;
int height = 200;
for(int i=0; i<a.length; i++){
window1.drawLine(x, 200, x, height-a[i]);
window1.drawLine(x+1, 200, x+1, height-a[i]);
window1.drawLine(x+2, 200, x+2, height-a[i]);
x+=20;
}
try {
Thread.currentThread().sleep(100);
} catch(Exception ex) {
}
}
}
heres the main class to run it:
public class AnimationRunner extends JFrame{
private static final int WIDTH = 800;
private static final int HEIGHT = 250;
JButton Selection;
JButton Merge;
public AnimationRunner()
{
super("Sorting Animation");
setSize(WIDTH,HEIGHT);
Animation a = new Animation();
Merge = new JButton("Merge sort");
Selection = new JButton("Selection sort");
Merge.setSize(120, 30);
Selection.setSize(120,30);
Merge.setLocation(200, 30);
Selection.setLocation(400, 30);
this.add(Merge);
this.add(Selection);
((Component)a).setFocusable(true);
getContentPane().add(new Animation());
setVisible(true);
}
public static void main( String args[] )
{
AnimationRunner run = new AnimationRunner();
}
}
You create a button for each action in your main class and add these to your JFrame. You also create a two instances of your Animation class. One which you create, setfocusable then do nothing with. Then another which you create and add to the contentPane of the JFrame.
In your Animation constructor you again create a button for each action, this time setting the action commands. You then add these to a panel. This panel is never added to anything and so these buttons will never be seen.
The buttons you see are not the buttons that you have defined the action commands for.
Also you should avoid using setSize() and Use Layout Managers to defines the sizes of your components.
There are a series of cascading problems...
In the AnimationRunner class you create two JButtons called Merge sort and Selection sort and add these to the main frame. This is what's actually on the screen. This buttons have no listeners attached, therefore never notify any body when they are clicked...
In the Animation class you create two JBttonss called Merge sort and Selection sort and add these to panel (and instance of JPanel), which is never added to anything. This means you can never possibly click them...
You don't seem to have an understanding of how painting works in Swing and seem to be assuming that you control the paint process in some way.
Painting is controlled by the paint sub system in Swing, which schedules and performs paint cycles when and where it sees fit. This means that your paint method may be called for any number of reasons, many of which you don't control.
Remove the logic of the sort out of the paint process and place into some kind of model, whose state you can control. Then use the custom painting capabilities to render the state of the model.
paint is an inappropriate method to be using for custom painting and you should be using paintComponent. You have broken the paint chain which may prevent the component from rendering child components and/or introduce series paint artifacts into your program
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
Swing is a single threaded framework work. That means that anything that blocks the Event Dispatching Thread will prevent it from process new repaint requests or events into the system. This will cause your program to look like it's "hung". In your case, you will most likely only ever see the end result of the painting process...after a short delay.
Instead, consider using a javax.swing.Timer to introduce a safe delty and update the model each time it ticks.
Take a look at Concurrency in Swing and How to use Swing Timers for more details
Swing programs are expected to run on a variety of hardware and software platforms, all with there own notions of DPI and font rendering approaches. This makes it very difficult to accommodate your design to all the possible needs of these systems.
Swing makes this easier by providing a layout management API, which takes the fiddle work out of making these decisions. Take a look at Laying Out Components Within a Container for more details
You should also take a look at Code Conventions for the Java Programming Language, it will make it eaiser for people to read your code.
You might find this example of some benifit
Related
I have a problem with the optimisation of adding about 4 thousand newly created JPanel components to an already existing panel.
The problem is that, surprisingly, it's quite slow. It takes nearly 10 seconds to finish adding them all and that is quite slow for me.
public static void main(String[] args) {
JMenuItem mntmGenerateRandom = new JMenuItem("Generate random int");
mntmGenerateRandom.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random rand = new Random();
for(int i = 0; i < 4000; i++){
intPositions.add(rand.nextInt(4000));
System.out.println(i);
}
quicksort(0, intPositions.size()-1, intPositions);
long start = System.currentTimeMillis();
repaintPanels();
long stop = System.currentTimeMillis();
System.out.println("Done in " + (stop-start) + "ms");
}
});
}
private void repaintPanels(){
panelArray.clear();
ExecutorService service = Executors.newWorkStealingPool();
service.submit(new Runnable() {
public void run() {
for(int i = 0; i < intPositions.size(); i++){
panelArray.add(i, new JPanel());
panelArray.get(i).setBounds(intPositions.get(i), 1, 1, panelParent.getHeight()-2);
panelParent.add(panelArray.get(i), 1);
}
}
});
panelParent.repaint();
panelParent.revalidate();
}
Here's the simplified code, removed everything unnecessary. The slowest part is when I call panelParent.add(panelArray.get(i), 1);
Could anyone helped me make it any faster (if it's even possible)?
If Joop is going to guess at a solution, then so will I.
As I noted in comments a JPanel is typically used as a container as a component that holds other components, and you don't appear to be using it to this purpose but rather (to my eyes -- but I'm not 100% sure until you clarify your problem for us), to draw an image. If so, if what you're trying to do is to draw a changing image of blocks or links, one that changes as your sorting algorithm (or whatever you're trying to do in your model) changes, then why not instead use a single drawing JPanel, one whose paintComponent has been overridden, and whose paintComponent draws the state of the model as the model changes. This too is much lighter weight than what you're trying to achieve.
Use a JTable instead. It can use one single JComponent child (like a JPanel) to draw all cells, using a CellRenderer and a CellEditor.
This is a fly weight pattern, you also might implement yourself: drawing in one single JPanel all "panels."
I need to write a strategic game which contains awt panel.
Scenario:-
Firstly, I need to read from a txt file(my map) and draw my images based on the txt file. (i did it in the code below).
public class temp extends JApplet implements Runnable,MouseListener {
public void init() {
setSize(1400,800 );
setBackground(Color.BLACK);
}
#Override
public void start() {
tilemap = new int[60][75];
filereader();
Thread thread = new Thread(this);
thread.start();
}
#Override
public void paint(Graphics g) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
int mod_i = 100*i;
int mod_j = 50*j;
g.setColor(Color.red);
String str = String.valueOf(tilemap[i][j]);
g.drawString(str,mod_i,mod_j);
switch (tilemap[i][j]) {
case 1:
g.drawImage(asfalt,mod_i,mod_j,this);
break;
case 2://it means everywhere u read 2, so draw a tree
g.drawImage(tree,mod_i,mod_j,this);
break;
}
}
}
Secondly , i have to divide my window into 3 parts! (one biggest part, for main background, and 2 small part (one for mini map that will show where I am in the big map and one for showing information) )
Question:-
How can I divide it? Should i use panels or borders??
I also have to use containers, as my map is too big and I have to scroll it.
Any suggestions for this scenario? Thanks
It's probably not the best idea to write everything in one class. You should definitely store your Filereader in another class.
By the way, do not mix awt and Swing. Either use JApplet and JPanel or Applet and Panel.
Why do you want to use borders? You can use Borders additionally, but that has nothing to do with your structure.
Make a new JPanel where you do all your "main background"-paintig stuff. Call setContentPane of your JApplet to add this JPanel.
Use for example BorderLayout for this JPanel. Create 2 more JPanels for map and information and simply add these JPanels to the main JPanel.
Use the setOpaque - Method of JPanel, if you need transparent background for a JPanel.
If your map (or your mini-map) is static, it might be a good idea, to draw your Images once to a BufferedImage and just draw the BufferedImage.
I am writing a GUI that is supposed to write lines and circles to a panel and I am supposed to use sliders to change how fast they add to the panel. I am supposed to add a clear button that will clear the entire panel and then when I move the sliders they should make the circles and lines begin to write on the panel again. There should be a specific stop point at the beginning of the sliders. We have been told to do this without actionlisteners on the sliders. I am having some trouble understanding how to make that work.
Below are the requirements for the assignment:
Write a Swing program that provides the following functionality:
Draw random length lines of random color at random coordinates with pauses between the drawing of each line.
Allow the user to set the length of the pause between lines with a slider. Have the slowest value actually stop drawing lines (i.e., it slows to a stop once it is at that value on the slider).
Have a clear button that clears all the lines & circles. Be sure that the clear button is operational at all times.
Draw random size circles of random color at random coordinates with pauses between the drawing of each circle. (Use draw, not fill.)
Allow the user to set the length of the pause between circles with a slider. Have the slowest value actually stop drawing circles (i.e., it slows to a stop once it is at that value on the slider). This is independent of the lines' speed.
The circles and lines are both drawn independently, each in their own Thread.
Do not use Timer for this, extend Thread and/or Runnable.
public class OhMy extends JFrame
{
private static final int MAX_COLOR = 225;
private static final long STOP_SLEEP = 0;
public OhMy()
{
this.setTitle("Oh My Window");
Container canvas = this.getContentPane();
canvas.setLayout(new GridLayout(2,1));
JPanel panControl = new JPanel(new GridLayout(1,1));
JPanel panDraw = new JPanel(new GridLayout(1,1));
canvas.add(panControl);
canvas.add(panDraw);
panControl.add(createPanControl());
panDraw.add(createPanDraw());
this.setSize(800, 600);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private JPanel createPanControl()
{
JPanel panControl = new JPanel();
JLabel lines = new JLabel("Lines");
panControl.add(lines);
lines.setForeground(Color.RED);
JSlider sldSpeedLines = new JSlider(1, 30, 5);
panControl.add(sldSpeedLines);
JButton btnClear = new JButton("Clear");
panControl.add(btnClear);
btnClear.setForeground(Color.RED);
JSlider sldSpeedCircles = new JSlider(0, 30, 5);
panControl.add(sldSpeedCircles);
JLabel circles = new JLabel("Circles");
panControl.add(circles);
circles.setForeground(Color.RED);
btnClear.addActionListener((e)->
{
repaint();
});
return panControl;
}
private JPanel createPanDraw()
{
JPanel panDraw = new JPanel();
class LinesThread extends Thread
{
#Override
public void run()
{
try
{
Graphics g = panDraw.getGraphics();
while(g == null)
{
Thread.sleep(STOP_SLEEP);
g = panDraw.getGraphics();
}
Random rand = new Random();
int red = rand.nextInt(MAX_COLOR);
int green = rand.nextInt(MAX_COLOR);
int blue = rand.nextInt(MAX_COLOR);
Color color = new Color(red, green, blue);
int x1 = rand.nextInt(panDraw.getWidth());
int y1 = rand.nextInt(panDraw.getHeight());
int x2 = rand.nextInt(panDraw.getWidth());
int y2 = rand.nextInt(panDraw.getHeight());
g.setColor(color);
g.drawLine(x1, y1, x2, y2);
}
catch(InterruptedException e1)
{
//awake now
}
}
}
return panDraw;
}
/**
* #param args
*/
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new OhMy();
}
});
}
}
You state:
"We have been told to do this without actionlisteners on the sliders..."
Good, because JSliders won't accept an ActionListener.
JSliders will accept a ChangeListener though, but you likely don't even need to use this.
Instead, give the clear button an ActionListener (you've no way to get around using ActionListeners at all).
In that ActionListener, reset the drawing and get the values from the JSliders by simply calling getValue() on it.
Don't get your Graphics object by calling getGraphics() on the JPanel since the Graphics object thus obtained will not be stable risking a broken image, or worse, a NullPointerException (to see what I mean, minimize and restore your current application while its drawing).
Instead either draw on a BufferedImage that is displayed in the JPanel's paintComponent method or draw directly in paintComponent itself.
Avoid using a Thread and Thread.sleep, but instead use a Swing Timer -- it's much easier this way to be sure that your code is threading appropriately.
Use this value to adjust the speed of your Swing Timer.
Edit
Thanks to Abishek Manoharan for pointing out problems in my answer...
If the JSliders need to change the speed of drawing while the drawing is proceeding, then you will in fact need to use ChangeListener on the slider.
In that listener change a field that will tell the Thread how long to sleep.
I see that you're also required to use background threads. If so, then be sure to make all Swing calls on the Swing event thread. So if you're in the background thread and need to make a Swing call, then queue it on the Swing event thread by calling SwingUtilities.invokeLater(...) and pass in a Runnable that has your Swing call.
This is a continuation from my last post Java: Animated Sprites on GridLayout. Thanks to a reply, it gave me an idea in where I just had to insert a loop in the trigger condition and call pi[i].repaint() in it. So far it works. Though I tried to integrate it to my game which composed of multiple sprites, it had no improvement in it. Without the animation, the sprites show on the grid with no problems. I inserted the animation loop in the GridFile class and it didn't show. I also tried to insert the animation loop in the MainFile, it showed irregular animations, kinda like a glitch. Can someone tell me where did I went wrong? Ideas are welcome.
MainFile class
public class MainFile {
JFrame mainWindow = new JFrame();
public JPanel gridPanel;
public MainFile() {
gridPanel= new GridFile();
mainWindow.add(gridPanel,BorderLayout.CENTER);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setSize(700,700);
mainWindow.setResizable(false);
mainWindow.setVisible(true);
}
public static void main(String[]args){
new MainFile();
}
}
GridFile class
public class GridFile extends JPanel{
ImageIcon gameBackground = new ImageIcon(getClass().getResource("Assets\\GridBackground.png"));
Image gameImage;
int[] pkmArray = new int[12];
int random = 0;
Pokemon[] pkm = new Pokemon[36];
JPanel[] pokeball = new JPanel[36];
int j = 0;
public GridFile(){
setLayout(new GridLayout(6,6,6,6));
setBorder(BorderFactory.createEmptyBorder(12,12,12,12));
gameImage = gameBackground.getImage();
for(int i = 0;i < 36;i++){
do{
random = (int)(Math.random() * 12 + 0);
if(pkmArray[random] <= 3){
pokeball[i] = new Pokemon(random);
pokeball[i].setOpaque(false);
pokeball[i].setLayout(new BorderLayout());
pkmArray[random]++;
}
}while(pkmArray[random] >= 4);
add(pokeball[i],BorderLayout.CENTER);
}
while(true){
for(int i = 0; i < 36; i++){
pokeball[i].repaint();
}
}
}
public void paintComponent(Graphics g){
if(gameImage != null){
g.drawImage(gameImage,0,0,getWidth(),getHeight(),this);
}
}
}
Use a swing Timer for the repainting, and give a bit time between the frames for swing to do the painting work. There's no point trying to draw faster than what could be displayed anyway. If you have the animation loop in main(), the repaint manager will try to drop some of the repaint requests that appear close to each other, which can be the cause of the irregular animation you see.
You should create and access swing components only in the event dispatch thread. You current approach is breaking the threading rules.
Addition: When the animation loop is where you have it now, the GridFile constructor never returns, which explains that you'll see nothing because the code never gets far enough to show the window.
(second question in a few hours)
Kay so I'm making a chess variant in java, I have my console program working how I want it but now I'm trying to convert it to a swing GUI while STILL keeping the console things intact. So up to now I have my array of squares with pieces in them for the console, and a 2-dimensional array of JPanels with pieces in them for the GUI. I haven't implemented moving pieces in the GUI yet so I'm still doing it from the console but the actual GUI doesn't update after I've moved a piece...even though it does on the console (sorry if this is confusing).
The GUI consists of a constructor which calls some methods drawBoard() and drawSidebar() and sets sizes, titles etcetc...so this is what the main method looks like:
public static void main(String args[]) {
ChessGUI GUI = new ChessGUI();
Board console = new Board();
do {
console.printBoard();
console.getScore();
console.getMove();
GUI.boardPanel.revalidate();
GUI.sidePanel.revalidate();
GUI.repaint();
} while (true);
}
and drawBoard() incase it makes any difference:
public void drawBoard() {
LayoutManager layout = new GridLayout(NUMBER_OF_ROWS, NUMBER_OF_COLS);
boardPanel.setLayout(layout);
boardPanel.setPreferredSize(new Dimension(200, 450));
chessBoard = new JPanel[NUMBER_OF_ROWS][NUMBER_OF_COLS];
for (int i = 0; i < NUMBER_OF_ROWS; i++) {
for (int j = 0; j < NUMBER_OF_COLS; j++) {
chessBoard[i][j] = new JPanel();
chessBoard[i][j].setBackground(getColor(i,j));
int index = i * 4 + j;
if (!(boardArray.chessBoard[index].square.isEmpty())) {
Piece piece = (Piece) boardArray.chessBoard[index].square.firstElement();
chessBoard[i][j].add(new JLabel(piece.toString()));
}
boardPanel.add(chessBoard[i][j]);
}
}
}
the repaint and revalidate methods don't seem to be calling at all, even though the console is being updated :(
I don't really understand what you are doing. But it doesn't make sense to recreate the entire board panel every time a move is made. All Swing components can only have a single parent, to the easier solution is to just move the piece from one panel to the other. So the code would be something like:
previousPanel.remove( piece );
currentPanel.add( piece );
previousPanel.revalidate();
previousPanel.repaint();
currentPanel.revalidate();
It looks like you're never actually removing anything from 'boardPanel,' even though you are resetting its LayoutManager.
A safer approach might be to remove 'boardPanel' from its container, then create a new instance for 'boardPanel,' add that to the container, then add the other JPanel pieces to this new 'boardPanel.' Effectively, you would be reconstructing the entire JPanel hierarchy after every move.
As you've noticed, Swing can be quite finicky once you start trying to add/move/remove components after they've been added to containers. For games, often the best approach would be to have 1 JComponent/Component and use Java2D methods to draw on top of it. Swing is typically only used for forms-based applications.
Changing the layout doesn't do anything.
You need to call boardPanel.removeChildren()
However, this is going to be extremely slow.
Really, what you should be doing is have your own JPanel, overwrite paintComponent() and draw the images into the appropriate dimensions using Java Graphics.