GridBagLayout anchor & JScrollPane issues - java

I have the following class
public class Demo {
private JFrame mainFrame;
static public Color BGCOLOUR1 = new Color(240, 240, 240);
public Demo() {
mainFrame = new JFrame("Demo");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setLayout(new BorderLayout());
mainFrame.setSize(900, 800);
JPanel centralPanel = new JPanel();
centralPanel.setOpaque(true);
centralPanel.setBackground(BGCOLOUR1);
centralPanel.setLayout(new GridBagLayout());
centralPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20),
BorderFactory.createTitledBorder("Panel")));
JPanel insidePanel = new JPanel();
insidePanel.setOpaque(true);
insidePanel.setBackground(BGCOLOUR1);
insidePanel.setLayout(new BorderLayout());
insidePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Inside panel"),
BorderFactory.createEmptyBorder(10, 10, 10, 10)));
JTextArea insidePanelText = new JTextArea(6, 50);
insidePanelText.setLineWrap(true);
insidePanel.add(insidePanelText);
centralPanel.add(insidePanel, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTH,
GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0));
JScrollPane scrollPane = new JScrollPane(centralPanel);
mainFrame.add(scrollPane);
}
Why is the inside panel positioned in the centre of the centralPanel (vertically) when I set the GridBagConstraints anchor to NORTH? I would like it positioned at the top.
Also, if I add the centralPanel in a JScrollPane before adding it to the mainFrame as per the example I can resize the application larger just fine, but as soon as I resize it smaller (even though it is still larger than I originally started it) a scroll bar appears. How can I prevent that from happening?
Edit: To illustrate the scrolling problem (I packed the frame when I took these screens):
When I start the application it has no scrollbars
I resize the window larger, and then smaller again. As soon as I make it smaller, the scrollbar appears.

As far as GridBagLayout is concerned, based on the properties you've supplied, it is been laid out to the NORTH of the cell.
GridBagLayout works mostly to the preferred size of the components and calculates the positions of each component around the center of the parent container.
If, instead of:
new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTH,
GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)
I use something like:
new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0)
it will produce the desired result.
Basically, by using gridy and setting it to 1, we're asking GridBagLayout to give all the remaining vertical space to this cell, but because we're not filling the cell, the contents is pushed to the NORTH of the cell
Also, if I add the centralPanel in a JScrollPane before adding it to the mainFrame as per the example I can resize the application larger just fine, but as soon as I resize it smaller (even though it is still larger than I originally started it) a scroll bar appears. How can I prevent that from happening?
I couldn't really replicate this particular problem, I could use either pack or setSize and resized the window smaller to its "packed" size and the scroll bars would appear, but once I resized the window beyond the "packed" size the scroll bars would disappear

Related

JScrollBar Layout Manager and SpringLayout Manager not working together

public void createSpringLayout(SpringLayout spring, JLabel label, JScrollPane scrollPane, JPanel buttonPanel) {
spring.putConstraint(SpringLayout.NORTH, label, 10, SpringLayout.NORTH, this);
spring.putConstraint(SpringLayout.WEST, label, 10, SpringLayout.WEST, this);
spring.putConstraint(SpringLayout.NORTH, scrollPane, 10, SpringLayout.SOUTH, label);
spring.putConstraint(SpringLayout.WEST, scrollPane, 0, SpringLayout.WEST, label);
spring.putConstraint(SpringLayout.EAST, scrollPane, -10, SpringLayout.EAST, this);
spring.putConstraint(SpringLayout.NORTH, buttonPanel, Spring.constant(10, 30, 30), SpringLayout.SOUTH, scrollPane);
spring.putConstraint(SpringLayout.SOUTH, buttonPanel, -10, SpringLayout.SOUTH, this);
spring.putConstraint(SpringLayout.WEST, buttonPanel, 0, SpringLayout.WEST, label);
}
The two Jpanels with all the stuff on them are using the same superclass for better looks.
However as you can see if the contents of the scrollpane are to wide the scrollpane just uses extra space in the bottom to create a horizontal scrollbar.
Even if I tell the Springlayout that the spring between the scrollpane and the buttonPanel can be between 10 and 30.
I think that first the SpringLayoutManager is called to layout its components and then the ScrollPane comes along and notices that its displayed Components do not fit in the Viewport and creates a Scrollbar, which the SpringLayoutManager is unaware of.
I can't find any solution tha tell the ScrollPane beforehand to calculate its needed Size or for it to just dont use more Space than it has from the beginning.
Do not pass negative numbers to putConstraint.
From the documentation of putConstraint:
Links edge e1 of component c1 to edge e2 of component c2, with a fixed distance between the edges.
The pad value is not an absolute pixel offset, it is the amount of padding (the distance) between the component and the linked edge. It should always be positive.
The same javadoc also describes the pad parameter as:
pad - the fixed distance between dependent and anchor
SpringLayout is complex, which means it’s easy to make mistakes with it. You can easily accomplish the same layout with a BorderLayout:
setLayout(new BorderLayout());
add(label, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

Jpanel is not repainting properly

I'm making a pacman game using java swing.
in my code i use 2 jpanels in the component panel
the first is for the map and the second is for the pacman.
now i am trying to move pacman to other cell when a button is clicked.it is moved but the old picture is not deleted.image before clicking, image after clicking
as you can see the new pacman appears but the old didn't disappear. and some trash also appeared.
this is the code of creating the jpanel for the pacman
JLabel pacman = new JLabel("", new ImageIcon("pacman.png"), JLabel.CENTER);
player = new JPanel(new BorderLayout());
player.setBounds(n*1, n*1, n, n);
//pacman.setOpaque(true);
pacman.setBackground(new Color(0, 0, 0, 0));
player.add(pacman);
//player.setOpaque(true);
player.setBackground(new Color(0, 0, 0, 0));
contentPane.setLayout(null);
JPanel panel = new JPanel();
panel.setBounds(0, 0, 1000, 1000);
panel.setLayout(null);
panel.add(player);
panel.setBackground(new Color(0, 0, 0,0));
contentPane.add(panel);
the code inside the button actionPreformed method is :
panel.remove(player);
player.setLocation(new Point(n*1, n*2));
panel.add(player);
panel.revalidate();
panel.repaint();
how can i make the old pacman disappears ?
player.setBackground(new Color(0, 0, 0, 0));
Don't use transparent colors. Swing does not handle transparency properly.
For full transparency there is a simple solution. Just make the component transparent:
player.setOpaque( false );
If you ever need partial transparency then check out Backgrounds With Transparency for a solution.

Java Swing GridBagLayout panel resize issues

I am working on a simple little swing application for a relative, but I'm having trouble implementing animations on it...
Here is a description of my layout:
I have a main window (created by instantiating, packing, and displaying a JFrame).
I have told the content panel of the main window to use GridBagLayout as its layout.
My grid is 2 grids wide, and 3 grids tall. In the first column I have three JButtons (A, B, and C), each taking up one row of vertical grid space, and one column of horizontal grid space. In the second column, I have another JPanel, which is one column wide, and three rows tall.
This second JPanel is also set to use GridBagLayout as its layout. In this case, there are two columns, and one row. The left column has a one collumn wide, one row tall JPanel with Button 1 inside of it. The right column consists of just a single JButton (Button 2), which is also one column wide, and one row tall.
Here is a screenshot of what I just described:
Now that you understand what the layout is, let me explain what I've been trying to do:
I've been trying to use the Universal Tween Engine to resize the Jpanel which contains Button 1. However, in my attempts, I get this as a result:
As you can probably see, the JPanel containing Button 1 has successfully resized! However, Button 2's size and position have not change at all, and, in fact, button 2 is getting cut off by the JPanel containing Button 1!
This is a problem, as I expected resizing the JPanel containing Button 1 to simultaneously change the cell sizes of its containing GridBagLayout. I was sorely mistaken on this matter, it seems.
So that brings me to my question... What do I need to do in order to "Update" my GridBagLayout so that it resizes its grids to accommodate the increase in size from the Jpanel containing Button 1? I have tried calling GridBagLayout.invalidatelayout() on my GridBaglayout, and I have also tried calling JFrame.getContentPane().invalidate() on my main window. Neither seemed to have had any effect.
Here is the compilable source code of my project:
ImageManager.java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import aurelienribon.tweenaccessors.swing.*;
import aurelienribon.utils.swing.*;
import aurelienribon.tweenengine.*;
import aurelienribon.tweenengine.equations.*;
public class ImageManager
{
/**
* #param args
*/
public static JFrame mainwindow;
public static TweenManager tweenManager;
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable() //For thread safety!
{
public void run()
{
InitGUI();
InitTween();
}
});
}
private static void InitTween()
{
((Window)mainwindow).addWindowListener(new WindowAdapter() {
#Override public void windowOpened(WindowEvent e)
{
new DrawingCanvas()
{
#Override protected void update(int elapsedMillis)
{
tweenManager.update(elapsedMillis);
}
}.start();
}
});
}
private static void InitGUI()
{
//Init the window and layout systems
mainwindow = new JFrame("Imaffect");
Container pane = mainwindow.getContentPane();
pane.setLayout(new GridBagLayout());
//Begin creating the UI!
pane.add(new JButton("Button A"), new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.PAGE_START, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 100, 0));
pane.add(new JButton("Button B"), new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.PAGE_START, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 100, 100));
pane.add(new JButton("Button C"), new GridBagConstraints(0, 2, 1, 1, 0, 0, GridBagConstraints.PAGE_START, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 100, 20));
pane.add(InitPreviewGUI(), new GridBagConstraints(1, 0, 1, 3, 1, 1, GridBagConstraints.PAGE_START, GridBagConstraints.BOTH, new Insets(0,0,0,0), 0, 0));
//Finalize the window
mainwindow.pack();
mainwindow.setVisible(true);
}
private static JPanel InitPreviewGUI()
{
JPanel panel = new JPanel(new GridBagLayout());
panel.add(InitSettingsGUI(), new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.PAGE_START, GridBagConstraints.BOTH, new Insets(0,0,0,0), 0, 0));
panel.add(new JButton("Button 2"), new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.PAGE_START, GridBagConstraints.BOTH, new Insets(0,0,0,0), 0, 0));
return panel;
}
private static JPanel InitSettingsGUI()
{
JPanel panel = new JPanel();
SetupSettingsTweens(panel);
SetupSettingsContent(panel);
return panel;
}
private static void SetupSettingsTweens(JPanel panel)
{
Tween.registerAccessor(Component.class, new ComponentAccessor());
tweenManager = new TweenManager();
Tween.to(panel, ComponentAccessor.WIDTH, 1000)
.target(200)
.ease(Cubic.INOUT)
.repeatYoyo(-1, 200)
.delay(500)
.start(tweenManager);
}
private static void SetupSettingsContent(JPanel panel)
{
panel.add(new JButton("Button 1"));
}
}
If you want to compile the code yourself, you will need the following three resources:
Universal Tween Engine library jar
DrawingCanvas.java (package aurelienribon.utils.swing)
ComponentAccessor.java (package aurelienribon.tweenaccessors.swing)
If you would like to experience my problem first hand, without complicated project setup, you may download the Eclipse Juno project, or the runnable jar file:
Imaffect Eclipse Juno Project
Imaffect Runnable Jar File
If you would prefer to stay away from downloads, here is a list of what's going on:
Set up GUI structure
Set up requirements for tween (nothing special)
Set up a thread that uses SwingUtilities.invokeAndWait to call a function called update()
Whenever update() is called, it uses Container.setSize() to animate size change on the JPanel that contains Button 1.
Any help with this problem is greatly appreciated! Thankyou :)
P.S. If there is a duplicate of this question, I am very sorry. I googled around quite a bit, but I was unable to find anything even related to my problem.
I would say in your update() method you should change preferred size of Button1 and call
container.revalidate();
container.repaint();
Changing the preferred size and revalidate() updates layout and repaint() reflects the changes.
Alternatively you can call setSize() for Button1 increasing width and in the same time decrease width of Button2 and again call repaint() (without revalidate() because revalidation would restore original layout).

How to get GridBagLayout to respect minimum size of a button or panel?

In the following GridBagLayout code, I'm expecting the specified minimum size of JButton btn2 to be respected when the JFrame is resized to be made smaller. But when I make the JFrame smaller, the btn2 gets smaller than its minimum size and then vanishes.
Can someone point me in the right direction of what I'm doing wrong? Maybe I have to set the minimum size of the JPanel that contains the buttons?
Any help is appreciated, thanks!
JFrame frame = new JFrame();
frame.setSize(400,300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setMinimumSize(new Dimension(400,300));
panel.setBackground(Color.RED);
panel.setLayout(new GridBagLayout());
GridBagConstraints gbc = null;
JButton btn1 = new JButton("btn1");
btn1.setPreferredSize(new Dimension(150,50));
btn1.setMinimumSize(new Dimension(150,50));
gbc = new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
new Insets(0,0,0,0), 0, 0);
panel.add(btn1, gbc);
JButton btn2 = new JButton("btn2");
btn2.setPreferredSize(new Dimension(150,150));
btn2.setMinimumSize(new Dimension(150,150));
gbc = new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0,
GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
new Insets(0,0,100,100), 0, 0);
panel.add(btn2, gbc);
frame.getContentPane().add(panel);
frame.setVisible(true);
In addition to Andrew Thompson's answer:
The GBL DOES NOT respect the minimum size set on a component if the component's preferred size can and will be respected.
The GBL DOES respect the minimum size set on a component when the component's preferred size cannot be respected, that is: the component prefers to be of size (w,h) but the GBL sees this is impossible due to lack of space, and therefore it falls back on the advertised minimum size of the component.
It is therefore very much possible to make your component suddenly become larger than it was before when you downsize the containing frame. You only have to set a minimum size on it that is larger than the preferred size. So you are making the frame smaller, and suddenly your component becomes larger. That is... weird but possible :) All hail GBL.
Here is an example of a panel that implements this behavior (GBC is an easier class to use than the original GridBagConstraints):
private static JPanel getPanel() {
JPanel panel = new JPanel(new GridBagLayout());
final JTextField textField = new JTextField("test");
final String text = "text";
final JLabel label = new JLabel(text);
final JButton button = new JButton(new AbstractAction("Enlarge label!") {
private String actText = text;
#Override
public void actionPerformed(ActionEvent arg0) {
actText = actText + text;
label.setText(actText);
}
});
GBC g;
g = new GBC().locate(0, 0).weight(0.0, 0.0).align(GBC.WEST, GBC.HORIZONTAL, null);
textField.setMinimumSize(new Dimension(200,20));
panel.add(textField, g);
g = new GBC().locate(1, 0).weight(1.0, 0.0);
panel.add(label, g);
g = new GBC().locate(0, 1).weight(1.0, 1.0).span(2, 1);
panel.add(button, g);
return panel;
}
In general, I would say to not use components that must be fixed in size within a GBL. Wrap it inside other panels with different layout managers.
If you really want to use your fixed-sized component within a GBL, override its getPreferredSize() method and return the size you want. But I prefer to ignore these methods.
Maybe I have to set the minimum size of the JPanel that contains the buttons?
AFAIR GBL was notorious for ignoring sizing hints. No, a correction on that. To get sensible resizing of components within GBL, use the GridBagConstraints with appropriate values. Beware though, the behavior of the layout to not display any component that would be forced to less than its minimum size.
I would pack() the frame then set the minimum size on the frame. Here is how it might look, changing the last line to..
frame.pack();
frame.setVisible(true);
frame.setMinimumSize(frame.getSize());
Given the layout though, I would tend to put btn1 into the PAGE_START of the BorderLayout of a panel that is then added to the LINE_START of another BL. btn2 would go in the CENTER of the outer BL.

Swing Draw 1px border lines between components in a GridBagLayout component

I have components laid out in a GridBagLayout. I want a 1px black line between all the components, as well as around the JPanel itself.
Currently, I am using a MatteBorder to do this. The parent component has a 1px MatteBorder on the top and left edges. Each child component has a 1px MatteBorder on the right and bottom edges. The horizontal and vertical gaps are zero on the GridBagLayout.
This mostly works, except I'm getting occasional gaps where the child borders meet the parent border.
I suspect this is due to a rounding/floating point inaccuracy in the distribution of extra space to the child components.
Is there a better way to achieve this look?
Attached is a simpler example:
public class SSBGuiTest extends JDialog {
public SSBGuiTest(Frame owner) {
super(owner);
initComponents();
}
public SSBGuiTest(Dialog owner) {
super(owner);
initComponents();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
wrapperPanel = new JPanel();
panelWithTopLeftMatteBorder = new JPanel();
panel1 = new JPanel();
label1 = new JLabel();
panel2 = new JPanel();
label2 = new JLabel();
panel3 = new JPanel();
label3 = new JLabel();
//======== this ========
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
//======== wrapperPanel ========
{
wrapperPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
wrapperPanel.setLayout(new BorderLayout());
//======== panelWithTopLeftMatteBorder ========
{
panelWithTopLeftMatteBorder.setBorder(new MatteBorder(1, 1, 0, 0, Color.black));
panelWithTopLeftMatteBorder.setLayout(new GridBagLayout());
((GridBagLayout)panelWithTopLeftMatteBorder.getLayout()).columnWidths = new int[] {0, 0};
((GridBagLayout)panelWithTopLeftMatteBorder.getLayout()).rowHeights = new int[] {0, 0, 0, 0};
((GridBagLayout)panelWithTopLeftMatteBorder.getLayout()).columnWeights = new double[] {1.0, 1.0E-4};
((GridBagLayout)panelWithTopLeftMatteBorder.getLayout()).rowWeights = new double[] {1.0, 1.0, 1.0, 1.0E-4};
//======== panel1 ========
{
panel1.setBorder(new MatteBorder(0, 0, 1, 1, Color.black));
panel1.setLayout(new BorderLayout());
//---- label1 ----
label1.setHorizontalAlignment(SwingConstants.CENTER);
label1.setText("label1");
panel1.add(label1, BorderLayout.CENTER);
}
panelWithTopLeftMatteBorder.add(panel1, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//======== panel2 ========
{
panel2.setBorder(new MatteBorder(0, 0, 1, 1, Color.black));
panel2.setLayout(new BorderLayout());
//---- label2 ----
label2.setHorizontalAlignment(SwingConstants.CENTER);
label2.setText("label2");
panel2.add(label2, BorderLayout.CENTER);
}
panelWithTopLeftMatteBorder.add(panel2, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
//======== panel3 ========
{
panel3.setBorder(new MatteBorder(0, 0, 1, 1, Color.black));
panel3.setLayout(new BorderLayout());
//---- label3 ----
label3.setHorizontalAlignment(SwingConstants.CENTER);
label3.setText("label3");
panel3.add(label3, BorderLayout.CENTER);
}
panelWithTopLeftMatteBorder.add(panel3, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
wrapperPanel.add(panelWithTopLeftMatteBorder, BorderLayout.CENTER);
}
contentPane.add(wrapperPanel, BorderLayout.CENTER);
pack();
setLocationRelativeTo(getOwner());
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel wrapperPanel;
private JPanel panelWithTopLeftMatteBorder;
private JPanel panel1;
private JLabel label1;
private JPanel panel2;
private JLabel label2;
private JPanel panel3;
private JLabel label3;
// JFormDesigner - End of variables declaration //GEN-END:variables
}
Which looks like this:
I think I'd use BorderFactory.createLineBorder(color, thickness) rather than ...matteBorder, since LineBorder seems to be a bit closer to what you're looking to do. As a convenience, you could also use LineBorder.createBlackLineBorder().
If the outsides of some components aren't quite hitting the insides of their containers, check that the containers (i.e. outer components) don't have non-zero insets set!
An alternate solution might be to let your background container have a black background and 1 pixel insets, and place non-bordered components on it with 1 pixel gaps between them. That should result in very precise black lines everywhere you don't have a component on top, and also eliminates the problem of multiple borders meeting up and resulting in a double-width border.
Finally, it looks to me like you're using components to draw tables. Do you need these components to have behavior of some sort, or would it be fine to really just put tables on your components? You could do this pretty conveniently using e.g. JLabels JTextPanels and setting their text properties to HTML for whatever you'd like to display. Java incorporates a fairly sophisticated HTML layout engine which even handles a goodly subset of CSS, either inline or even from a href'd file.

Categories

Resources