How to make this 3 column splitter in Java? - java

I'm using Java and MiGLayout to try and re-create this layout:
http://www.methvin.com/splitter/3csplitter.html
So something like this:
Each column needs to be resizable
The size of the left and right columns stay the same size when resizing the main window
Middle column fills all available space and changes size when resizing
In other words, the left and right panel need to "stick" to the left and right side of the window, but also be resizable (and not change proportionally when being resized)
I've tried many things, but the resizing is always the problem. Here is the current code which is an attempt at doing this with a nested JSplitPane.
public class MainGUI extends JFrame {
private String app_name = "Layout Test";
private int window_x_min = 700;
private int window_y_min = 450;
public MainGUI() {
setTitle(app_name);
setSize(window_x_min + 200, window_y_min + 100);
setMinimumSize(new Dimension(window_x_min, window_y_min));
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JPanel panel = new JPanel();
this.setContentPane(panel);
panel.setLayout(new MigLayout("","[]","[grow]"));
JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitpane.setContinuousLayout(true);
splitpane.setTopComponent(new JButton("middle"));
splitpane.setBottomComponent(new JButton("right"));
JSplitPane splitpane2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitpane2.setContinuousLayout(true);
splitpane.setDividerLocation(450);
splitpane2.setBottomComponent(splitpane);
splitpane2.setTopComponent(new JButton("left"));
panel.add(splitpane2, "push, grow");
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
I've managed to make it load up initially how I like, however with the following issues:
Resizing from the right side changes size of the the "right" panel, not the size of the middle panel
Resizing from the left side changes the size of the "right" panel as well
It appears that the left side of the app is working, I need the right side to function the same way. Hope that I'm making sense, thanks!

You need to manage how the space is allocated when the split panes are resized. This is done by using the setResizeWeight() method. By default the value is 0.0f which means the "left" component is fixed. So you need to manipulate this property on one of the split panes. Read the API for more information on how this property works.
I don't use MigLayout, but doing a simple test using a standard BorderLayout all you need to add is:
splitpane.setResizeWeight(1.0f);
Also, when you post a SSCCE don't forget to include the main() method so we can just copy/paste/execute the code. We should not need to do any extra work.

Related

JPanel above JLabel using Layered Panes

I have a JPanel that contains a bunch of small icons, and a JLabel containing a bigger icon.
First of all, I need clarification on something. By not setting panel.isOpaque, the said panel would have a transparent background and would properly overlap the icons of my JPanel and JLabel. Is this true?
Now to my problem. I've been following the LayeredPanes tutorial on the Oracle website, and I can't seem to make it right on my case.
JFrame window = new JFrame();
ImageIcon underIcon;
URL urlUnder = myClass.class.getResource("images/underImage.gif");
underIcon = new ImageIcon(urlUnder);
JLabel labelUnder = new JLabel(underIcon);
ImageIcon panelIcon;
URL urlAbove = myClass.class.getResource("images/aboveImage.gif");
panelIcon = new ImageIcon(urlAbove);
JLabel aboveIcon1 = new JLabel(panelIcon);
JLabel aboveIcon2 = new JLabel(panelIcon);
JPanel panelAbove = new JPanel(new BorderLayout());
panelAbove.setOpaque(false);
panelAbove.add(aboveIcon1, BorderLayout.WEST);
panelAbove.add(aboveIcon2, BorderLayout.EAST);
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setLayout(new BorderLayout());
layeredPane.add(labelUnder, BorderLayout.CENTER, 1);
layeredPane.add(panelAbove, BorderLayout.CENTER, 2);
layeredPane.setOpaque(true);
window.setContentPane(layeredPane);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
I only see the contents of the panel. And I'm 100% sure the images are there on getResource, so that's not the issue. Please advice on how to properly use Layered Panes.
The problem is you are setting a layout manager to the JLayeredPane
layeredPane.setLayout(new BorderLayout());
In of it self, this is not a problem, but your choice of layout manager is.
BorderLayout will only a single componet to occupy any of it's 5 predefined positions. This means when you add your second component, it, effectively, replaces the first.
Try using someone like GridBagLayout instead. Providing a single GridBagConstaint set so that the weightx/y values ara equal to 1, the fill property is set to BOTH and it's gridx/y properties are set to 0
Unlike BorderLayout, GridBagLayout will allow you to layout components to the same position
From the Java Tutorial:
layeredPane.add(dukeLabel, new Integer(2), 0);
This code uses the three-argument version of the add method. The third argument specifies the Duke label position within its depth, which determines the component's relationship with other components at the same depth.
Positions are specified with an int between -1 and (n - 1), where n is the number of components at the depth. Unlike layer numbers, the smaller the position number, the higher the component within its depth. Using -1 is the same as using n - 1; it indicates the bottom-most position. Using 0 specifies that the component should be in the top-most position within its depth. As the following figure shows, with the exception of -1, a lower position number indicates a higher position within a depth.
So your depth orders are the wrong way around, also start from 0.
I also found this in the Solving Common Component Problems Section
Problem: The components in my layered pane are not layered correctly. In fact, the layers seem to be inversed — the lower the depth the higher the component.
This can happen if you use an int instead of an Integer when adding components to a layered pane. To see what happens, in the LayeredPaneDemo class, change
layeredPane.add(label, new Integer(i));
to
layeredPane.add(label, i);.

Dynamically create list of jpanels swing/java

I am attempting to create panel, PluginListPanel (extending JPanel), which will show a list of plugin panels which will respect the preferred height of the plugin's panel, but will force the width. I have a solution, but it is slow and has a weird bug. In the screenshot, there are two such panels, one to the left and one to the right:
I don't know different layout manager systems very well, but I know that the TOP field in the BorderLayout does what I want. So, I came up with this 'recursive' solution:
public PluginListPanel(List<PanelContainer> items) {
JPanel body = this;
for (PanelContainer item : items) {
body.setLayout(new BorderLayout(0, 0));
JPanel panel = new PluginOnePanel(item);
body.add(panel, BorderLayout.NORTH);
JPanel newBody = new JPanel();
body.add(newBody, BorderLayout.CENTER);
body = newBody;
}
}
The problem I encounter is when I scroll, the system responds somewhat slowly, and the colour of the SamplePluginPanels is different (see picture below), even if the number of SamplePluginPanels is as low as 8.
The question is, how can I make this more elegant, not slow down the program and not miscolour the panels?
Any insights are highly appreciated.
I think a vertical Box is the answer (actually 2, on both columns):
Box leftBox = Box.createVerticalBox();
Box rightBox = Box.createVerticalBox();
/* put them together */
setLayout(new GridLayout(2,1));
add(leftBox);
add(rightBox);
Also make sure, that the content of the scroll pane implements Scrollable and returns true, to scrollableTracksViewportWidth(). This will force equal widths.

JScrollPane does not update scrollbar

I am trying to create a JScrollPane that contains a JPanel that will be increasing and decreasing in height. When it becomes larger than the size of the JScrollPane, it should create a vertical scroll bar which will allow me to scroll through the entire JPanel. However, I am having difficulty achieving this. Yes, I know I am not using LayoutManagers. No, I will not be using them, and I need a solution that does not involve their usage.
Here are the two button's AbstractActions that add and subtract from the JPanel:
class AddACT extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
info.setSize(420,info.getHeight() + 40);
info.add(new SubPanel); // Adds another JPanel into the main JPanel (for content input)
gui.repaint();
infoS.validate();
}
}
class RemoveACT extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
info.remove(subPanel()); // This would remove the last JPanel added to the main JPanel
info.setSize(420,info.getHeight() - 40);
gui.repaint();
infoS.validate();
}
And here is the code for the main JPanel and the JScrollPane:
final JPanel info = new JPanel();
final JScrollPane infoS = new JScrollPane(info, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
info.setLayout(null);
info.setSize(420,600);
infoS.setLocation(10,80);
infoS.setSize(420,490);
gui.add(infoS); // gui is the frame's content pane (the overall JPanel)
This is the second project I've been trying to learn GUI by doing. I am a complete novice in Swing and am only intermediate in Java. Sorry if I am making a blindingly obvious mistake.
1) Use LayoutManagers (+1 to #kleopatra and #GagandeepBali comments)
The absence of LayoutManagers only guarantees your GUI's will look very trashy (especially when run on other OSes/builds) and being a Novice you should rather learn the correct way than learn the wrong way and get into bad habits like calling setSize() etc.
Have a read on these links to get you started:
A Visual Guide to Layout Managers
Concurrency in Swing
2) See this example for how to use a JScrollPane, it simply adds a JPanel with buttons to a JScrollPane which in-turn is added to the JFrame.
3) Also see this example for how to make the JScrollPane vertically scroll-able only.
4) For more on JScrollPanes have a look here: How to Use Scroll Panes.
5) As for how it interacts with LayoutManager, if you do not explicitly set its size via setPreferredSize(Dimension d) the scroll pane computes it based on the preferred size of its nine components (the viewport, and, if present, the two scroll bars, the row and column headers, and the four corners)
6) On your usage of validate():
validate() is used when new JComponents are added to a visible component
revalidate() is used when JComponent is removed/added from a visible component
revalidate() covers validate() too
Thus always use this:
//add or remove component(s)
revalidate();
repaint();
References:
http://www.daniweb.com/software-development/java/threads/405568/validate-vs-revalidate
LayoutManager is not required to solve the problem. The problem in Thrfoot's example is in these lines:
final JScrollPane infoS = new JScrollPane(info, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
info.setLayout(null);
info.setSize(420,600);
The program appears to recognize there is a need for scroll bars (it would show the scroll bar if your setting was VERTICAL_SCROLLBAR_AS_NEEDED), but the actual scrolling does not work (the scroll bar slider is not there).
To fix this, first set the preferred size of info, then construct the infoS.
Example:
info.setPreferredSize(420,600);
final JScrollPane infoS = new JScrollPane(info, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
The idea is to set the preferred size of the info panel before it is used for the scroll pane. This is the same reason to set the size and location of infoS before adding to the gui:
infoS.setLocation(10,80);
infoS.setSize(420,490);
gui.add(infoS); // gui is the frame's content pane (the overall JPanel)

Showing specific part of an image in a limited space?

I have a frame of 100x100 and an image of 400x400, I have a layered pane on the entire frame and the image inside the layered pane. As you can notice the image is bigger than the frame so only a part of the image i.e. the top-left part of the image will be shown. Suppose there is a man in the image at X=32 and y=40 and width = 10 and height = 10 (basically a rectangle) w.r.t. the frame.
Questions
the image is 400x400 right? That means at runtime I can only see 100x100 part of the image and the part of the image that has been shown is the top-left part right? But suppose I want to show a building that is situated at 350x350 w.r.t. the image, then how do I do it (since it is located in the bottom part of the image so it wont be shown)?
suppose the first question is solved, then assume there is a building at 350x350 with width = 20 and height = 20 w.r.t the 'frame'. Now when at runtime I show the image (now the bottom part of the image is visible, assuming question 1 is solved) the co-ordinates of the building w.r.t. frame should not change i.e. they should remain 350x350 w.r.t. the frame.
Point 1) I'm guessing you're adding the image in a JPanel and then into the JFrame or the image into a JPanel which goes into a JScrollPane which finally lands inside the JFrame. JPanels and JScrollPanes both support programmatic scrolling through the use of #scrollRectToVisible.
Here's a (kind of) working sample of the scrollRectToVisible
JPanel panel = new JPanel();
JPanel panel2 = new JPanel();
panel2.setPreferredSize(new Dimension(200, 11000));
SpringLayout layout = new SpringLayout();
panel2.setLayout(layout);
for (int i = 0; i < 100; i++) {
JLabel textField = new JLabel();
textField.setText("textfield number " + i);
textField.setPreferredSize(new Dimension(150, 100));
layout.putConstraint(SpringLayout.NORTH, textField, i * 110, SpringLayout.NORTH, panel2);
panel2.add(textField);
}
JScrollPane scrollPane = new JScrollPane(panel2, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(200, 500));
panel2.scrollRectToVisible(new Rectangle(0, 800, 150, 100));
panel.add(scrollPane);
Add the panel variable to a JFrame and you're good to run this code. Also, please note that this is meant to be sample code, not the actual way code should be written to add components to panels :P
Point 2) I still don't fully understand this query so I'll give you an answer in 2 parts.
a) I don't see why relative calculations matter? Can you try the sample I provided and see if it does what it needs to? If not, read option b
b) the Rectangle object has a setLocation(int, int) (Reference: Java documentation) that you should play around with. I can't give you a more accurate answer or a code sample since I don't full understand what you're trying to do and I don't have a code sample that I can play with.

How to limit dynamic addition of components within the borders of the form or a panel

I am trying to add components to a jpanel dynamically from a database, one after the other, however they extend beyond the limits of the form (and even the screen) horizontally. I am using a panel with BoxLayout which positions inner components on the x-axis. What I would like to do is limit the addition of components on the x-axis at the border of the form (or the border of the container panel) .
What I tried so far is:
To set the maximum size (width) of the
container jpanel but that did not
work.
To set the layout manager to
flowlayout, but it also expands
infinitely on the x-axis.
To position the container panel inside a
scrollpane. That makes a huge
horizontal scrollbar, which means that
it does not limit the components to
the border of the form.
Should I make a custom layout? Any ideas simpler than that?
My code looks like:
jpanelCases.setLayout(new BoxLayout(this.jpanelCases, BoxLayout.X_AXIS));
db = Database.getInstance();
List<Category> cats = db.getCategories();
for(Category c : cats){
JPanel jp = new JPanel();
//addition of other components to the newly created panel here
jpanelCases.add(jp);
}
however they extend beyond the limits of the form (and even the screen) horizontally.
So what do you want to do in this case:
a) just ignore the components and not add them to the frame
b) display the components on a new line
If the answer is "b", the try the WrapLayout.
Try to put 2 glues, one at first one at last:
jpanelCases.setLayout(new BoxLayout(this.jpanelCases, BoxLayout.X_AXIS));
jpanelCases.add(Box.createHorizontalGlue());
db = Database.getInstance();
List<Category> cats = db.getCategories();
for(Category c : cats){
JPanel jp = new JPanel();
//addition of other components to the newly created panel here
jpanelCases.add(jp);
}
jpanelCases.add(Box.createHorizontalGlue());
What about using JScrollPane on the JPanel? This will contain the elements within the panel without exceeding its physical boundaries. Otherwise, if you do not like the scroll idea I suggest that you somehow keep track of the total sum of width of elements added to the panel with respect to the panel physical width, and stop adding elements when the next element width added to the sum exceeds the panel width.
I hope this helps!

Categories

Resources