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).
Related
I have a JScrollPanel and a JPanel added to it. I would like to draw to the JPanel and make the scrollbars of the JScrollPane appear whenever the drawing exceeds the size of the panel and be able to scroll the drawing both vertically and horizontally.
I have tried consulting with various forums and the official docs and tried a few things (setting the borders, the preferred size, etc.) but none seems to yield the desired effects.
I have a JFrame (with GridBagLayout, btw.) :
JFrame frame1 = new JFrame("Application");
frame1.setVisible(true);
frame1.setMinimumSize(new Dimension(580,620));
frame1.setResizable(false);
frame1.setLocationRelativeTo(null);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
The relevant components are :
JPanel panel1 = new JPanel();
JScrollPane scrollPane = new JScrollPane(panel1);
frame1.add(scrollPane, gbc_panel1); //added with layout constraints
JPanel :
panel1.setBackground(Color.BLACK);
panel1.setPreferredSize(new Dimension(500,500));
panel1.setMinimumSize(new Dimension(360,360));
panel1.setMaximumSize(new Dimension(1000,1000));
JScrollPane :
scrollPane.setAutoscrolls(true);
The relevant code from the action event
of a button that does the drawing :
Graphics g;
g = panel1.getGraphics();
panel1.paint(g);
g.setColor(new Color(0,128,0));
/* this is followed by some more code that
does the drawing of a maze with g.drawLine() methods */
The code does the drawing perfectly, I just can't seem to figure it out how to make the scrolling and dynamic resizing happen.
I would appreciate any helpful comments or remarks!
Thank you!
Ultimately rewriting the paint method did the trick as #MadProgrammer suggested. I was just hoping that I could do the painting without having to define my custom JPanel class, but looks like it doesn't work that way.
The custom class looks like this:
class Drawing extends JPanel {
int mazeSize;
public Drawing(JTextField jtf)
{
try {
this.mazeSize = Integer.parseInt(jtf.getText());
}
catch (Exception e)
{
JOptionPane.showMessageDialog(this, "ERROR! Invalid size value!");
}
} // the constructor gets the size of the drawing from a textField
public Dimension getPreferredSize() {
return new Dimension(mazeSize*10,mazeSize*10);
} //getPreferredSize - this method is used by the scroll pane to adjust its own size automatically
public void drawMaze (Graphics g)
{
/* some irrelevant code that does the desired drawing to the panel by calling g.drawLine()*/
} // drawMaze method that does the de facto drawing
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawMaze(g);
}// paintComponent() #Override method - this was the tricky part
}//Drawing JPanel subclass
It is also worth noting (if some noob like myself happens to stumble upon this question), that after instantiating the new JPanel subclass in the action event, I had to add it to the JScrollPanel in the following way, instead of just simply using its add() method:
Drawing drawPanel = new Drawing(textfield1);
scrollPane.getViewport().add(drawPanel);
Again, thanks for the suggestion!
Once finished with the program (a random maze generator that uses a recursive backtracking algorithm), I will make the source code available at my github profile.
This is my first java project and I am trying to draw a simple rectangle on my JPanel inside my JFrame. Been trying to solve this issue with the help of the same topics on stackoverflow but still no success.
The exception I get when I run the program is java.lang.NullPointerException. From my understanding I can not draw on the JPanel itself? which is created in mainWindow.
Main:
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GameBoard game = new GameBoard();
mainWindow view = new mainWindow(game);
mainModel model = new mainModel();
mainController cont = new mainController(model, view, game);
cont.controllerInit();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
View:
public class mainWindow{
public JFrame frame;
public JPanel panel;
GameBoard game = new GameBoard();
frame = new JFrame();
frame.getContentPane().setBackground(SystemColor.control);
frame.setBounds(100, 100, 728, 435);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.getContentPane().setLayout(null);
panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
panel.setBounds(166, 44, 550, 349);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
Game:
public class GameBoard extends JPanel{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawRect(200, 200, 200, 200);
}
}
Never, ever call paintComponent directly, no external source has any reason to do so. Also, what do you thing would happen if you passed it null?
You should start by having a look at Performing Custom Painting and Painting in AWT and Swing to get a better understand of how paint in Swing works.
The Swing API basically uses a delegate model, where the system delegates responsibility of the paint of each component to the component. This is achieved by the system calling the components paint method, which in-turn calls (among a few others) paintComponent.
Swing also uses a passive rendering approaching, meaning that painting occurs at the discretion of the paint system. You component is notified of the need when its paint method is called. This may occur at any time.
In order for a component to be painted, it must first be added to container which is realised on the screen (has a native peer), in most cases, this means that the component hierarchy needs to resolve to some kind of window based class, like JFrame.
So, the answer to your question is:
Read the above documentation (and get a better understanding of how the API works)
Add your GameBoard to a container which can be resolved to a window based class
Never call paint or paintComponent directly
Reflection....
private mainWindow view;
private mainModel model;
public GameBoard(mainModel m, mainWindow v)
{
view = v;
model = m;
}
To me, this makes no sense. There is no reasonable reason why GameBoard needs a reference to mainWindow. GameBoard is, in of itself, a "view". If anything, the only thing you "should" be passing to GameBoard (assuming you're trying to use a MVC) is a controller
By input elements I mean things like JSpinners and JComboxBoxes. My glasspane is passed a JPanel containing JSpinners, JComboBoxes and for the most part, JLabels. The glasspane has a MouseListener attached. The surprising thing is that mouseEntered is called upon the mouse cursor leaving the input elements and hovering over the other parts or empty space of the JPanel! Is this normal behaviour? How can I get the input elements to be considered part of the JPanel for Glasspane purposes?
Here is a screenshot of my UI with its input elements and jLabels.
Here is an example piece of Code:
import javax.swing.*;
public class DialogTest {
public DialogTest() {
JPanel dialogPanel = new JPanel();
SpinnerModel edgeModel = new SpinnerNumberModel(1, 1, 9, 1);
JSpinner edgeSpn = new JSpinner(edgeModel);
dialogPanel.add(edgeSpn);
JDialog initialDialog = new JDialog(new JFrame(), "Test", true);
initialDialog.setContentPane(dialogPanel);
initialDialog.pack();
glass = new GlassComponent(dialogPanel);
initialDialog.setGlassPane(glass);
glass.setOpaque(false);
glass.setVisible(true);
initialDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
initialDialog.setVisible(true);
}
}
public class GlassComponent implements MouseListener {
JPanel c;
public GlassComponent(JPanel c) {
this.c = c;
this.c.addMouseListener(this);
}
...
public mouseEntered(MouseEvent e) {
System.out.println("Entered JPanel");
}
}
By way of explanation, my goal is to eventually use the GlassPane to block input for those elements marked with the prohibition sign. However, given that the mouseListener assigned to the dialogPanel is seemingly generating new events upon leaving the input elements, I may have some difficulties achieving this.
You can forward mouse events to the underlying components, as shown in The Glass Pane demo's method, redispatchMouseEvent().
You appear to be using glasspane in a way that I feel it shouldn't be used.
As far as I know, a glasspane typically shouldn't be holding components at all but rather cover over the top-level window and then can act as a gate-keeper for the components that are below it, all held by the top level window's contentPane.
you can use GlassPane for overlay required Container or JComponent by #camickr, or my questions based on his code here or here,
another suggestion could be use JLayer (required Java7 for Java6 is there JXLayer)
I'm creating an applet which consists of a class which extends JApplet, with a menubar and a class which extends a JPanel.(So there is a menubar and a JPanel shown in the applet).
In this class I add and remove some textfields to the JPanel. This all works fine. Here's where it gets tricky: it only works the first time. When I add some new textfields to the JPanel, they are added and visible in the JPanel, but the menubar in the JFrame stops working.
Since the code is too extensive I'll only post parts of it.
Here's the code where I add the JPanel to the JApplet:
public class Simulator extends JApplet implements ItemListener, ActionListener {
Container pane = getContentPane();
canvas = new DrawCanvas();
pane.add(canvas, BorderLayout.LINE_END);
}
Here's the code of the JPanel:
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
if(textfield != null)
remove(textfield);
textfield = new JTextField();
this.add(textfield);
}
}
This works the first time(when nothing is removed), but the second time the menubar stops working.
When I leave out the this.add(textfield); line, the menubar keeps working.
I once had similar problems with popup menus beeing painted behind other components.
Try calling static JPopupMenu.setDefaultLightWeightPopupEnabled(false); or the setLightWeightPopupEnabled on your specific submenu. This will make (all) popup menus (i.e. submenus) to heavy weight components that have a native peer.
I believe you are running into issues with threading. Adding and removing JComponents during painting might mess up the EDT (which is calling the paint method in the first place).
I'm trying to close a frame yet open a new frame.
My application has page A, a JPanel with some controls and a specific button, and when the user clicks the button, I want page A to disappear and page B to appear (page B has controls that depend on the choices that are made by the user on page A).
This has been asked before, but there was no satisfactory answer. Inside the ActionListener implementation, namely public void ActionPerformed(ActionEvent e) from my jpanelForPageA class, I can comfortably write this.setVisible(false), but how can I set page B to a visible state?
You can do the removal of panel a and then the addition of panel b trick. Another is to use a CardLayout.
When you create your panels, you add them to a containing JPanel that you initialize with a CardLayout:
JPanel container = new JPanel(new CardLayout());
containter.add(getPanelA(), "PANEL_A");
containter.add(getPanelB(), "PANEL_B");
Then, in your actionPerformed, when you want to show panelB, you do this:
CardLayout cl = (CardLayout) container.getLayout();
cl.show("PANEL_B");
Take a look at this tutorial for some more ideas.
For some reason, I can never to get setVisible() to work for me to do what you're describing. Instead, I do this:
frame.remove(panelA);
frame.add(panelB);
"frame" is just the JFrame you want to put the panels in. Try this if the setVisible() method doesn't work :)
To your original question, all you have to do is (like aioobe said):
panelB.setVisible(true);
((btw, posting some of your code would help me figure out what you're trying to ask))
And this is just a guess as to what you're trying to do -- I'm guessing your JPanels are in different classes. Then, you'll need to do this:
class pages extends JFrame implements ActionListener
{
public pages()
{
panelA a = new panelA(this)
}
changeToA(panelB b)
{
remove(panelB);
add(new panelA(this));
}
changeToB(panelA a)
{
remove(panelA);
add(new panelB(this));
}
}
class panelA extends JPanel implements ActionListener
{
pages p;
public panelA(pages p)
{
this.p = p
}
// all that actionlistener code stuff
p.changeToB(this);
}
class panelB extends JPanel implements ActionListener
{
pages p;
public panelB(pages p)
{
this.p = p
}
// all that actionlistener code stuff
p.changeToA(this);
}
You pass the pages class to the panels so the panels can tell the pages class to remove themselves.
((I don't know if there is an easier way, but this is what I do all the time))
I hope I helped :)
You have to remove Panel A from the frame, add Panel B to the frame, and call invalidate on the frame (or containing panel). At least in Swing, I'm not sure about AWT, there you might need repaint or revalidate instead of invalidate.
You could also just create a whole new JFrame and dispose the one containing panel A.