Reputation: 173
I am making an editor for a game i am creating, and i have problems implementing a scroll-able level-overview. The overview is supposed to have this layout :
The Canvas is supposed to always have a size of 1280x720, so i want to use a JScrollPane to be able to still view the entire canvas when the JFrame gets smaller. Now to achieve this i used a GridBagLayout and set a preferred size on the canvas. The problem is that expanding the JFrame will eventually allow you to expand the canvas beyond its preferred size. To stop this i set the layout of the scrollpanes viewport to a FlowLayout to prevent it from resizing the view its holding. This seems to create two problems:
1 : The GridBagLayout no longer distributes the space correctly when expanded to full screeen.
2 : When you make the ScrollPane smaller than its viewport view, the view gets cut off, also scrolling doesn't seem to work.
To Summarize my Questions :
Is there another/better way to implement this UI, and if not how can i solve the issues of the gridbaglayout not distributing space correctly when expanded to fullscreen and cutting off the viewports view ?
Here the code i used for the examples :
public class Main extends JFrame {
public Main() {
this.setSize(800, 400);
this.setMinimumSize(new Dimension(400, 400));
this.setLocationRelativeTo(null);
JPanel optionsPanel = new JPanel();
optionsPanel.setBackground(Color.red);
JPanel scrollContentPane = new JPanel();
scrollContentPane.setPreferredSize(new Dimension(700,700));
scrollContentPane.setBorder(BorderFactory.createLineBorder(Color.red));
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().setLayout(new FlowLayout());
scrollPane.getViewport().setView(scrollContentPane);
scrollPane.setHorizontalScrollBarPolicy(scrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel contentPanel = new JPanel();
contentPanel.setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.fill = gc.BOTH;
gc.weightx = 1.0;
gc.weighty = 1.0;
gc.gridx = 0;
gc.gridy = 0;
contentPanel.add(optionsPanel, gc);
gc.gridx = 1;
contentPanel.add(scrollPane, gc);
this.setContentPane(contentPanel);
this.setVisible(true);
}
public static void main(String[] args) {
Main main = new Main();
}
}
Here is the actual code i am using in my programm :
previewPanel = new PreviewPanel(this);
controlPanel = new ControlPanel(this);
centerPanel = new JPanel();
centerPanel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = c.BOTH;
c.weightx = 1;
c.weighty = 1;
// #HACK, fill grids with empty labels
c.gridx = 0;
c.gridy = 0;
centerPanel.add(new JLabel(), c);
c.gridx = 1;
centerPanel.add(new JLabel(), c);
c.gridx = 2;
centerPanel.add(new JLabel(), c);
c.gridx = 3;
centerPanel.add(new JLabel(), c);
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
centerPanel.add(controlPanel, c);
c.gridx = 1;
c.gridy = 0;
c.gridwidth = 3;
centerPanel.add(previewPanel, c);
setLayout(new BorderLayout());
add(centerPanel, BorderLayout.CENTER);
add(menuBar, BorderLayout.NORTH);
}
Upvotes: 0
Views: 129
Reputation: 811
Border cut off; this is caused because the JScrollPane
doesn't recognize the border as a part of the component. So when you scroll all the way to the edge you will see the first non border pixel of your component. To solve this instead of setting layout on the viewport create another panel add the canvas to this new panel and set this new panel as the viewport in your scroll pane.
JPanel scrollContentPane = new JPanel();
// to set the size properly see later
JPanel scrollContentPaneViewport = new JPanel(new FlowLayout());
scrollContentPaneViewport.add(scrollContentPane);
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().setView(scrollContentPaneViewport);
To restrict the size of your canvas (scrollContentPane) you shouldn't set sizing hints set[minimum|preferred|maximum]size
(reason is here) instead you should subclass the panel and override the get[minimum|preferred|maximum]size
methods, override the ones you need but if you want to make the panel always fixed size you should override all three.
JPanel scrollContentPane = new JPanel() {
// this is important never return this dimension always make a copy
private final Dimension d = new Dimension(700, 700);
@Override
public Dimension getPreferredSize() {
return new Dimension(d);
}
@Override
public Dimension getMinimumSize() {
return new Dimension(d);
}
@Override
public Dimension getMaximumSize() {
return new Dimension(d);
}
};
To make sure that the optionsPanel and the scrollPane are always equally divided use GridLayout
instead of GridBagLayout
.
Edit: GridBagLayout
component relative distribution
You should provide the layout with information on what to do with the extra space. This is done by setting the rowWeights
and columnWeights
fields.
JPanel contentPanel = new JPanel();
GridBagLayout gbl_contentPanel = new GridBagLayout();
// This tells the layout that first row should take all available space (there is only one row)
gbl_contentPanel.rowWeights = new double[]{1.0};
// This tells the layout that second column should be 3 times bigger than first column (3 is 3 times bigger than 1)
gbl_contentPanel.columnWeights = new double[]{1.0, 3.0};
contentPanel.setLayout(gbl_contentPanel);
This should tell the GridBagLayout
how to distribute extra space but it still won't work.
It is because GridBagLayout
is one of the layouts that respects preferred size returned from components. The two components you added are "telling" the layout their minimum maximum and preferred size and layout takes it into account when calculating how much space the components should get.
To solve that you should override getPreferredSize()
in both components to return equal dimensions. (I like to return 0, 0)
JPanel optionsPanel = new JPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension();
}
};
Upvotes: 2
Reputation: 324098
Don't play with the layout manager of the viewport
If you want to control the size of the panel added to the viewport, then add your panel to a wrapper panel instead. Something like:
JScrollPane scrollPane = new JScrollPane();
//scrollPane.getViewport().setLayout(new FlowLayout());
//scrollPane.getViewport().setView(scrollContentPane);
JPanel wrapper = new JPanel();
wrapper.add( scrollContentPane );
scrollPane.getViewport().setView(wrapper);
Upvotes: 2