I have the following scenario: If I have a while block in the paint() method (used for example to simulate a simple animation such as rotating a polygon, done by multiple drawing and erasing the figure), is there a way to break the while block, when clicking the mouse inside the applet?
The animation of the polygon is done without recalling the paint() method. Also would it be possible to do so if the while block looked something like this:
while (count<n)
{
//code that draws the polygon rotating
count++;
}
Yes there is a scenario to hold on your while loop.
The simpliest way would be to set up a variable in your classfile private boolean stopLoop=false and within your while loop check for this attribute while (!stopLoop).
Now the MouseEvent just set the attribute stopLoop=true and you are done (if you need help, here you are How to Write a Mouse Listener
The other solution is using Swing Timer as mentioned by #camickr (see other answer). Lets assume you have a general Timer method outside your paint() method. Then you sould't use a while loop in there. I would suggest to just paint a static picture and if you want that your poligon rotates, just draw the next one, but with another angle and so on.
The idea is that you cut out your while loop into the Timer method so paint() gets called a lot of times. If you want to stop the poligon from circling around use a boolean flag for it or stop the timer. In the first case you can handle more then one polygon and each of them can be started and stopped, if you handle the boolean variables and the mouse event correct.
If you have further questions please add some more detail, or bedder show us some minimized code.
Don't use a while loop.
Instead use a Swing Timer to schedule the animation. Then you can simply start/stop the timer as required.
Related
In a Swing application when the mouse is moved within the frame, the actionPerformed method stops cycling. How can I fix this?
Here's the basic layout of my program:
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// main game loop
}
}
public void paintComponent(Graphics g) {
// render loop
}
I found a similar question here. The user found that by lowering the polling rate of the mouse they fixed the problem; however I cannot change the polling rate on my apple trackpad, and no other solutions were offered. Also it is an inelegant solution that would require the user to change settings, and honestly there has to be a better way to fix the problem.
Basically the question boils down to this:
Is there a way for me to change the polling rate from within my program? I did some research and couldn't find a solution.
How can I disable mouse movement events, so as to not slow down my game loop? (Also perhaps move it to a separate process, and use the mouses x and y position provided by that process for logic in the game loop.)
What alternate solution can I implement to fix this problem?
I think you need to implement the "ActionListener" where you can take it, because when you are moving will work the ActionListener, when you will click, it will be already ActionEvent.
Also you can get more from:
https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html
and
How can I get the location of the mouse pointer in a JPanel (Without any operation of the Mouse)?
As I wrote in title,
I want to drawLine additionally when Timer generates events.
Line will be draw like
first time;
ㅡ
second time;
ㅡ
ㅡ
I want line will be added on before's maintained situation
If first purpose cannot be done, (because I'm not good at Java yet, that's just my idea) I want to draw N line in Nth events then i redraw new N+1 line in (N+1)th events.
Which could be done in Java?
P.S. How to stop the Swing Timer?
You'll basically have to (not in order):
Build an event to happen on each tick of the timer
Pass it to a new timer (stopping the timer is in the documentation)
Set some sort of incrementing counter, either coordinate-based or tick-based
Override the paintComponent method in a component to draw the lines, based on the incrementing counter
From inside the event, call repaint() on your component (will happen on each timer tick)
Work on each one of those tasks individually, and when you feel you've mastered each one, you can try putting them all together.
I have a JPanel that contains a few other objects that do stuff. I will simplify the example by talking about some circle object (defined by circle class I made), and a square object (similar).
the circle moves randomly around the screen, while the square sits at its place. my intention is to move the square using the arrow buttons.
the current design is to have a thread with a while loop that contains a delay that sets the 'refresh rate' inside its run method.
I'm trying every method I know to capture the arrow keys and move the square around while the ball is running around the screen.
how do I capture keypresses (arrows for the example) so I can know where to move my square to?
I tried implementing keylistener in the jpanel but it didn't work. when I tried to use a KeyEvent in the run, I got an exception.
please save me. :)
EDIT:
Thanks for that info. I would like further help to settle this issue -
lets say I have the following code:
this.getInputMap().put(KeyStroke.getKeyStroke("UP"), "actionName");
this.getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "actionName");
this.getActionMap().put("actionName",
new AbstractAction("actionName") {
public void actionPerformed(ActionEvent evt) {
//dostuff
}
}
);
how do I distinguish between UP and DOWN presses? what do I need to change?
Thanks! I'm a bit of a newbie, I know :)
KeyListener isn't designated for listening in Swing GUI, this Listener was builded for pre_historic AWT Component, these days so far away, use KeyBindings, this example can save your person
I'm working on a Java Game and I've come to a point where I'm having problems with the KeyListeners/KeyBinding. What I basically want to do is temporarily disable the keyboard/do not allow more inputs when an animation occurs. This animation is generated by updating data.
What I currently get is that I press the key for the animation, the animation starts, and I press another key that does some other function. It gets added to the stack/queue(?) of the keyboardlistener and triggers when the first animation finishes.
I'm using a JPanel that implements KeyListener.
To give an idea of the code:
public void keyPressed(KeyEvent arg0) {
//Prevents Repeated keys
pressed.add(arg0);
if (pressed.size() == 1) {
int key = ((KeyEvent) pressed.toArray()[0]).getKeyCode();
if (key == KeyEvent.VK_ENTER) {
doSomeAnimation();
} else if (key == KeyEvent.VK_SPACE) {
doADifferentAnimation();
}
Update();
}
}
Things I've tried:
1) Set the focusable(false) on the JPanel before the calls to the animations. Then set the focusable(true) and grab the focus when they've been completed.
2) Used a boolean to track when an animation occurred.
3) Use Key Bindings.
No matter what method I used, I always ended up with the problem that I would still take in input from the keyboard when the animation was occurring. Then, once that animation was finished, it'd go to the next element in the stack/queue(?) and process that. Also, these animations would need to occur more than once (so using an array of booleans to verify if it's been executed already wouldn't be helpful).
So, if you have any idea or help (or places to point me to) that would be greatly appreciated.
Some Extra Information: Java 1.6, IDE Eclipse, MVC structure. (This in question is the Controller/Model)
Assuming your animation is driven by an instance of javax.swing.Timer, the queue in question is the EventQueue, which runs events in "the same order as they are enqueued" by the Timer. Because it is impractical to stop other devices on the host platform from evoking your listeners, you have to track the effect in your application. As a concrete example, this game has several overloads of the model's move() method to handle input from disparate sources: keyboard, mouse or animation timer. The timer may be toggled on or off to see its effect. This example, which supplants the EventQueue with a custom implementation, may also offer some insight.
Apologies for the somewhat unclear question - couldn't think of a better way of putting it.
I use a JXTaskPane (from the Swing labs extension API) to display some information.
The user can "click" the title to expand the panel. The JXTaskPane is in a container JPanel, which is then added to a JFrame, my main application window.
I want my application window to resize to the size of the expanded task pane. To achieve this, I added a component listener to my container JPanel which would set size to the now expanded panel.
panel.addComponentListener(new ComponentListener()
{
public void componentResized(ComponentEvent e)
{
Dimension newSize = ((JXTaskPane)e.getSource()).getSize();
reSizeFrame(newSize);
}
}
private void reSizeFrame(Dimension newSize)
{
if ((newSize.height < maxSize.height) && (newSize.width < maxSize.width))
{
containerPanel.setSize(newSize);
appFrame.setSize(containerPanel.getSize());
appFrame.pack();
}
}
The problem is that the componentResized method is called as the task pane expands, as a result the resizeFrame method is called lots of times, and looks really awful on the screen.
How can I detect when the JXTaskpane has finished resizing? I thought of two approaches:
Put the resizeFrame() method in a SwingUtilities.invokeLate(..) call.
Put in a timer resizeFrame call, so any subsequent calls do not do anything until the timer fires. This should give enough time for the panel to resize.
What is the best way forward?
Also - This is my first serious Java GUI app after years of server side program. StackOverflow has been very helpful. So thanks!
I know you've already selected an answer, but overriding the paint method is definitely not correct, and while you may be able to hack something in place, it won't be ideal.
Looking at the source for JXTaskPane and specifically looking in setExpanded() (line 387), you can see it calls JXCollapsiblePane.setCollapsed(...) and then fires a property change event for expanded. A listener on that property won't be correct, because it'll fire before the animation is complete. So, if you go into JXCollapsiblePane and look at setCollapsed(...) (line 470) you'll see that if it's animated, it sets the paramaters and starts a timer. We want to know when the animation ends, so in that file, look at the animator (line 620, and specifically 652-667), which shows that when the animation ends, it fires a property change for ANIMATION_STATE_KEY with a value of "collapsed" or "expanded". This is the event you actually want. However, you don't have access to JXCollapsiblePane, so go back to JXTaskPane and search for ANIMATION_STATE_KEY, and you find line 208, which shows that JXTaskPane creates a listener on JXCollapsiblePane.ANIMATION_STATE_KEY and refires it as it's own event.
Since you do have access to JXTaskPane, you can listen for that event, so doing ...
taskPane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if(e.getNewValue().equals("expanded") {
...
}
else if(e.getNewValue().equals("collapsed") {
...
}
}
}
should get your event exactly when you want it.
The correct way to listen for events in Swing is through property listeners. Unfortunately, the only way to find out what the correct properties and values are is by digging through source code.
As a suggestion, have you tried overriding the paint method, first calling super and then putting your resize code at the end of that if (and only if) the size has changed significantly.
I'm not familiar with JXTaskPane, but my first reaction is that maybe you're handling the wrong event. You want the frame to resize when the user clicks on the header - so why not handle that event (perhaps using EventQueue.invokeLater() to resize the frame after the task pane has been resized)?
But if that doesn't work and you need to use the approach you've outlined above, using a javax.swing.Timer is probably best. Set it for 200 milliseconds or so and just restart() it every time componentResized() fires.