I just can't get this right. I have a slider to increase my JPanel's size (used as a canvas to draw on).
Whenever the JPanel receives the event, I resize it with setBounds() and I can see it resizing for a split second, but a next Paint or something switches it back to the original size given by the slider's preferred size property.
public class ShapesMainFrame extends JFrame {
private PaintCanvas paintCanvas;
public ShapesMainFrame() {
[...]
JScrollPane scrollPane = new JScrollPane(paintCanvas);
scrollPane.setPreferredSize(new Dimension(1,600));
add(scrollPane, BorderLayout.CENTER);
pack();
}
}
public class PaintCanvas extends JPanel {
[...]
public void setScale(int value) {
setSize(1000,1000);
}
}
So when I try to change the size of the JPanel to a big value it should resize and the scrollbars should appear right? Well it stays the same 600px tall how I set it at the start.
Never use setSize() or setBounds when using a layout manager. Its the "preferred size" that is important. Normally the preferred size of a component is determined automatically by the layout manager. But if you are doing custom painting on the panel you may need to determine the preferred size manually.
The scrollbars will appear when the preferred size of the panel is greater than the size of the scroll pane. Override the getPreferredSize() method (preferred solution) or use the setPreferredSize() method of the custom panel.
All you need to do is call revalidate() on the content within the JScollPane after updating it's size. Also, use the setPreferredSize() when using a layout manager.
public void setScale(int value) {
setPreferredSize(new Dimension(1000, 1000);
revalidate();
}
That will force the JScrollPane to update it's scrollbars.
Also, you could call
paintCanvas.revalidate()
If you wanted to update the JScrollPane from outside of your paintCanvas class
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.
i' m programming an application which works with swing components, i notice one thing on which i would an explanation
i have these classes:
this enum on which i instantiate the gui dimension
public enum GuiDimension {
WIDTH(700), HEIGHT(400);
private final int value;
private GuiDimension(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
this class that starts the application
private GamePanel gamePanel = new GamePanel();
public static void main(String[] args) {
new MainFrame();
}
public MainFrame() {
initGameFrame();
}
private void initGameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(gamePanel);
setResizable(false);
setUndecorated(true);
pack();
setVisible(true);
setLocationRelativeTo(null);
}
}
and this class that set the size of the panel
public class GamePanel extends JPanel {
public GamePanel() {
setPreferredSize(new Dimension(GuiDimension.WIDTH.getValue(),GuiDimension.HEIGHT.getValue()));
//it makes other stuff that are not of interest for this contest
}
}
What I noticed is that, it is true that enums are not really integers but objects ,but when I return
GuiDimension.WIDTH.getValue()
GuiDimension.HEIGHT.getValue()
they return integers that can well be used for other purposes once it has been taken.
now if I insert this on:
SetSize (new Dimension (GuiDimension.WIDTH.getValue (), GuiDimension.HEIGHT.getValue ()));
or
SetSize (GuiDimension.WIDTH.getValue (), GuiDimension.HEIGHT.getValue ());
instead of this,which i inserted in the example
setPreferredSize(new Dimension(GuiDimension.WIDTH.getValue(),GuiDimension.HEIGHT.getValue()));
the frame is displayed with wrong dimension, and I do not understand why.
If GuiDimension.WIDTH.getValue () and GuiDimension.WIDTH.getValue ()) are correct for setPreferredSize (...),
why is not the same for setSize (int,int) and for setSize(Dimension) ?
when tested this simple code you can see that.
Most of the layout managers will ignore calls a component's size but will respect its preferredSize, and sometimes the minimum and maximum, and so when you call pack(), your size will change to what the layout managers and the constituent component preferred sizes think should be the best size.
Incidentally, per kleopatra (Jeanette), if you absolutely need to set a component's preferred size, you're better off overriding getPreferredSize() than by calling setPreferredSize(...). The latter can be overridden by calling setPreferredSize(...) on the same component elsewhere while the former can't.
As an aside, in your example code, you are using WIDTH twice and appear to be not using HEIGHT.
Edit
You had a comment that was deleted regarding pack and component sizes. My reply to it was:
The pack() method requests that the layout managers do their laying out of components, and its the layout managers that matter here -- what do they look at, the size vs. the preferredSizes. If you read the javadoc and tutorials for most of the layout managers, you'll see that they respect the preferred sizes most. Some, like BoxLayout, also look at the maximum size and minimum size as well.
I created a class that extends JComponent. An object of that class is added to a JPanel. A JPanel has a default flow layout manager. When I added the JPanel to a JFrame, nothing was visible except an empty JFrame. Yes, the frame is visible and sized to the maximum screen dimensions.
I tried several modifications to change this problem and deduced:
Component size - set it to no avail and still an empty JFrame
Intermediate panel - adding component to an intermediate panel and still an empty JFrame
Finally I decided to change the layout manager of the panel I was adding my component to and changed the panel layout manager to a border layout. I then added the component to the center and now it appears.
Follow-on questions I have are:
When making a custom JComponent, what are my considerations?
Why do I have to change the layout manager of a panel for a custom JComponent?
My naivety asks "If I can add a JButton to a panel and, using the default flow layout manager it shows, why not a custom JComponent?
My custom JComponent is an inner class:
public class OuterClass
{
private class Panel extends JPanel
{
public Panel()
{
add(new Custom());
}
}
private class Custom extends JComponent
{
public Custom()
{
// Initialization of members but not size of component
}
}
}
A JPanel has a default flow layout manager.
Yes, and the FlowLayout respects the preferred size of every component. Your component doesn't have a preferred size so it defaults to (0, 0).
Override the getPreferredSize() method to return the appropriate dimension.
"Why do I have to change the layout manager of a panel for a custom JComponent?"
The thing about JComponent is that it has no default LayoutManager to layout the components. If you run this test, you'll see
public static void main(String[] args) {
JComponent component = new JComponent() {};
System.out.println("JComponent = " + component.getLayout());
JPanel panel = new JPanel();
System.out.println("JPanel = " + panel.getLayout());
}
The result is
JComponent = null
JPanel = java.awt.FlowLayout[hgap=5,vgap=5,align=center]
So with JComponent, since it has a null layout, it doesn't know where to layout the the component. So you either have to
Explicity setBounds() on the component you want to add, or
Explicity set the layout, like you mentioned you had to do for the JComponent.
"Finally I decided to change the layout manager of the panel I was adding my component to and changed the panel layout manager to a border layout. I then added the component to the center and now it appears."
So by setting the LayoutManager to BorderLayout, you told the JComponent how to layout the components you add. It can be any LayoutManager though, not just BorderLayout
I have some panels in a card layout container (no idea if that is correct terminology). I can't find a way to set the location, or size of these panels inside the container. I tried setBounds and setLayout(null) and I still can't get anything to change.
These are my fields and the constructor. I've gotten my frame working and I can see and use the buttons to change cards, but I really can't change much else about the cards. I set the two card panels two have different backgrounds, but they only make a small boarder of color around the button and leave it in the centre of the screen.
I also don't understand why this isn't pasting my code properly... So sorry!
public class TestPanel extends JPanel implements ActionListener {
CardLayout cl = new CardLayout();
private JPanel panelCont = new JPanel();
private JPanel panel1 = new JPanel();
private JPanel panel2 = new JPanel();
private static JButton but1 = new JButton("Change panels");
private static JButton but2 = new JButton("Change back");
public TestPanel() {
panelCont.setLayout(cl);
panel1.add(but1);
panel2.add(but2);
panel1.setBackground(Color.black);
panel2.setBackground(Color.blue);
panelCont.add(panel1, "1");
panelCont.add(panel2, "2");
cl.show(panelCont, "1");
but1.addActionListener(this);
but2.addActionListener(this);
add(panelCont);
}
}
Thanks. I apologise in advance. I'm finding it hard to understand card layout.
A CardLayout respects the preferred size of the panels added to the layout. That is the size will be the size of the largest panel added to the layout.
I set the two card panels two have different backgrounds, but they only make a small boarder of color around the button and leave it in the centre of the screen.
The default layout for a panel is the FlowLayout. A FlowLayout by default has a 5 pixel horizontal/vertical gap around each component. So the preferred size of your panel is the size of the button plus the 5 pixel gap.
The panel is displaying correctly. When you add other components to the panel the size will change as required.
It's not clear where you pack() the enclosing Window. By default, pack() causes a panel having CardLayout to adopt the the size of the largest panel's preferred size, which is determined by the size of its contents. This example uses setPreferredSize() to specify an arbitrary size, but you can override getPreferredSize() as shown here.
I am implementing a Comment box facility in my application which user can resize using mouse. This comment box contains a scrollpane which instead contains a JEditorPane in which user can insert comment. I have added the editor pane inside a scroll pane for the following reason:
auto scolling of jeditorpane
When the user resizes the comment box, I am setting the desired size for JScrollPane and the JEditorPane. When the user is increasing the size of the comment box, the size of these components are increasing as desired but when the size of the comment box is decreased, the size of the JEditorPane does not decrease even after setting the size. This leads to the scrollbars inside the scrollpane.
I tried using setPreferrredSize, setSize, setMaximumSize for JEditorPane. Still the size of the editor pane is not reducing. I tried calling revalidate() or updateUI() after the setting of size but no use.
I am using Java 1.4.2.
Please provide me some insight....
I realise this is long since answered, but for future reference all you need to do is override the getScrollableTracksViewportWidth() to always return true, eg.
JEditorPane pane = new JEditorPane() {
public boolean getScrollableTracksViewportWidth() {
return true;
}
};
panel.add(new JScrollPane(pane));
Actually it is possible, luiscubal. Here is how,
To the JScrollPane add a ComponentListener for resize events
public static void main(String...args) {
//our test frame
JFrame frame = new JFrame("JEditorPane inside JScrollPane resizing");
frame.setLayout(new BorderLayout());
//our editing pane
final JEditorPane editor = new JEditorPane();
//our simple scroll pane
final JScrollPane scroller = new JScrollPane(editor);
//NOTE: this is the magic that is kind of a workaround
// you can also implement your own type of JScrollPane
// using the JScrollBar and a JViewport which is the
// preferred method of doing something like this the
// other option is to create a JEditorPane subclass that
// implements the Scrollable interface.
scroller.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
editor.setSize(new Dimension(
scroller.getWidth()-20,
scroller.getHeight()-20));
}
});
//just use up the entire frame area.
frame.add(scroller, BorderLayout.CENTER);
//quick and dirty close event handler
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(320, 240); //something not too big
frame.setLocationRelativeTo(null); //centers window on screen
frame.setVisible(true); // normally done in a SwingUtilities.invokeLater
}
Look luiscubal it is possible. Don't be so quick to announce things in Java as not possible. The swing api is quiet flexible and can do a lot of the work for you. However, if you use JComponents in ways they weren't made to be used you will end up with problems and have two options.
subclass subclass subclass basically create your own component.
find a work around, like the above solution.
Decreasing the size of a JEditorPane in a JScrollPane and then reducing it, is not possible.
You may want to use a JTextArea instead.