I have a JPanel that I create like so
JPanel statsKeysPanel = new JPanel(new MigLayout("insets 0", "[]", ""));
and populate with a dynamic number of JLabels stacked on top of each other. For the sake of an example:
for(int i = 0; i < 30; i++) {
statsKeysPanel.add(new JLabel("" + i + " key value"), "wrap");
}
I then create and add the scroller like so
JPanel panel = new JPanel(new MigLayout("insets 0", "[center][][center][]", "[][]"));
final JScrollPane keysScroller = new JScrollPane(this.statsKeysPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
keysScroller.setMaximumSize(new Dimension(100, 300));
panel.add(keysScroller, "cell 0 1");
The max of 300 is applied but the 15 out of 30 JLabels that don't fit in 300px are hidden, and scrolling doesn't work. What am I doing wrong? (image below)
final JScrollPane keysScroller = new JScrollPane(this.statsKeysPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
Why are you using NEVER for both the horizontal and vertical scrollbar? I would think this would prevent a scrollbar from appearing.
I generally don't set either property and just let the scrollpane determine when to display the scrollbar. Sometimes I use ALWAYS to reserve space for the scrollbar.
You are using unnecessarily two panels; one will suffice. I think that
you have omitted the code that caused your error.
Here is a working example:
package com.zetcode;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import net.miginfocom.swing.MigLayout;
public class StatsKeyEx extends JFrame {
public StatsKeyEx() {
initUI();
}
private void initUI() {
JPanel pnl = new JPanel(new MigLayout());
for (int i = 0; i < 60; i++) {
pnl.add(new JLabel("" + i + " key value"), "wrap");
}
JScrollPane spane = new JScrollPane(pnl);
spane.setPreferredSize(new Dimension(150, 200));
add(spane);
pack();
setTitle("Scrolling");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
StatsKeyEx ex = new StatsKeyEx();
ex.setVisible(true);
}
});
}
}
The scrollbars are shown as needed.
Related
After reading this answer, I came to using getPreferredSize instead of setPreferredSize. But I still can't use the #Override getPreferredSize, but that's not the main problem I'm facing right now.
I have a CardLayout application which calls a class called HiraganaData
HiraganaData is a class which extends a JPanel so it can be used by the CardLayout, but it also has 2 more JPanels on it, one for a "back" button and one for the rest of buttons, before using this idea I was using a JTable, but faced problems on making cells as buttons, so I dropped the idea and came with this new one using GridLayout. Some of the buttons will be disabled, anyway I can do that and won't include that code since it's not relevant.
So my actual question or problem is:
How can I add a JScrollPane only to buttonsPanel, did my best
trying to add it even to the whole "global" pane w/o success.
This is the most aproximate GUI I can do with the same code of my class I just added a JFrame to it.
Not sure if relevant, but I'm using a CardLayout with different sizes, in the way #MadProgrammer suggested on this answer.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.DefaultCellEditor;
import java.awt.Dimension;
public class HiraganaPage extends JPanel {
JFrame frame = new JFrame("Hello");
JButton kanas[][] = new JButton[26][5];
JButton backButton = new JButton("back");
JPanel backPanel = new JPanel();
JPanel buttonsPanel = new JPanel();
public static void main(String args[]) {
new HiraganaPage();
}
public HiraganaPage() {
JPanel pane = new JPanel();
backPanel.add(backButton);
buttonsPanel.setLayout(new GridLayout(0, 5));
pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
pane.add(backPanel);
//pane.setPreferredSize(new Dimension(500, 500));
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 5; j++) {
kanas[i][j] = new JButton("1");
buttonsPanel.add(kanas[i][j]);
}
}
JScrollPane scroll = new JScrollPane(buttonsPanel);
pane.add(buttonsPanel);
this.add(pane, BorderLayout.CENTER);
frame.add(this);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(50, 50);
frame.setSize(300, 300);
}
}
This is how it looks like in my complete application
And this is how it looks like in the MCVE.
The main problem you've facing is the fact that you removing the buttonsPane from the scroll pane when you add it to the pane...
JScrollPane scroll = new JScrollPane(buttonsPanel);
pane.add(buttonsPanel);
this.add(pane, BorderLayout.CENTER);
And the fact that you never actually add the scroll pane to anything doesn't help either.
A component may only have a single parent, so when you add buttonsPane to pane, it is effectively removed from the scroll pane
Instead, set the layout manager of the HiraganaPage to BorderLayout, add the buttonsPanel to the scroll pane and add the scroll pane to the CENTER position of the HiraganaPage then add the pane to the NORTH position of the HiraganaPage
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestScrollPane {
public static void main(String[] args) {
new TestScrollPane();
}
public TestScrollPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new HiraganaPage());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class HiraganaPage extends JPanel {
JFrame frame = new JFrame("Hello");
JButton kanas[][] = new JButton[26][5];
JButton backButton = new JButton("back");
JPanel backPanel = new JPanel();
JPanel buttonsPanel = new JPanel();
public HiraganaPage() {
setLayout(new BorderLayout());
JPanel pane = new JPanel();
backPanel.add(backButton);
buttonsPanel.setLayout(new GridLayout(0, 5));
pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
pane.add(backPanel);
// pane.setPreferredSize(new Dimension(500, 500));
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 5; j++) {
kanas[i][j] = new JButton("1");
buttonsPanel.add(kanas[i][j]);
}
}
add(pane, BorderLayout.NORTH);
add(new JScrollPane(buttonsPanel));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
}
}
My situation is a bit complex, so rather than explain I'll just show a picture of what I currently have:
The idea is that I want to move the instrument selector (the combo box that says "Flute" in each staff) above the staff. However, I always want to keep it in the same place, on the left directly above the staff, even when scrolling horizontally. When scrolling vertically, it should move so that it is always directly above its staff. Kind of like a toolbar. The problem is that it's already inside of a JScrollPane (as there could be multiple staves and you need to scroll both axes and there's one "instrument panel" per staff (though eventually there will be other UI elements to interact with in this pseudo-toolbar local to to staff in which it is attached)). Is this something where using absolute positioning + listening for scroll/resize/window move events is needed? Or is there perhaps a layout I'm not aware of that can do this sort of thing?
Thanks for looking!
Add the components to individual scroll panes, but never show the horizontal scrollbar
Have all the scroll panes share the same BoundRangeModel.
Create a separate JScrollBar component that uses this model. Whenever its scrolls the separate scroll panes will also scroll:
Something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class ScrollSSCCE extends JPanel
{
public ScrollSSCCE()
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
BoundedRangeModel model = null;
for (int i = 0; i < 5; i++)
{
JLabel label = new JLabel("Flute " + i);
label.setAlignmentX(JComponent.LEFT_ALIGNMENT);
add( label );
JTextArea textArea = new JTextArea(3, 20);
textArea.setText("Just some text to make a horizontal scroll necessary");
JScrollPane scrollPane = new JScrollPane( textArea );
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setAlignmentX(JComponent.LEFT_ALIGNMENT);
add( scrollPane );
// Share the horizontal scrollbar model
JScrollBar horizontal = scrollPane.getHorizontalScrollBar();
if (i == 0)
model = horizontal.getModel();
else
horizontal.setModel( model );
}
// Create the scrollbar that uses the shared model
JScrollBar shared = new JScrollBar( JScrollBar.HORIZONTAL );
shared.setModel( model );
shared.setAlignmentX(JComponent.LEFT_ALIGNMENT);
add( shared );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("Scroll SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ScrollSSCCE() );
frame.setLocationByPlatform( true );
frame.setSize(200, 400);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Edit:
You can actually do this without even creating the "shared" scrollbar. Just use the scrollbar of the last scrollpane:
if (i != 4)
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
I need a vertical scroll bar as well.
Don't think you need to create another panel. Just add the current panel directly to a scroll pane:
JScrollPane master = new JScrollPane( new ScrollSSCCE() );
master.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JFrame frame = new JFrame("Scroll SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//frame.add( new ScrollSSCCE() );
frame.add( master );
frame.setLocationByPlatform( true );
frame.setSize(200, 400);
frame.setVisible( true );
That's the last tip I have. I won't be around for a couple of days. Good luck.
I got this working to some extend with the following approach:
Put a JPanel with null Layout over the staff
Add the comboBox to this panel
Make the panel height the same as the comboBox and as width as the staff (as width as you can scroll)
Add an AdjusmentListener to the horizontal bar to update the coordinate X of the comboBoxes.
In other words the comboBoxes slides inside their null layout panels to match the position you are looking at. It is not perfect because this causes the comboBoxes to wiggle a little when you are moving the scrollBar.
The mock interface that I made looking like yours is the following:
Notice how in the screenshot the horizontal bar is moved but the comboBoxes with "Flute" are still visible.
So the most important code is:
Put the comboBox inside a null layout panel:
JComboBox comboBox = new JComboBox(new String[]{"Flute", "Piano", "Cello"});
comboBox.setBounds(0, 0, comboBox.getPreferredSize().width, comboBox.getPreferredSize().height);
_comboBoxes.add(comboBox);
JPanel comboBoxPanel = new JPanel();
comboBoxPanel.setLayout(null);
comboBoxPanel.add(comboBox);
Then you put this panel over the staff, with whatever layout you are using. In my case I had a Panel with a vertical Box Layout containg the comboBoxPanel and the red panel where the staff would be.
Add a listener to the scroll bar and update the comboBoxes position inside their panels:
scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e)
{
for (JComboBox comboBox : _comboBoxes)
{
comboBox.setLocation(e.getValue(), 0);
}
}
});
Full code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollIndependentTest
{
private ArrayList<JComboBox> _comboBoxes = new ArrayList<JComboBox>();
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
new ScrollIndependentTest().createAndShowGUI();
}
});
}
public void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setTitle("Fourier Synthesis");
JPanel listPanel = new JPanel();
listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
for (int i = 0; i < 10; ++i)
{
listPanel.add(createStaffPanel());
}
JScrollPane scrollPane = new JScrollPane(listPanel);
scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener()
{
#Override
public void adjustmentValueChanged(AdjustmentEvent e)
{
for(JComboBox comboBox : _comboBoxes)
{
//if (comboBox.isVisible()) //maybe?
comboBox.setLocation(e.getValue(), 0);
}
}
});
frame.add(scrollPane);
/*
* Cosmetic elements to make it look more similar to your case
*/
JMenuBar menuBar = new JMenuBar();
menuBar.add(new JMenu("File"));
menuBar.add(new JMenu("Synthesis"));
menuBar.add(new JMenu("Help"));
frame.setJMenuBar(menuBar);
JPanel toolBar = new JPanel();
toolBar.setLayout(new BoxLayout(toolBar, BoxLayout.Y_AXIS));
for (int i = 0; i < 10; ++i)
{
toolBar.add(new JButton("Note " + i));
}
frame.add(toolBar, BorderLayout.WEST);
/*
* end
*/
frame.setSize(new Dimension(500, 400));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createStaffPanel()
{
JPanel staffPanel = new JPanel();
staffPanel.setLayout(new BoxLayout(staffPanel, BoxLayout.Y_AXIS));
staffPanel.setBorder(BorderFactory.createLineBorder(Color.black));
JComboBox comboBox = new JComboBox(new String[]{"Flute", "Piano", "Cello"});
comboBox.setBounds(0, 0, comboBox.getPreferredSize().width, comboBox.getPreferredSize().height);
_comboBoxes.add(comboBox);
JPanel comboBoxPanel = new JPanel();
comboBoxPanel.setLayout(null);
comboBoxPanel.add(comboBox);
comboBoxPanel.setPreferredSize(new Dimension(600, comboBox.getPreferredSize().height));
staffPanel.add(comboBoxPanel);
JPanel panel = new JPanel();
panel.setBackground(Color.red);
panel.setPreferredSize(new Dimension(600, 100));
panel.setAlignmentX(Component.LEFT_ALIGNMENT);
staffPanel.add(panel);
return staffPanel;
}
}
I have been writing and refactoring the code that I started with for Hangman game. I have basically two classes now. One is that launch the hangman (contains main) and other is the getting the panel plugged in with various components. In total, I will have one frame and one Panel, few buttons and a Label and textfield. I have pasted my two bits of code below. I wanted to have all the pushed down to the frame, The label and textfield in the center and I need empty space on the top to put more stuff. I tried BorderLayout.SOUTH, but did not help. The border title is spreading all over, but I would like it to have it surround just the radio buttons. Kindly run the code to see what I mean.
Thanks
Code1 : with Main method
import javax.swing.*;
import javax.swing.text.MaskFormatter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.text.ParseException;
public class HangmanGUI {
DetailsPanel myPanel;
public HangmanGUI() throws ParseException {
myPanel = new DetailsPanel();
JFrame myframe = new JFrame();
myframe.getContentPane().setLayout(new BorderLayout());
myframe.getContentPane().add(myPanel, BorderLayout.SOUTH);
myframe.setTitle("Hangman Game");
myframe.setVisible(true);
myframe.setLocationRelativeTo(null);
myframe.pack();
myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) throws ParseException {
new HangmanGUI();
}
}
code 2: with panel and components
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
public class DetailsPanel extends JPanel {
public DetailsPanel() {
setPreferredSize(new Dimension(400, 600));
setBorder(BorderFactory.createTitledBorder(" ciick here "));
createFormattedPanel();
for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {
String buttonText = String.valueOf(alphabet);
JButton letterButton = new JButton(buttonText);
letterButton.addActionListener(clickedbutton());
this.add(letterButton, BorderLayout.CENTER);
}
}
private ActionListener clickedbutton() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
System.out.println("actionCommand is: " + actionCommand);
}
};
}
public void createFormattedPanel() {
MaskFormatter formatter = null;
try {
JLabel label = new JLabel("Guesss");
formatter = new MaskFormatter("? ? ? ? ? ? ?");
formatter.setPlaceholderCharacter('?');
JFormattedTextField input = new JFormattedTextField(formatter);
input.setColumns(20);
this.add(label);
this.add(input);
} catch (java.text.ParseException exc) {
System.err.println("formatter is bad: " + exc.getMessage());
System.exit(-1);
}
}
}
I would use an image JPanel to hold your hangman drawing, and to act as a placeholder and place it in the JFrame in the BorderLayout.CENTER position. I would also clean up the south JPanel by using layout managers and not relying on default FlowLayout. For instance a BorderLayout for the south Jpanel, put the guess in the page start slot and the buttons in the center slot, and put the buttons into a GridLayout JPanel. For example:
import javax.swing.*;
import javax.swing.text.MaskFormatter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.text.ParseException;
public class HangmanGUI {
private DetailsPanel myPanel;
private ImagePanel imagePanel = new ImagePanel();
public HangmanGUI() throws ParseException {
myPanel = new DetailsPanel();
JFrame myframe = new JFrame();
// myframe.getContentPane().setLayout(new BorderLayout());
myframe.getContentPane().add(imagePanel, BorderLayout.CENTER);
myframe.getContentPane().add(myPanel, BorderLayout.SOUTH);
myframe.setTitle("Hangman Game");
// myframe.setVisible(true);
// myframe.setLocationRelativeTo(null);
myframe.pack();
myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myframe.setLocationRelativeTo(null);
myframe.setVisible(true);
}
public static void main(String[] args) throws ParseException {
new HangmanGUI();
}
}
class ImagePanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final String TITLE = "Hangman Image";
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public ImagePanel() {
setBorder(BorderFactory.createTitledBorder(TITLE));
}
}
class DetailsPanel extends JPanel {
public DetailsPanel() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createTitledBorder(" ciick here "));
add(createFormattedPanel(), BorderLayout.PAGE_START);
JPanel letterPanel = new JPanel(new GridLayout(0, 5));
for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {
String buttonText = String.valueOf(alphabet);
JButton letterButton = new JButton(buttonText);
letterButton.addActionListener(clickedbutton());
letterPanel.add(letterButton, BorderLayout.CENTER);
}
add(letterPanel, BorderLayout.CENTER);
}
private ActionListener clickedbutton() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
System.out.println("actionCommand is: " + actionCommand);
}
};
}
public JPanel createFormattedPanel() {
JPanel panel = new JPanel();
MaskFormatter formatter = null;
try {
JLabel label = new JLabel("Guesss");
formatter = new MaskFormatter("? ? ? ? ? ? ?");
formatter.setPlaceholderCharacter('?');
JFormattedTextField input = new JFormattedTextField(formatter);
input.setColumns(20);
// this.add(label);
// this.add(input);
panel.add(label);
panel.add(input);
} catch (java.text.ParseException exc) {
System.err.println("formatter is bad: " + exc.getMessage());
System.exit(-1);
}
return panel;
}
}
Also, don't set the position of a component or set it visible until after adding all and packing all. Don't set a component's size but rather let the preferred sizes and the layout managers set the sizes for you.
Edit
You ask:
first of all thank you for helping out. This is the solution I wanted. I have some basic questions though. why did you comment out // myframe.getContentPane().setLayout(new BorderLayout());
The JFrame's contentPane already uses BorderLayout by default so it would be unnecessary to explicitly set it to this layout.
also you created three panels, which is nice. for imagepanel, you specified the dimension. but for detailsPanel, you did not specify the dimension. does it mean?
I figure that the image will have a definite size, and so I override its getPreferredSize so that it will be big enough to show the image. All other components, I let them size themselves based on their component's preferred sizes and their layout managers.
if I have many panels and I specify dimension(size) for one panel, all others will get that default on the frame.
Again, all others will have their own preferred sizes based on the preferred sizes of their components and their layout managers.
"IF" I understand correctly, why not just add the buttons to their own panel and set the title border on it instead, for example...
public DetailsPanel() {
setPreferredSize(new Dimension(400, 600));
createFormattedPanel();
JPanel buttons = new JPanel();
buttons.setBorder(BorderFactory.createTitledBorder(" ciick here "));
for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {
String buttonText = String.valueOf(alphabet);
JButton letterButton = new JButton(buttonText);
letterButton.addActionListener(clickedbutton());
buttons.add(letterButton, BorderLayout.CENTER);
}
add(buttons);
}
You count create a GridLayout for your buttons like so:
public DetailsPanel() {
setPreferredSize(new Dimension(400, 600));
createFormattedPanel();
JPanel buttons = new JPanel();
buttons.setBorder(BorderFactory.createTitledBorder(" ciick here "));
buttons.setLayout(new GridLayout(5,6));
for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {
String buttonText = String.valueOf(alphabet);
JButton letterButton = new JButton(buttonText);
letterButton.addActionListener(clickedbutton());
buttons.add(letterButton);
}
add(buttons);
}
Then I also added an extra dummy Panel in your main with just a label, change this label to what ever component you want:
myPanel = new DetailsPanel();
JFrame myframe = new JFrame();
JPanel aDummy = new JPanel();
aDummy.add(new JLabel("Extra text"));
myframe.getContentPane().setLayout(new BorderLayout());
myframe.getContentPane().add(aDummy, BorderLayout.CENTER);
myframe.getContentPane().add(myPanel, BorderLayout.SOUTH);
//...etc
Comes out like this
I'm trying to figure out how to create a vertical TitledBorder in a JPanel.
I've got this situation:
I'd like to have "Actuators st..." placed vertically, so user can read it.
Is there a way to do it, or should I implement my own customized JPanel & TitledBorder?
maybe crazy idea but is possible with JSeparator too :-)
required proper LayoutManager, maybe GridBagLayout (JComponent placed without GBC can take PreferrredSize from JComponent, but isn't resiziable), not GridLayout
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
public class NestedLayout {
private JFrame frame = new JFrame();
private JPanel leftPanel = new JPanel();
private JSeparator sep = new JSeparator();
private JLabel label = new JLabel("<html> L<br>a<br>b<br>e<br>l<br></html>");
public NestedLayout() {
label.setOpaque(true);
sep.setOrientation(JSeparator.VERTICAL);
sep.setLayout(new GridLayout(3, 1));
sep.add(new JLabel());
sep.add(label);
sep.add(new JLabel());
leftPanel.setLayout(new BorderLayout());
leftPanel.setBorder(BorderFactory.createEmptyBorder(
10, //top
10, //left
10, //bottom
10)); //right
leftPanel.add(sep, BorderLayout.CENTER);
leftPanel.setPreferredSize(new Dimension(40, 220));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(leftPanel, BorderLayout.WEST);
//frame.add(label);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
NestedLayout nestedLayout = new NestedLayout();
}
});
}
}
As shown in How to Use Borders, you can create a compound border using an empty border and a titled border.
Addendum: As an alternative, you can use the border's getMinimumSize() method to ensure that the title is visible. See also this related Q&A.
f.add(createPanel("Actuator status"), BorderLayout.WEST);
f.add(createPanel("Indicator result"), BorderLayout.EAST);
...
private Box createPanel(String s) {
Box box = new Box(BoxLayout.Y_AXIS);
TitledBorder title = BorderFactory.createTitledBorder(null, s,
TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION);
box.setBorder(title);
for (int i = 0; i < 6; i++) {
JButton b = new JButton(null, UIManager.getIcon("html.pendingImage"));
b.setAlignmentX(JButton.CENTER_ALIGNMENT);
box.add(b);
}
box.validate();
Dimension db = box.getPreferredSize();
int max = Math.max(title.getMinimumSize(box).width, db.width);
box.setPreferredSize(new Dimension(max, db.height));
return box;
}
I searched a little bit and did not find a good answer to my problem.
I am working on a gui that has to be resizable. It contains a status JTextArea that is inside a JScrollPane. And this is my problem. As long as I don't manually resize my JFrame, the "initial" layout is kept and everything looks fine. As soon as I manually resize (if the JTextArea is already in scrolled mode), the layout gets messed up.
Here is a SSCCE (I got rid of most of the parts while keeping the structure of the code. I hope it's more readable that way):
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import net.miginfocom.swing.MigLayout;
public class Tab extends JFrame {
private static final long serialVersionUID = 1L;
private JTextArea messageTextArea;
private JPanel optionPanel, messagePanel;
private JTabbedPane plotTabPane;
public static void main(String[] args) {
final Tab tab = new Tab();
tab.setSize(1000, 600);
tab.setVisible(true);
new Thread(new Runnable() {
#Override
public void run() {
int count = 0;
tab.printRawMessage("start");
while (true) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {}
tab.printRawMessage("\ntestMessage" + count++);
}
}
}).start();
}
public Tab() {
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
}
private void initComponents() {
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new MigLayout("insets 0", "", ""));
mainPanel.add(getLeftTopPanel(), "shrinky, top, w 450!");
mainPanel.add(getRightPanel(), "spany 5, wrap, grow, pushx, wmin 400");
mainPanel.add(getMessagePanel(), "pushy, growy, w 450!");
JScrollPane contentScrollPane = new JScrollPane(mainPanel);
contentScrollPane.setBorder(BorderFactory.createEmptyBorder());
setContentPane(contentScrollPane);
}
protected JPanel getLeftTopPanel() {
if (optionPanel == null) {
optionPanel = new JPanel();
optionPanel.setBorder(BorderFactory.createTitledBorder(null, "Configuration", TitledBorder.LEFT, TitledBorder.TOP, new Font("null", Font.BOLD, 12), Color.BLUE));
optionPanel.setLayout(new MigLayout("insets 0", "", "top, align 50%"));
JLabel label = new JLabel("Choose");
label.setHorizontalAlignment(JLabel.RIGHT);
optionPanel.add(label, "w 65!");
optionPanel.add(new JSeparator(JSeparator.VERTICAL), "spany 5, growy, w 2!");
optionPanel.add(new JComboBox(new String[] {"option1", "option2", "option3"}), "span, growx, wrap");
optionPanel.add(new JLabel("Type"), "right");
optionPanel.add(new JTextField("3"), "w 65!, split 2");
optionPanel.add(new JLabel("Unit"), "wrap");
optionPanel.add(new JLabel("Slide"), "right");
optionPanel.add(new JSlider(0, 100), "span, growx, wrap");
}
return optionPanel;
}
protected JTabbedPane getRightPanel() {
if (plotTabPane == null) {
plotTabPane = new JTabbedPane();
plotTabPane.add("Tab1", new JPanel());
plotTabPane.add("Tab2", new JPanel());
}
return plotTabPane;
}
protected JPanel getMessagePanel() {
if (messagePanel == null) {
messagePanel = new JPanel();
messagePanel.setBorder(BorderFactory.createTitledBorder(null, "Status Console", TitledBorder.LEFT, TitledBorder.TOP, new Font("null", Font.BOLD, 12), Color.BLUE));
messagePanel.setLayout(new MigLayout("insets 0", "", "top, align 50%"));
messagePanel.add(new JScrollPane(getMessageTextArea()), "push, grow");
}
return messagePanel;
}
protected JTextArea getMessageTextArea() {
if (messageTextArea == null) {
messageTextArea = new JTextArea();
messageTextArea.setEditable(false);
messageTextArea.setFont(new Font(null, Font.PLAIN, 20));
messageTextArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
}
return messageTextArea;
}
public void printRawMessage(String rawMessage) {
getMessageTextArea().append(rawMessage);
getMessageTextArea().setCaretPosition(getMessageTextArea().getText().length());
}
}
The layout stuff basically happens in initComponents().
To see the problem:
Start the Application (I used miglayout-4.0-swing.jar).
Wait a bit (don't resize the window), until there are enough messages to create the scrollbar in the status text area.
Now this is what I want. The JTextArea goes all the way to the bottom of the JFrame and is scrolled if neccessary.
Now resize the window. As you can see, everything gets messed up. It will only be fine, if the window is maximized.
Here are two screenshots. The first one is how I want it to be:
The second one is after resizing:
My question: Can somebody tell me, how I keep the layout the way it is before resizing? I want to have the JTextArea go all the way down to the bottom of the window. And if neccessary, the scrollbar should appear. The only way, the status panel can go below the bottom of the window is, if the window is too small (because the configuration panel has a fixed height).
I hope I made myself clear. If not, please ask. ;)
EDIT: You can see the behaviour I want, if you remove the top JScrollPanel (the one that holds all the components). Just change
JScrollPane contentScrollPane = new JScrollPane(mainPanel);
contentScrollPane.setBorder(BorderFactory.createEmptyBorder());
setContentPane(contentScrollPane);
to
setContentPane(mainPanel);
to see what I mean. Unfortunately, this way I loose the scrollbars if the window is very small.
Focusing on your status area and using nested layouts produces the result shown below. Note in particular,
Use invokeLater() to construct the GUI on the EDT.
Use javax.swing.Timer to update the GUI on the EDT.
Use pack() to make the window fit the preferred size and layouts of its subcomponents.
Use the update policy of DefaultCaret to control scrolling.
Avoid needless lazy instantiation in public accessors.
Avoid setXxxSize(); override getXxxSize() judiciously.
Critically examine the decision to extend JFrame.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.border.TitledBorder;
import javax.swing.text.DefaultCaret;
public class Tab extends JFrame {
private JTextArea messageTextArea;
private JPanel optionPanel, messagePanel;
private JTabbedPane plotTabPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final Tab tab = new Tab();
tab.setVisible(true);
Timer t = new Timer(200, new ActionListener() {
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
tab.printRawMessage("testMessage" + count++);
}
});
t.start();
}
});
}
public Tab() {
initComponents();
}
private void initComponents() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel(new GridLayout(1, 0));
Box leftPanel = new Box(BoxLayout.Y_AXIS);
leftPanel.add(getLeftTopPanel());
leftPanel.add(getMessagePanel());
mainPanel.add(leftPanel);
mainPanel.add(getRightPanel());
this.add(mainPanel);
this.pack();
this.setLocationRelativeTo(null);
}
protected JPanel getLeftTopPanel() {
optionPanel = new JPanel();
optionPanel.setBorder(BorderFactory.createTitledBorder(null,
"Configuration", TitledBorder.LEFT, TitledBorder.TOP,
new Font("null", Font.BOLD, 12), Color.BLUE));
JLabel label = new JLabel("Choose");
label.setHorizontalAlignment(JLabel.RIGHT);
optionPanel.add(label);
optionPanel.add(new JSeparator(JSeparator.VERTICAL));
optionPanel.add(new JComboBox(
new String[]{"option1", "option2", "option3"}));
optionPanel.add(new JLabel("Type"));
optionPanel.add(new JTextField("3"));
return optionPanel;
}
protected JTabbedPane getRightPanel() {
plotTabPane = new JTabbedPane();
plotTabPane.add("Tab1", new JPanel());
plotTabPane.add("Tab2", new JPanel());
return plotTabPane;
}
protected JPanel getMessagePanel() {
messagePanel = new JPanel(new GridLayout());
messagePanel.setBorder(BorderFactory.createTitledBorder(null,
"Status Console", TitledBorder.LEFT, TitledBorder.TOP,
new Font("null", Font.BOLD, 12), Color.BLUE));
final JScrollPane sp = new JScrollPane(getMessageTextArea());
sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
messagePanel.add(sp);
return messagePanel;
}
protected JTextArea getMessageTextArea() {
messageTextArea = new JTextArea("", 10, 19);
messageTextArea.setEditable(false);
messageTextArea.setFont(new Font(null, Font.PLAIN, 20));
messageTextArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
DefaultCaret caret = (DefaultCaret) messageTextArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
return messageTextArea;
}
public void printRawMessage(String rawMessage) {
messageTextArea.append(rawMessage + "\n");
}
}
Add size constraints to your mainPanel in the initComponents method. For instance :
mainPanel.setMinimumSize(new Dimension(400, 400));
mainPanel.setPreferredSize(new Dimension(400, 400));
mainPanel.setMaximumSize(new Dimension(400, 400));