I am new to Swing and cannot find a page that helps me understand JTabbedPane. I cannot find a way to control the layout of components of the tabbed panels. I can layout each of my panels correctly as separate GUIs but not in a tabbed pane like I need to do. I would like to use the BorderLayout not FlowLayout.
Also, you can see I'm trying to use colors to keep track of my panels and their components. I cannot set the background of the JTabbedPane. It is still the default grey. Can someone tell me why this is?
Thank you for any advice you can give.
What I have so far appears to follow a 'flow layout' despite any changes I've tried
(Methods have been removed or nearly removed to keep code shorter)
public class GUIFrame extends JFrame {
public GUIFrame(String title) {
JFrame frame = new JFrame(title);
Container c = frame.getContentPane();
buildGUI(c);
setFrameAttributes(frame);
}
private void buildGUI(Container c) {
c.setLayout(new BorderLayout());
c.setBackground(Color.BLACK);
JTabbedPane tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
tabs.setBackground(Color.YELLOW);
c.add("Center", tabs);
tabs.addTab("Specialty", new SpecialtyPanel());
tabs.addTab("Treatment", new TreatmentPanel());
tabs.addTab("Doctor", new DoctorPanel());
tabs.addTab("Patient", new PatientPanel());
}
private void setFrameAttributes(JFrame f) {
f.setSize(500, 500);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
MedicalSystemIO test = new MedicalSystemIO();
new GUIFrame("Tabbed Title");
}
public class SpecialtyPanel extends JPanel implements ActionListener {
JTextField jteInput = null;
DefaultListModel<String> model = new DefaultListModel<String>();
JList<String> list = new JList(model);
JScrollPane pane = new JScrollPane(list);
public SpecialtyPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.black));
buildGUI(panel);
}
private void buildGUI(JPanel panel) {
JPanel jpaInput = createInputPanel();
JPanel jpaProcess = createProcessPanel();
JPanel jpaOutput = createOutputPanel();
//panel.setLayout(new BorderLayout());
add("North", jpaInput);
add("Center", jpaProcess);
add("South", jpaOutput);
}
private JPanel createInputPanel() {
JPanel jpaInput = new JPanel();
jpaInput.setBackground(Color.RED);
return jpaInput;
}
private JPanel createProcessPanel() {
JPanel jpaProcess = new JPanel();
jpaProcess.setBackground(Color.BLUE);
return jpaProcess;
}
private JPanel createOutputPanel() {
JPanel jpaOutput = new JPanel();
jpaOutput.add(pane);
return jpaOutput;
}
The SpecialtyPanel is shown that way (flow layout) as you are putting the components on it in the wrong way:
No need for passing a new panel into the buildGUI method as you want to put them directly on the SpecialtyPanel which already is a JPanel,
you commented out the setting of the BorderLayout and
you used the wrong notation of passing the layout constraints in the add methods.
Your constructor and build method should look like this:
public SpecialtyPanel() {
buildGUI();
}
private void buildGUI() {
setBorder(BorderFactory.createLineBorder(Color.black));
JPanel jpaInput = createInputPanel();
JPanel jpaProcess = createProcessPanel();
JPanel jpaOutput = createOutputPanel();
setLayout(new BorderLayout());
add(jpaInput, BorderLayout.NORTH);
add(jpaProcess, BorderLayout.CENTER);
add(jpaOutput, BorderLayout.SOUTH);
}
To have the panel another color than gray you have to color the component that is put on the tabbed pane as it covers the whole space. Add the desired color to the buildGUI method, e.g.:
private void buildGUI(JPanel panel) {
// ...
setBackground(Color.YELLOW);
}
As a JPanel is opaque by default (that means not transparent), you need to set panels on top (except those which you colored explicitly) to be transparent. In case of SpecialtyPanel:
private JPanel createOutputPanel() {
JPanel jpaOutput = new JPanel();
jpaOutput.add(pane);
jpaOutput.setOpaque(false); // panel transparent
return jpaOutput;
}
Related
I have a two JPanels, one that is displayed as a home/welcome page, the other displayed when a user clicks a button. The first panel does not disappear when the button is clicked, the second panel sort of displays its components at the same time so there is two panels worth of buttons/text fields etc both visible at the same time.
How do I fix this so panel1 disappears/panel2 appears?
(If I set the container visibility to false after button click, neither panel's components are displayed.)
public class mainApplication {
private static JFrame mainApp;
private static JPanel panel1;
private static JPanel panel2;
public mainApplication() {
JFrame.setDefaultLookAndFeelDecorated(true);
mainApp = new JFrame("Keystroke Authenticator Application");
mainApp.setSize(640, 480);
mainApp.setLocationRelativeTo(null);
mainApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainApp.add(panel1());
mainApp.setVisible(true);
}
private JPanel panel1() {
panel1 = new JPanel();
panel1.setSize(640,480);
Container contain1 = mainApp.getContentPane();
//Buttons, text fields and labels are configured with groupLayout here
panel1.setVisible(true);
buttonNew.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent clickNew) {
panel2 = panel2();
panel1.setVisible(false);
//contain1.setVisible(false); - neither panel are displayed
}
}
);
return panel1;
}
private JPanel panel2() {
panel2 = new JPanel();
panel2.setSize(640,480);
Container contain2 = mainApp.getContentPane();
//Buttons, text fields and labels are configured with groupLayout here
panel2.setVisible(true);
mainApp.add(panel2);
}
}
I solved my own problem, it seemed to be the fact I was creating a container within each JPanel and using it with the GroupLayout. I removed the created container and replaced the container with the name of the JPanel:
//working code
GroupLayout layout = new GroupLayout(panel1);
panel1.setLayout(layout);
//instead of the original below
GroupLayout layout = new GroupLayout(container1);
container1.setLayout(layout);
I would recommend using a layout manager; this should solve most of your problems.
public mainApplication() {
//normal formatting stuff
mainApp.setLayout(new FlowLayout()); //This will make things appear/disappear
mainApp.setResizable(false) //This will stop your frame from changing sizes on you
}
private JPanel panel1(JFrame frame) {
//normal formatting stuff
frame.add(panel1); //this will make your panel appear in the frame
//more formatting stuff and button creation
buttonNew.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
panel2 = panel2();
frame.remove(panel1);
frame.add(panel2);
frame.pack(); //this swaps out your components so that the frame displays panel2 instead. Pack makes it repaint itself.
}
return panel1;
}
Basically, what you were doing was telling the frame to paint the first panel, but then you told it to paint over it with panel 2, and never said to stop painting panel 1. Using a layout manager handles all of this behind the scenes and helps for other stuff in the long run.
JScrollPane works perfectly when I give it a JPanel and then add the JScrollPane directly on to a JFrame with frame.getContentPane.add(). However, it doesn't work when I add the JScrollPane to a JPanel and then add the JPanel to the JFrame. I need to use the second method because I'm going to add multiple things inside the JPanel and JFrame and I need to keep it organized. Here is my code.
import java.awt.*;
import javax.swing.*;
public class Main {
/**
* #param inpanel asks if the JScrollPane should
* be inside of a JPanel (so other things can also be added)
*/
public static void testScroll(boolean inpanel) {
JFrame f = new JFrame();
f.setLayout(new BorderLayout());
f.setResizable(true);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.red));
//panel.setLayout(new BoxLayout(panel, 1));
panel.setLayout(new GridLayout(0,1));
for (int i = 0; i < 100; i++) {
JLabel l = new JLabel("hey"+i,SwingConstants.CENTER);
l.setBorder(BorderFactory.createLineBorder(Color.green));
l.setPreferredSize(new Dimension(200,200));
panel.add(l);
}
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.setBorder(BorderFactory.createLineBorder(Color.blue));
//**********THIS DOES NOT WORK HOW I WANT IT TO************
if(inpanel){
JPanel holder = new JPanel();
holder.add(scrollPane);
f.getContentPane().add(holder);
}
//************THIS DOES WORK HOW I WANT IT TO****************
else{
f.getContentPane().add(scrollPane);
}
f.pack();
f.setSize(500, 500);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
JScrollBar bar = scrollPane.getVerticalScrollBar();
bar.setValue(bar.getMaximum());
bar.setUnitIncrement(50);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
testScroll(false); //OR TRUE
}
};
SwingUtilities.invokeLater(r);
}
}
In the main method, if I pass false, it works like I mentioned before, but when I pass true it shows up without a scroll bar.
Picture when passing false
Picture when passing true
I need a way to add the JScrollPane to a JPanel and still have it work.
Thanks in advance!
Your problem is the holder JPanel's layout. By default it is FlowLayout which will not re-size its child components when need be. Make it a BorderLayout instead, and your scrollpane will resize when needed. If you need something more complex, check out the layout manager tutorials.
I have created a JScrollPane with a JPanel inside it and I want to add JPanel/JLabel/Other objects after pressing the button. For example after three button presses I want to get something like this:
I tried myJPane.add(testLabel) with testlabel.setBounds()but no result, I don't want to use GridLayout because of the unchangeable sizes. I would like it if the added objects had different sizes - adjusted to the text content.
What should I use for it and how?
Thanks in advance.
Best regards,
Tom.
Here is a JPanel inside a JScrollPane that adds JLabels to it when pressing the button:
public class Example extends JFrame {
public Example() {
JPanel boxPanel = new JPanel();
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.PAGE_AXIS));
JTextField textField = new JTextField(20);
JButton sendButton = new JButton("Send");
sendButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JLabel label = new JLabel(textField.getText());
label.setOpaque(true);
label.setBackground(Color.RED);
boxPanel.add(label);
boxPanel.add(Box.createRigidArea(new Dimension(0,5)));
textField.setText("");
boxPanel.revalidate();
// pack();
}
});
JPanel southPanel = new JPanel();
southPanel.add(textField);
southPanel.add(sendButton);
add(new JScrollPane(boxPanel));
add(southPanel, BorderLayout.PAGE_END);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new Example();
}
}
The BoxLayout will stack the labels on top of each other.
Notes:
setOpaque(true) must be called on label for it to honor the background color.
Box.createRigidArea is used for creating gaps. Use it as you wish.
The call to revalidate() is imperative in order to display the new components immediately.
Calling pack() (on the JFrame) will resize it each time to fit all the new components. I just put it there for demonstration since the initial frame size is too small to display the initial components added.
I will use a BoxLayout, creating a vertical box, and after each button action, it will add a new JPanel to this box.
Example:
public class YourChat extends JPanel{
private JScrollPane sc;
private Box bv;
public YourChat(){
bv = Box.createVerticalBox();
sc = new JScrollPane(bv);
//your functions (panel creation, addition of listeners, etc)
add(sc);
}
//panel customized to have red backgroud
private class MyPanel extends JPanel(){
private JLabel label=new JLabel();
public MyPanel(String text){
setBackgroundColor(Color.red);
add(label);
}
}
//inside the action listener
public void actionPerformed(ActionEvent e) {
sc.add(new MyPanel(textField.getText()));
textField.setText("");
}
}
For extra information check on:
[https://docs.oracle.com/javase/tutorial/uiswing/layout/box.html]
See also the example
[http://www.java2s.com/Code/Java/Swing-JFC/VerticalandhorizontalBoxLayouts.htm]
Use BoxLayout if you want only add vertically, otherwise you can use FlowLayout for both directions.
I have a JLayeredPane within a JFrame, with two JPanels in it. It all works fine, except since the JLayeredPane requires me to have a null Layout, it doesn't resize the two JPanels correctly when the JFrame resizes.
public class CreatorUI extends JFrame{
private static final long serialVersionUID = 1L;
private JPanel moveablePane = new JPanel();
private Container backgroundPane;
private JLayeredPane layers = new JLayeredPane();
private ComponentMover componentMover = new ComponentMover();
private ComponentResizer componentResizer = new ComponentResizer();
public CreatorUI(){
backgroundPane = new BackgroundUI().initComponents();
layers.add(backgroundPane, 1);
moveablePane.setLayout(null);
componentMover.setAutoLayout(true);
moveablePane.setOpaque(false);
layers.add(moveablePane, 2);
moveablePane.setSize(backgroundPane.getSize());
add(layers, BorderLayout.CENTER);
layers.setPreferredSize(backgroundPane.getPreferredSize());
pack();
}
public void addWindow(WindowComponent window){
this.componentMover.registerComponent(window);
this.componentResizer.registerComponent(window);
this.componentMover.setDragInsets(this.componentResizer.getDragInsets());
this.moveablePane.add(window);
}
public static void main(String[] args){
CreatorUI frame = new CreatorUI();
frame.addWindow(new MapComponent());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
This is what it looks like normally (looks perfect for the normal size):
This is what it looks like once I've tried to resize the JFrame:
I figured out that I can make the top layer resizable if I change my code to this:
public CreatorUI(){
componentMover.setAutoLayout(true);
backgroundPane = new BackgroundUI().initComponents();
moveablePane.setLayout(null);
moveablePane.setOpaque(false);
moveablePane.setSize(backgroundPane.getSize());
layers.setLayout(new BorderLayout());
layers.add(backgroundPane, BorderLayout.CENTER);
layers.setLayer(backgroundPane, new Integer(1));
layers.add(moveablePane, BorderLayout.CENTER);
layers.setLayer(moveablePane, new Integer(2));
add(layers, BorderLayout.CENTER);
layers.setPreferredSize(backgroundPane.getPreferredSize());
pack();
}
This is what it look like now (only the pink layer resizes):
I have the same problem with a more complex container with a shadow Layer above. I was searching for an answer but only found this (which was not helping me). What i did was this:
layers.addComponentListener(new ComponentAdapter() {
#Override public void componentResized(ComponentEvent e) {
Dimension size = layers.getSize(); // get size
backgroundPane.setSize(size); // push size through
moveablePane.setSize(size); // push size trhough
// otherChildOfLayers.setSize(size); // push size trhough
layers.revalidate(); // revalidate to see updates
layers.repaint(); // "Always invoke repaint after revalidate"
}
});
some more information about repaint/revalidate
Answering my own question
There is a very hack-y fix, which (for your purposes) is to keep the overlay at max size at all times.
public CreatorUI(){
componentMover.setAutoLayout(true);
backgroundPane = new BackgroundUI().initComponents();
moveablePane.setLayout(null);
moveablePane.setOpaque(false);
moveablePane.setSize(Toolkit.getDefaultToolkit().getScreenSize());
layers.setLayout(new BorderLayout());
layers.setLayer(moveablePane, JLayeredPane.DRAG_LAYER);
layers.add(moveablePane, BorderLayout.CENTER);
layers.setLayer(backgroundPane, JLayeredPane.DEFAULT_LAYER);
layers.add(backgroundPane, BorderLayout.CENTER);
add(layers, BorderLayout.CENTER);
layers.setPreferredSize(backgroundPane.getPreferredSize());
pack();
}
Ok so im working on this game in java called 8 bit chimera. Im working on the main menu right now but when im using the card layout the window wont open for some reason. Here is some code.
import javax.swing.*;
import java.awt.*;
public class MainScreen extends JFrame{
String Title = "MainMenu";
MainMenuComp MMC = new MainMenuComp();
BreedingGround BGR = new BreedingGround();
public MainScreen() {
setTitle("8-bit Chimera "+Title);
setSize(800,600);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
add(MMC);
add(BGR);
}
public static void main(String[] args){
new MainScreen();
}
}
that was the Main window
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MainMenuComp extends JPanel implements ActionListener{
BreedingGround BGR = new BreedingGround();
ImageData ID = new ImageData();
Image TitleBg;
Image Title;
CardLayout CL;
JButton Play;
public MainMenuComp() {
setLayout(new GridBagLayout());
GridBagConstraints GBC = new GridBagConstraints();
ImageIcon TitleData = new ImageIcon(ID.TitleSource);
ImageIcon TitleBackGroundData = new ImageIcon(ID.TitleBackGroundSource);
ImageIcon PlayData = new ImageIcon(ID.PlaySource);
TitleBg = TitleBackGroundData.getImage();
Title = TitleData.getImage();
Play = new JButton();
Play.setIcon(PlayData);
add(Play,GBC);
add(BGR,"Breed");
}
public void actionPerformed(ActionEvent AE){
if(AE.getSource() == Play){
CL.show(this, "Breed");
}
}
public void paintComponent(Graphics g){
g.drawImage(TitleBg,0,0,800,600,this);
g.drawImage(Title,250,80,280,140,this);
}
}
this was the card layout
import javax.swing.*;
import java.awt.*;
public class BreedingGround extends JPanel{
ImageData ID = new ImageData();
Image Swamp;
CardLayout CL;
public BreedingGround(){
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
ImageIcon SwampData = new ImageIcon(ID.SwampSource);
Swamp = SwampData.getImage();
}
public void paintComponent(Graphics g){
g.drawImage(Swamp,0,0,800,600,this);
}
}
and that was what i wanted the CardLayout to open. The problem is that when i try to run it the window wont run and this keeps showing in the compiler.
--------------------Configuration: 8-bit Chimera - JDK version 1.6.0_26 - --------------------
Exception in thread "main" java.lang.IllegalArgumentException: cannot add to layout: constraints must be a GridBagConstraint
at java.awt.GridBagLayout.addLayoutComponent(GridBagLayout.java:685)
at java.awt.Container.addImpl(Container.java:1074)
at java.awt.Container.add(Container.java:927)
at MainMenuComp.<init>(MainMenuComp.java:26)
at MainScreen.<init>(MainScreen.java:7)
at MainScreen.main(MainScreen.java:23)
Process completed.
All i really want to know is what this is saying.
I don't see where you ever set the layout of a container to be CardLayout, and if you don't set the layout to this, you can't magically use it. If you haven't yet gone through the CardLayout tutorial, consider doing so as it's all explained there.
Edit 1
Comment from Alexander Kim:
when i added the cardbagLayout it wont load the image and the button size filled the whole screen. I also took away the grids
You need to nest your JPanels in order to nest layouts. Use a single JPanel as the CardLayout container whose single function it is is to display other JPanels (the "cards"). These other JPanels will use whatever layouts that are necessary to properly display the components that they hold such as your JButton or "grids" (whatever they are). And even these JPanels may hold other JPanels that use other layouts.
Again, please read the layout tutorials as it's all described well there. You will not regret doing this.
Edit 2
Here's a very simple example that uses a CardLayout. The component displayed by the CardLayout using JPanel (called the cardContainer) is changed depending on which item is selected in a combobox.
Here's the CardLayout and the JPanel that uses it:
private CardLayout cardLayout = new CardLayout();
// *** JPanel to hold the "cards" and to use the CardLayout:
private JPanel cardContainer = new JPanel(cardLayout);
And here's how I add a component to the cardlayout-using JPanel:
JPanel redPanel = new JPanel();
//...
String red = "Red Panel";
cardContainer.add(redPanel, red); // add the JPanel to the container with the String
I also add the String to a JComboBox so I can use this combo box later to tell the CardLayout to display this JPanel (redPanel) if the user selects the item "Red" in this same JComboBox:
cardCombo.addItem(red); // also add the String to the JComboBox
Here's the ActionListener in the JComboBox that lets me change the item displayed in the cardlayout using JPanel:
cardCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String item = cardCombo.getSelectedItem().toString();
// *** if combo box changes it tells the CardLayout to
// *** swap views based on the item selected in the combo box:
cardLayout.show(cardContainer, item);
}
});
And here's the whole shebang:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SimpleCardLayoutDemo {
private CardLayout cardLayout = new CardLayout();
// *** JPanel to hold the "cards" and to use the CardLayout:
private JPanel cardContainer = new JPanel(cardLayout);
private JComboBox cardCombo = new JComboBox();
private JPanel comboPanel = new JPanel();;
public SimpleCardLayoutDemo() {
JPanel greenPanel = new JPanel(new BorderLayout());
greenPanel.setBackground(Color.green);
greenPanel.add(new JScrollPane(new JTextArea(10, 25)), BorderLayout.CENTER);
greenPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
greenPanel.add(new JButton("Bottom Button"), BorderLayout.PAGE_END);
String green = "Green Panel";
cardContainer.add(greenPanel, green);
cardCombo.addItem(green);
JPanel redPanel = new JPanel();
redPanel.setBackground(Color.red);
redPanel.add(new JButton("Foo"));
redPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
String red = "Red Panel";
cardContainer.add(redPanel, red);
cardCombo.addItem(red);
JPanel bluePanel = new JPanel();
bluePanel.setBackground(Color.blue);
JLabel label = new JLabel("Blue Panel", SwingConstants.CENTER);
label.setForeground(Color.white);
label.setFont(label.getFont().deriveFont(Font.BOLD, 32f));
bluePanel.add(label);
String blue = "Blue Panel";
cardContainer.add(bluePanel, blue);
cardCombo.addItem(blue);
comboPanel.add(cardCombo);
cardCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String item = cardCombo.getSelectedItem().toString();
// *** if combo box changes it tells the CardLayout to
// *** swap views based on the item selected in the combo box:
cardLayout.show(cardContainer, item);
}
});
}
public JPanel getCardContainerPanel() {
return cardContainer;
}
public Component getComboPanel() {
return comboPanel ;
}
private static void createAndShowUI() {
SimpleCardLayoutDemo simplecardDemo = new SimpleCardLayoutDemo();
JFrame frame = new JFrame("Simple CardLayout Demo");
frame.getContentPane().add(simplecardDemo.getCardContainerPanel(), BorderLayout.CENTER);
frame.getContentPane().add(simplecardDemo.getComboPanel(), BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// to run Swing in a thread-safe way
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Your problem is with add(BGR,"Breed");. The layout of MainMenuComp is a GridBagLayout, so the constraint must be a GridBagConstraint, not a String (you have "Breed" as the constraint).