I am having a JFrame looking like this:
When it is resized, it will make the JComboBoxes adapt to the size of the JFrame like this:
which is what I want. But when I add a JScrollPane, the JComboBoxes no longer adapt:
Any ideas how to make the JComboBoxes still fill HORIZONTAL with the added JScrollPane?(making the JScrollPane only effect the VERTICAL scrolling)
public class FillHorizonScrollVertical extends JFrame {
private boolean withScroller = true;
private JPanel content;
public FillHorizonScrollVertical() {
// FILL = BOTH for JFrame
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
this.setLayout(new GridBagLayout());
// GridLayout = All content gets the same size
content = new JPanel(new GridLayout());
// ADD TO THE FRAME
JScrollPane scroller = new JScrollPane(content);
if (withScroller)
this.add(scroller, c);
else
this.add(content, c);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
// ADDING THE BOXES AFTER SETTING VISIBLE
addBoxes();
this.pack();
}
/**
* Create JComboBoxes and adding them to the JPanel
*/
private void addBoxes() {
// CREATE JCOMBOBOXES
JComboBox cb1 = new JComboBox();
cb1.addItem("");
cb1.addItem("aaaaaaaaaaaaaaaaaa");
JComboBox cb2 = new JComboBox();
cb2.addItem("");
JComboBox cb3 = new JComboBox();
cb3.addItem("");
content.add(cb1);
content.add(cb2);
content.add(cb3);
}
public static void main(String[] args) {
new FillHorizonScrollVertical();
}
}
You make use of the Scrollable interface which will allows you to flag of it should track the viewports width or height. In this case, you want to track the width, which will force the view to always be the same size as the view port...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Rectangle;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScrollableExample {
public static void main(String[] args) {
new ScrollableExample();
}
public ScrollableExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new TestPane()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Scrollable {
public TestPane() {
setLayout(new GridLayout());
JComboBox cb1 = new JComboBox();
cb1.addItem("");
cb1.addItem("aaaaaaaaaaaaaaaaaa");
JComboBox cb2 = new JComboBox();
cb2.addItem("");
JComboBox cb3 = new JComboBox();
cb3.addItem("");
add(cb1);
add(cb2);
add(cb3);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 32;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 32;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
}
Take a look at How to use scroll panes for more details
You can set the scrolling policy on the JScrollPane:
JScrollPane scroller = new JScrollPane(content,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
You might want to check out the Scrollable interface too, which allows fine-tuning of scrolling behaviour.
Solved by adding:
cb1.setPreferredSize(new Dimension(0, 30));
cb2.setPreferredSize(new Dimension(0, 30));
cb3.setPreferredSize(new Dimension(0, 30));
Related
JTextField, JSlider, JComboBox, etc added to a JComponent are not displayed in the JFrame containing the JComponent. It seems only drawing by the Graphics parameter allows painting. The included test program compares using JPanel to JComponent in my efforts to discover how to display components added to a JComponent. Is there any way to get such components displayed?
public class TestPaints {
public static void main(String[] args) {
new TestPaints();
}
JTextField _text1;
JLabel _label1 = new JLabel("Text1");
JTextField _text2;
JLabel _label2 = new JLabel("Text2");
TestPaints() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Paint a Widget");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(3, 2));
GridBagConstraints grid = new GridBagConstraints();
frame.add(new JLabel("TextField in JComponent "));
grid.gridx = 2;
frame.add(new JLabel("TextField in JPanel"), grid);
grid.gridy = 2;
grid.gridx = 1;
frame.add(new TestJComponent(), grid);
grid.gridx = 2;
frame.add(new TestJPanel(), grid);
grid.gridy = 3;
grid.gridx = 1;
/* tabbing between the two TextFields shows that keystrokes are seen */
frame.add(_label1, grid);
grid.gridx = 2;
frame.add(_label2, grid);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestJComponent extends JComponent {
public TestJComponent() {
setPreferredSize(new Dimension(100, 30));
_text1 = new JTextField(6);
_text1.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
_label1.setText(_text1.getText());
_label1.repaint();
}
});
_text1.setOpaque(true);
_text1.setVisible(true);
add(_text1);
/* This doesn't work
JPanel panel = new JPanel();
panel.add(_text1);
add(panel); */
setOpaque(true);
setVisible(true);
setBackground(Color.green);
}
public void paint(Graphics g) {
super.paint(g); // did not do background. Rectangle r = g.getClipBounds(); // needs this
g.setColor(getBackground());
g.fillRect(r.x, r.y, r.width, r.height);
/* Variations such as these don't work */
_text1.setOpaque(true);
_text1.setVisible(true);
_text1.paintComponents(g);
}
}
class TestJPanel extends JPanel {
TestJPanel() {
_text2 = new JTextField(6);
_text2.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
_label2.setText(_text2.getText());
_label2.repaint();
}
});
add(_text2);
setBackground(Color.blue);
}
}
}
Edit: you need to give your JComponent a layout such as FlowLayout for components to show properly since it does not have a default layout like JPanel has. So add setLayout(new FlowLayout()) into your JComponent's constructor
You have:
frame.setLayout(new GridLayout(3, 2));
and then try to add components to the JFrame's contentPane using GridBagConstraints, and this doesn't make sense. If you want to use these constraints, then the container needs to use GridBagLayout, not GridLayout.
Also this is dangerous code:
public void paint(Graphics g) {
super.paint(g); // did not do background. Rectangle r = g.getClipBounds(); // needs this
g.setColor(getBackground());
g.fillRect(r.x, r.y, r.width, r.height);
/* Variations such as these don't work */
_text1.setOpaque(true);
_text1.setVisible(true);
_text1.paintComponents(g);
}
You should be overriding JComponent's paintComponent method, not its paint method (call super.paintComponent) and should not be setting component visibility or calling a component's paintComponents method directly within any painting method.
Another issue: don't use KeyListeners within Swing text components but rather add a DocumentListener to the component's Document. Otherwise you risk breaking some of the functionality of the text component, and also your listener won't work for copy/paste, while the DocumentListener will.
And another issue, your main issue: you need to give the JComponent a layout. It does not default to FlowLayout like a JPanel does. This is why the added components are not showing within it.
For example:
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class TestPaints2 {
private JTextField textField1 = new JTextField(8);
private JTextField textField2 = new JTextField(8);
private JLabel label1 = new JLabel("Text1");
private JLabel label2 = new JLabel("Text2");
public TestPaints2() {
textField1.getDocument().addDocumentListener(new MyDocListener(label1));
textField2.getDocument().addDocumentListener(new MyDocListener(label2));
TestJComponent2 jComponent = new TestJComponent2();
jComponent.add(textField1);
TestJPanel2 jPanel = new TestJPanel2();
jPanel.add(textField2);
JPanel mainPanel = new JPanel(new GridLayout(0, 2));
mainPanel.add(new JLabel("JComponent"));
mainPanel.add(new JLabel("JPanel"));
mainPanel.add(jComponent);
mainPanel.add(jPanel);
mainPanel.add(label1);
mainPanel.add(label2);
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class MyDocListener implements DocumentListener {
private JLabel label;
public MyDocListener(JLabel label) {
this.label = label;
}
#Override
public void changedUpdate(DocumentEvent e) {
updateLabel(e);
}
#Override
public void insertUpdate(DocumentEvent e) {
updateLabel(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
updateLabel(e);
}
private void updateLabel(DocumentEvent e) {
Document doc = e.getDocument();
int offset = doc.getLength();
try {
String text = doc.getText(0, offset);
label.setText(text);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new TestPaints2());
}
}
class TestJComponent2 extends JComponent {
private static final Color BG = Color.GREEN;
private static final int GAP = 5;
public TestJComponent2() {
setOpaque(true);
setBackground(BG);
setLayout(new FlowLayout());
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
class TestJPanel2 extends JPanel {
private static final Color BG = Color.BLUE;
private static final int GAP = 5;
public TestJPanel2() {
setBackground(BG);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
}
}
I'm trying to have a JPanel of varying size (potentially much wider than the standard screen) inside of a JScrollPanel. Currently it works out great, and I have configured the scrollbars to work fine manually, however I would like the JPanel to "scroll" constantly to the left, so that over time the whole thing is displayed. All of the answers I found are specific to JTextArea and use Carets, or use rectToVisible. Neither of these will work because I'm trying to scroll internally to a single JPanel.
I've included what I believe to be all of the relevant code below.
center is the JPanel (of which Grid is a subclass, used to paint specifically a grid with some specific cells colored) with a BorderLayout that I would like to autoscroll.
public GuiViewFrame(Song playMe) {
String[][] songArray = playMe.to2DArray();
this.displayPanel = new ConcreteGuiViewPanel(playMe);
main = new JPanel();
main.setLayout(new BorderLayout());
displayPanel.setLayout(new BorderLayout());
center = new Grid(playMe);
labels = new Labels(playMe);
horiz = new Horiz(playMe);
center.setPreferredSize(new Dimension(10 * songArray.length, 10 * songArray[0].length));
horiz.setPreferredSize(new Dimension(10 * songArray.length, 10));
horiz.setVisible(true);
main.add(center, BorderLayout.CENTER);
main.add(horiz, BorderLayout.NORTH);
scroll = new JScrollPane(main,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
add(scroll, BorderLayout.CENTER);
labels.setPreferredSize(new Dimension(20, 10 * songArray[0].length));
labels.setVisible(true);
add(labels, BorderLayout.WEST);
JScrollBar horiz = scroll.getHorizontalScrollBar();
InputMap im = horiz.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke("RIGHT"), "positiveUnitIncrement");
im.put(KeyStroke.getKeyStroke("LEFT"), "negativeUnitIncrement");
im.put(KeyStroke.getKeyStroke("HOME"), "minScroll");
im.put(KeyStroke.getKeyStroke("END"), "maxScroll");
this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.pack();
}
The project as a whole is to generate a view for playing music that combines MIDI and a GUI, but right now once MIDI plays enough of the song, the relevant notes are off screen. I would like to scroll at a rate to keep pace with MIDI.
You can set the value of the horizontal scrollbar to control what is currently visible:
JScrollBar horizontal = scroll.getHorizontalScrollBar();
horizontal.setValue( horizontal.getValue() + ??? );
You would need to use a Swing Timer to schedule the scrolling at an appropriate interval.
Simple example of using a Timer to scroll text:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class TimerTest extends JPanel implements ActionListener
{
JLabel timeLabel;
JLabel scrollLabel;
public TimerTest()
{
setLayout( new BorderLayout() );
timeLabel = new JLabel( new Date().toString() );
add(timeLabel, BorderLayout.NORTH);
scrollLabel = new JLabel( "Some continuously scrolling text!! " );
add(scrollLabel, BorderLayout.SOUTH);
int time = 1000;
javax.swing.Timer timer = new javax.swing.Timer(time, this);
timer.setInitialDelay(1);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
timeLabel.setText( new Date().toString() );
String oldText = scrollLabel.getText();
// Scroll right to left
String newText = oldText.substring(1) + oldText.substring(0, 1);
// Scroll left to right
// int length = oldText.length();
// String newText = oldText.substring(length-1, length)
// + oldText.substring(0, length-1);
scrollLabel.setText( newText );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TimerTest() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
One possible solution might be to take advantage of JComponent#scrollRectToVisible and a Swing Timer
For example...
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScrollTest {
public static void main(String[] args) {
new ScrollTest();
}
public ScrollTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new TestPane()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements Scrollable {
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JViewport viewport = (JViewport) getParent();
Rectangle viewRect = viewport.getViewRect();
if (viewRect.x + viewRect.width < getWidth()) {
viewRect.x += 2;
scrollRectToVisible(viewRect);
} else {
((Timer)e.getSource()).stop();
}
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1000, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawLine(0, 0, getWidth(), getHeight());
g2d.dispose();
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 200);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 64;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 64;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width <= getParent().getSize().width;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return getPreferredSize().height <= getParent().getSize().height;
}
}
}
Hey Guys my problem is when I place the mouse on a JButton in my JFrame, I want it to show a list of JButtons on its left.
I don't known how to do that really I feel like I'm blocked and I cant make any progress in my project.
I'd would be grateful if you could help me and thanks in advance.
Can you create the list of buttons in a JPanel, add it to your JFrame and then call myPanel.setVisible(false). When you click your button then call myPanel.setVisible(true)?
As for ensuring that myPanel is positioned correctly you will want to use a Layout Manager
Or is there a more complex behaviour you want?
A basic option would be to use a MouseListener and a CardLayout. The MouseListener would be used to determine when the mouse cursor enters/exists a given component and the CardLayout would be used to display the appropriate sub component for each "menu" element.
I have to say, JButton would be me last choice for the "menu" item, in most cases, a JLabel would be preferred or even perhaps using a JMenu, which can can have sub menus, which can be displayed automatically might be a better choice, or even a JComboBox....
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ShowStuff {
public static void main(String[] args) {
new ShowStuff();
}
public ShowStuff() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
MenuPane menu = new MenuPane();
menu.addMenu("Fruit", new FruitPane());
menu.addMenu("Meat", new MeatPane());
menu.addMenu("Dairy", new DairyPane());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(menu);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MenuPane extends JPanel {
private JPanel subMenu;
private JPanel menu;
private CardLayout cardLayout;
private MouseListener mouseHandler;
public MenuPane() {
setLayout(new BorderLayout());
cardLayout = new CardLayout();
subMenu = new JPanel(cardLayout);
menu = new JPanel(new GridBagLayout());
add(subMenu);
add(menu, BorderLayout.WEST);
subMenu.add(new JPanel(), "BLANK");
mouseHandler = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (e.getSource() instanceof JButton) {
JButton btn = (JButton) e.getSource();
cardLayout.show(subMenu, btn.getText());
}
}
#Override
public void mouseExited(MouseEvent e) {
cardLayout.show(subMenu, "BLANK");
}
};
}
public void addMenu(String name, JPanel subMenuPane) {
JButton button = new JButton(name);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
menu.add(button, gbc);
subMenu.add(subMenuPane, name);
button.addMouseListener(mouseHandler);
}
}
public abstract class ButtonPane extends JPanel {
private int gridy = 0;
public ButtonPane() {
setLayout(new GridBagLayout());
}
protected void addButton(String name) {
JButton btn = new JButton(name);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = gridy++;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(btn, gbc);
}
}
public class FruitPane extends ButtonPane {
public FruitPane() {
addButton("Banana");
addButton("Grapes");
addButton("Apples");
addButton("Tomatoes");
}
}
public class MeatPane extends ButtonPane {
public MeatPane() {
addButton("Lamb");
addButton("Beef");
addButton("Pork");
addButton("Mince");
}
}
public class DairyPane extends ButtonPane {
public DairyPane() {
addButton("Milk");
addButton("Cream");
addButton("Cheese");
addButton("Yoghurt");
}
}
}
When the addComponents() method is run once, I get the desired layout of my JPanel. However, when the code in addComponents() is executed more than once, the layout of the JPanel is completely wrong. Is there anything that I seem to be doing wrong?
public class DeleteStudent extends JPanel {
public SearchPanel search = new SearchPanel();
private final JButton deleteButton = new JButton("Delete from database");
private GridBagConstraints cons = new GridBagConstraints();
private final GridBagLayout gridBag = new GridBagLayout();
public DeleteStudent() {
super();
setLayout(gridBag);
setPreferredSize(new Dimension(400, 300));
addComponents();
addComponents(); //Method fails when run more than once!
}
public void addComponents() {
cons.gridy = 1;
cons.insets = new Insets(50, 0, 0, 0);
gridBag.setConstraints(deleteButton, cons);
removeAll();
add(search);
add(deleteButton);
update();
}
private void update() {
revalidate();
repaint();
}
Screenshots:
JPanel after 1 method call: http://img402.imageshack.us/img402/6409/oncer.png
JPanel after 2 method calls: http://imageshack.us/scaled/landing/254/twiced.png
Adding to my comment:
Seems problem is you set the JPanels GridBagLayout constraints before calling removeAll() it should be done after calling removeAll(); so that when we add the new components the LayoutManager is still in effect and hasnt been reset to its defaults values.
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
createAndShowGui();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void createAndShowGui() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DeleteStudent());
frame.pack();
frame.setVisible(true);
}
}
class DeleteStudent extends JPanel {
public JPanel search = new JPanel() {//for testing
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
};
private final JButton deleteButton = new JButton("Delete from database");
private GridBagConstraints cons = new GridBagConstraints();
private final GridBagLayout gridBag = new GridBagLayout();
public DeleteStudent() {
super();
setLayout(gridBag);
addComponents();
addComponents(); //Method fails when run more than once!
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
public void addComponents() {
removeAll();//must call this before resetting Layout and adding new components
cons.gridy = 1;
cons.insets = new Insets(50, 0, 0, 0);
gridBag.setConstraints(deleteButton, cons);
add(search);
add(deleteButton);
update();
}
private void update() {
revalidate();
repaint();
}
}
This code should produce a frame with 2 vertical split panes
that should initialise so that the dividers are evenly placed.
However, this only works if the frame size is fixed.
It seems this is because the sum of the preferred heights of the split
pane is too big for my screen (1280x1024). The JFrame height reported
by getSize() ends up being greater than my screen height and thus
the dividers are badly placed.
How should this be done when using frame.pack()?
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class TestSplitPanels extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
TestSplitPanels tps = new TestSplitPanels();
frame.setContentPane(tps);
frame.pack();
// uncomment this and the dividers are nicely positioned
// frame.setSize(600,600);
frame.setVisible(true);
}
public TestSplitPanels() {
JTable jt1 = new JTable();
JTable jt2 = new JTable();
JTable jt3 = new JTable();
// wrap every JTable in a scroll pane
JScrollPane jsr1 = new JScrollPane();
jsr1.setViewportView(jt1);
JScrollPane jsr2 = new JScrollPane();
jsr2.setViewportView(jt2);
JScrollPane jsr3 = new JScrollPane();
jsr3.setViewportView(jt3);
final JSplitPane jsl1 = new JSplitPane();
final JSplitPane jsl2 = new JSplitPane();
/*
* Make a vertical split pane who's top component is
* is the first scroll pane and bottom component is
* another scroll pane. Try to set the divider location
* at a third of the height of the parent component,
* when that value is known
*/
jsl1.setOrientation(JSplitPane.VERTICAL_SPLIT);
jsl1.setTopComponent(jsr1);
jsl1.setBottomComponent(jsl2);
jsl1.addAncestorListener(new BaseAncestorListener() {
#Override
public void ancestorAdded(AncestorEvent event) {
jsl1.setDividerLocation(getSize().height/3);
}
});
/*
* Set components and set the divider position as before
* for the second split pane
*/
jsl2.setOrientation(JSplitPane.VERTICAL_SPLIT);
jsl2.setTopComponent(jsr2);
jsl2.setBottomComponent(jsr3);
jsl2.addAncestorListener(new BaseAncestorListener() {
#Override
public void ancestorAdded(AncestorEvent event) {
jsl2.setDividerLocation(getSize().height/3);
}
});
setLayout(new BorderLayout());
add(jsl1, BorderLayout.CENTER);
}
public static class BaseAncestorListener
implements AncestorListener {
#Override
public void ancestorAdded(AncestorEvent event) {
}
#Override
public void ancestorRemoved(AncestorEvent event) {
}
#Override
public void ancestorMoved(AncestorEvent event) {
}
}
}
I used setResizeWeigth to tell the JSplitPanes how to distribute size. As a bonus, the split panes keep the even distribution even when you resize the window. They will lose the even distribution once you resize one of the panes.
Note that you do not necessarily need the AncestorListener anymore. I used it just to print out the sizes of the JScrollPanes.
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
public class TestSplitPanels extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final TestSplitPanels tps = new TestSplitPanels();
frame.setContentPane(tps);
frame.pack();
frame.setVisible(true);
}
public TestSplitPanels() {
JTable jt1 = new JTable();
JTable jt2 = new JTable();
JTable jt3 = new JTable();
final JScrollPane jsr1 = new JScrollPane();
final JScrollPane jsr2 = new JScrollPane();
final JScrollPane jsr3 = new JScrollPane();
jsr1.setViewportView(jt1);
jsr2.setViewportView(jt2);
jsr3.setViewportView(jt3);
JSplitPane jsl1 = new JSplitPane();
JSplitPane jsl2 = new JSplitPane();
jsl1.setOrientation(JSplitPane.VERTICAL_SPLIT);
jsl1.setTopComponent(jsr1);
jsl1.setBottomComponent(jsl2);
jsl1.setResizeWeight(0.33); // <-- here
jsl2.setOrientation(JSplitPane.VERTICAL_SPLIT);
jsl2.setTopComponent(jsr2);
jsl2.setBottomComponent(jsr3);
jsl2.setResizeWeight(0.5); // <-- here
this.addAncestorListener(new BaseAncestorListener() {
#Override
public void ancestorMoved(AncestorEvent event) {
System.out.println("jsr1 size: " + jsr1.getSize());
System.out.println("jsr2 size: " + jsr2.getSize());
System.out.println("jsr3 size: " + jsr3.getSize());
System.out.println("----");
}
});
setLayout(new BorderLayout());
add(jsl1, BorderLayout.CENTER);
}
public static class BaseAncestorListener implements AncestorListener {
#Override
public void ancestorAdded(AncestorEvent event) {
}
#Override
public void ancestorRemoved(AncestorEvent event) {
}
#Override
public void ancestorMoved(AncestorEvent event) {
}
}
}
jsl2.addAncestorListener(new BaseAncestorListener() {
#Override
public void ancestorAdded(AncestorEvent event) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jsl2.setDividerLocation(getSize().height / 3); }
});
}
});
You can use the method : setDividerLocation in the JSplitPaneClass