The JMenuItems of JMenuBar drops down to a JPanel added to the JFrame, but the JPanel erases the JMenuItems.
Do I supposed to pause the re-drawing of the JPanel?
I'm using getGraphics() on the JPanel for drawing an image, this method is called from a thread with (for example) 200 ms delay.
edit:
It's a (very simple) game inside the JPanel.
(I've added a field paused to the JPanel and i've edited the paint method so it repaints the JPanel only if paused is false, however I don't know if this "solution" is good. (It's set to true when the user clicks on the menu and set to false when selects or cancels it.)
You should always be repainting the JPanel from the Event Dispatch Thread, not an arbitrary thread. If you want to do this in order to animate the panel (e.g. with the 200ms delay you mention) then consider using javax.swing.Timer, which periodically fires an ActionEvent on the Event Dispatch Thread.
Example
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Add additional graphics operations here.
}
}
final JPanel panel = new MyPanel();
int delay = 200; // Milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
panel.repaint();
}
};
new Timer(delay, taskPerformer).start();
I'm using getGraphics() on the JPanel
for drawing an image
Never use the getGraphics() method like that. You have no control over when the component should be repainted. Custom painting should be done by overriding the paintComponent() method of the panel. When you use the Timer you just use panel.repaint() and the Swing repaint manager should look after the details of what needs to be painted.
Have a look at javax.swing.Timer documentation
https://docs.oracle.com/javase/1.5.0/docs/api/javax/swing/Timer.html
It has code right at the top to fire an event on fixed interval.
Related
I have a data plot with a color bar that's a JPanel with a layout that has two JPanels inside of it. One JPanel is the data plot itself, and the other is the color bar. I'd like to add functionality so the color bar can be toggled on and off, and I've gone about this by simply removing the JPanel containing the color bar. Something like this:
public class Data2DPlotWithColorBar extends JPanel {
public Data2DPlotWithColorBar() {
this.data2DPlot = new Data2DPlot();
this.colorBar = new VerticalColorBar();
this.setPlot();
}
public final void toggleColorBar() {
enableColorBar = !enableColorBar;
setPlot();
}
private void setPlot() {
this.removeAll();
this.setLayout(new BorderLayout());
if (enableColorBar) {
this.add(colorBar, BorderLayout.EAST);
}
this.add(this.data2DPlot, BorderLayout.CENTER);
this.revalidate();
this.repaint();
}
private final Data2DPlot data2DPlot;
private final VerticalColorBar colorBar;
private boolean enableColorBar;
}
The problem is that when the color bar is removed, the data plot has a component listener with the componentResized method overrided which correctly resizes the data (maintains fixed aspect ratio) to fit the size of the JPanel. Something like this:
public class Data2DPlot extends JPanel {
...
#Override
public final void componentResized(ComponentEvent e) {
double scaleFactorBuf = Math.min((double)getPixelMaxViewWidth()/getNativeWidth(),
(double)getPixelMaxViewHeight()/getNativeHeight());
// Make sure scaleFactorBuf isn't close to zero
if (Math.abs(scaleFactorBuf) > MathUtilities.LAMBDA) {
scaleFactor = scaleFactorBuf;
}
}
...
#Override
protected final void paintComponent(Graphics g) {
super.paintComponent(g);
....
}
}
It turns out that as-is, the dataplot is not resizing properly. I did some debugging and I found out that componentResized gets called AFTER the paintComponent method when I toggle the color bar off and on. This means the image gets painted, and then the scaleFactor gets updated afterwards, which is incorrect. The only way I've been able to fix it so far is to call repaint() at the very end of the componentResized method. However, repaint() is already called when the component is resized, so I feel like this is the incorrect approach. Some googled led me to solutions involving the use of revalidate and repaint after modifying a JPanel on demand. However, any combination of doing this still led to componentResized being called after repaint. Is there a standard fix for this?
An answer proposed in this thread offers an easy solution; rather than overriding the componentResized method, do the setBounds(int,int,int,int) one.
The call order of componentResized, setBounds, and repaint is strange; on program startup it is like this;
setBounds
componentResized
repaint
while if you manually resize it later (I did not test with in-code resizing order) it goes
setBounds
repaint
componentResized
By setting your flags in setBounds rather than componentResized, you can know to recompute your repaint size-sensitive variables on panel resizing, effective immediately.
#SuppressWarnings("serial")
class GUI extends JPanel implements ActionListener
{
public void paint(Graphics g)
{
super.paint(g);
System.out.println("In here...");
g.drawRect(frame.getWidth()/2,frame.getHeight()/2,(frame.getWidth()/2)+5,(frame.getHeight()/2+5));
g.setColor(Color.BLACK);
g.fillRect(frame.getWidth()/2,frame.getHeight()/2,(frame.getWidth()/2)+5,(frame.getHeight()/2+5));
}
#Override
public void actionPerformed(ActionEvent ae)
{
if(ae.getSource() == slow)
{
this.setMotionToSnake(slowMotion);
this.repaint();
}
}
}
Im writing a snake game program. While doing that paint method is not being called.
Explaining the code:
im adding one menu bar to my frame
in that menu bar start is one menu with 3 sub menus in it viz slow,medium,fast.
Whenever i say slow the motion of the snake to move is decided and now i should able to see a rectangular box(at least) in the frame.
Thats why im calling the repaint method there.
Other than this.repaint() i have also used frame.repaint() / just repaint().
But method is not getting invoked.
Thanks for ur help in advanced.
im using flow layout manager frame.setLayout(new FlowLayout());
A FlowLayout respects the size of all the components added to the panel. You are creating a custom component and by default the size of your component is (0, 0) so there is nothing to paint.
Override the getPreferredSize() of your custom component to return the proper Dimension for your component.
Also, custom painting is done by overriding the paintComponent(...) method, not the paint() method.
Read the section from the Swing tutorial on Custom Painting for more information and working examples that show how to do this.
I have a JLayeredPane with 2 layers
the first layer is a JPanel Wrapping an Image.
the second layer is another object which extends JPanel called ResizableRectangle and implements KeyListener.
I've overrode the KeyPressed method but it doesn't receive the keyPressed event and the method doesn't get invoked.
I've set the setFocusEnable(true) and used grabFocus(), requestFocus() and requestFocusInWindows() but all of them return false.
I figured out when I press tab after the the JFrame loads , the Focus goes to the panel that I want , and the listener gets the events.
I've added a KeyListener to the JLayeredPane and it works fine but the problem is that i want to add listener to the panel not the layeredPane.
I hope that you put Image or ImageIcon to the JLabel then add to the JLabel MouseListener
then just to call
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
myPanel.grabFocus();
myPanel.requestFocus();//or requestFocusInWindows();
}
});
I am creating a program that uses JFrame, JPanel, JLabel and all other sorts of swing components.
What I want to do is create a 2D animation on a separate JPanel that is dedicated to this animation. So I will be overriding the paintComponent (Graphics g) method.
I have experience making animations with for loops + Threads, but I am hearing that threads are not safe with swing.
Due to this, is it safe for me to make an animation with the use of the Runnable interface? If not what shall I use (e.g. Timer) and please give a small example on how to best use it (or a link to a web page).
EDIT:
Thanks to Jeff, I will be using Timer to create the animation. For future viewers of this question, here is a quick program I coded in about 5 minutes, excuse the dirty code.
I have also added some quick comments.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class JFrameAnimation extends JFrame implements ActionListener
{
JPanel panel;
Timer timer;
int x, y;
public JFrameAnimation ()
{
super ();
setDefaultCloseOperation (EXIT_ON_CLOSE);
timer = new Timer (15, this); //# First param is the delay (in milliseconds) therefore this animation is updated every 15 ms. The shorter the delay, the faster the animation.
//This class iplements ActionListener, and that is where the animation variables are updated. Timer passes an ActionEvent after each 15 ms.
}
public void run ()
{
panel = new JPanel ()
{
public void paintComponent (Graphics g) //The JPanel paint method we are overriding.
{
g.setColor (Color.white);
g.fillRect (0, 0, 500, 500); //Setting panel background (white in this case);
//g.fillRect (-1 + x, -1 + y, 50, 50); //This is to erase the black remains of the animation. (not used because the background is always redrawn.
g.setColor (Color.black);
g.fillRect (0 + x, 0 + y, 50, 50); //This is the animation.
}
}
;
panel.setPreferredSize (new Dimension (500, 500)); //Setting the panel size
getContentPane ().add (panel); //Adding panel to frame.
pack ();
setVisible (true);
timer.start (); //This starts the animation.
}
public void actionPerformed (ActionEvent e)
{
x++;
y++;
if (x == 250)
timer.stop (); //This stops the animation once it reaches a certain distance.
panel.repaint (); //This is what paints the animation again (IMPORTANT: won't work without this).
panel.revalidate (); //This isn't necessary, I like having it just in case.
}
public static void main (String[] args)
{
new JFrameAnimation ().run (); //Start our new application.
}
}
Jimmy, I think you are misunderstanding how threads work in Swing. You must use a specific thread called the Event Dispatch Thread to do any updating on swing components (with a few specific exceptions I won't discuss here). You can use a swing timer to set a recurring task to run on the event dispatch thread. See this example of how to use Swing timers. http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html
You should also read up on the Event Dispatch Thread so you understand its place in Swing http://download.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html
Java also provides a variety of methods for working with Swing in the SwingUtilities class, notably invokeLater and invokeAndWait which will run code on the event dispatch thread.
While is good to understand the EDT and SwingUtilities (everybody doing Swing should do so) if you're going to be doing a lot of animation I would recommend to use the TimingFramework. This will allow you to concentrate a little more on design and will give you a better control on "rate". Inherently the timing framework uses a Swing timer so the callbacks are on the EDT. Part of the Filthy-rich clients collection, the author made the chapter available.
Have fun!
My program have 3 classes. 1) main, 2) frame, 3) drawingBoard. The logic of my program is that, a new drawing will be displayed every times user click on New pattern button (and this working fine).
1st class - main method
public class mainPage {
public static void main(String[]args){
JFrame appFrame = new Frame();
appFrame.setVisible(true);
appFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);*/
}
}
2nd class - describe the layout (I use Grid Bag Layout)
public class Frame extends JFrame implements ActionListener {
public Frame (){
GridBagLayout m = new GridBagLayout();
Container c = (Container)getContentPane();
c.setLayout (m);
GridBagConstraints con;
JButton bPattern = new JButton("New Pattern");
....
bPattern.addActionListener(this);
JPanel pDraw = new JPanel();
.....
pDraw.add(new drawingBoard()); //drawing will be placed in this panel
}
public void actionPerformed(ActionEvent e) {
repaint();
}
}
3rd class - run drawing functions e.g. paintComponent (), etc.
public class drawingBoard extends JPanel {
public drawingBoard(){}
public void paintComponent(Graphic g){}
....
}
The problem is that, when I look on the console, it seems that even though the user did not click on the button, the program call the class 'drawingBoard' and repaint. The paint component is in the 3rd class (drawingBoard). Although this seem not to give me a problem (e.g. no drawing displayed on the panel unless the user click the button), I am just curious how this happened. is that because I wrote this code at FRAME class (). My intention to write this code is to make sure the drawing should be place in this specific panel (I have 3 panels) but not to call the 3rd class unless the button has been clicked.
JPanel pDraw = new JPanel();
pDraw.add(new drawingBoard()); //place drawing here
The repaint method (and subsequently, the paintComponent method) is not only called by the JFrame but also by Swing itself as well, when there needs to be a repaint of the contents of the JPanel.
The Painting in AWT and Swing article is a good place to start to get information on how painting works.
In this case, the repaint method is being called by events which the article calls System-triggered Painting:
In a system-triggered painting
operation, the system requests a
component to render its contents,
usually for one of the following
reasons:
The component is first made visible on the screen.
The component is resized.
The component has damage that needs to be repaired. (For example,
something that previously obscured the
component has moved, and a previously
obscured portion of the component has
become exposed).