Updating JTabbedPane when new tab is added - java

I'm working on a project, and have run into a little bit of a logic error, hopefully one of you can clear this up.
I'm building an application that will display a SQL database (among other things). Currently, the way I have things set up, I have a JTabbedPane inside a Container (BorderLayout.CENTER) not that this is really pertinent information.
Anywho, I would like to add a tab once the user has connected to a database (and eventually selected which 'table' to see. For now however, there is only one table to be displayed.
So, when the user hits 'Connect', ideally the connection will be successful, at which point in time a JTable is populated with the database information.
Once this table is initialized and ready to go, add it to a new JPanel, and add that panel to the JTabbedPane.
This is where the error comes in. I 'believe' my logic thus far is correct, and I don't get any compiler/runtime errors, the new tab just isn't shown (and if I click where it should be) nothing happens.
Below is some of my code, if anything needs clarified please don't hesitate to ask!
This is the Table_Builder Class code (I will clean it up once it is working properly!)
public class Table_Builder extends Framework
{
private DefaultTableModel updated_table_model;
private JTable updated_table;
private JScrollPane table;
public Table_Builder()
{
// no implemention needed
}
public Table_Builder(Vector rows, Vector columns)
{
updated_table_model = new DefaultTableModel(rows, columns);
updated_table = new JTable(updated_table_model);
updated_table.setCellSelectionEnabled(true);
updated_table.setFillsViewportHeight(false);
table = new JScrollPane(updated_table);
JPanel tab2 = new JPanel();
tab2.add(table);
tab2.setVisible(true);
center.add("Table Viewer", tab2);
// I'm thinking some sort of listener needs to be active, so it knows I'm adding a new
// tab, but I'm not sure how this actually works.
center.addPropertyChangeListener("foregroud", null);
center.repaint();
// center has already been added to container so i don't think that needs to be done again?
}
Framework
protected void center_panel()
{
JPanel tab1 = new JPanel();
tab1.add(//emitted);
center.setPreferredSize(new Dimension(1340, 950));
center.setBackground(new Color(90, 90, 90));
center.addTab("Tab1", tab1);
container.add(center, BorderLayout.CENTER);
}
Best Regards,
Mike
UPDATE:
Framework has these variables I am using to build the 'Frame'
Framework is a borderlayout (east, west, north, south, center)
protected JTabbedPane center // this is the center panel
protected Container container // this will house all panels to be added
As seen above, I am currently adding tabs by
1.) creating a new JPanel
2.) adding (whatever needs to be displayed) to the jpanel
3.) adding that jpanel to the JTabbedPane
this is done by
center.addTab("Tab name here", panel to be added);
The javadoc for this says
center.addTab("String title", Component component);
This works as intended, the problem I am encountering, is that this is done prior to server connection. After the user connects to the server, I would like to add a new tab, which is being done from Table_Builder, which inherits from Framework (which is why center and container are protected and not private).

Your code for adding a tab in the constructor is the following:
JPanel tab2 = new JPanel();
tab2.add(table);
tab2.setVisible(true);
center.add("Table Viewer", tab2);
// I'm thinking some sort of listener needs to be active, so it knows I'm adding a new
// tab, but I'm not sure how this actually works.
center.addPropertyChangeListener("foregroud", null);
center.repaint();
There are 2 errors and a lot of unnecessary lines. The errors are:
center.add("Table Viewer", tab2); is using the add function of the Container class. When you wanted to use center.addTab("Table Viewer", tab2);.
Just to clear up what #peeskillet was pointing out, there is not a "foregroud" property, nor a "forground" (as per your comment), but a "foreground" property.
Now what you need to do is just the following:
JPanel tab2 = buildTableViewerTab();
center.addTab("Table Viewer", tab2);
Where buildTableViewerTab() (returning a JPanel) is the code necessary to create the JPanel that you desire. Just create the component and add it to the tabbedPane properly.
To show how this code works here is a simple executable application demonstrating this functionality. Again, what #peeskillet was asking you in his second comment is to do this same example but in your own way and with your code demonstrating the errors you were encountering. Although doing this you probably would have found them.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class AddTabsExample
{
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new AddTabsExample();
}
});
}
public AddTabsExample()
{
JFrame frame = new JFrame("Tab adder frame");
final JTabbedPane tabbedPane = new JTabbedPane();
frame.add(tabbedPane);
JButton addButton = new JButton("Add tab");
addButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
JPanel newTabComponent = new JPanel();
int tabCount = tabbedPane.getTabCount();
newTabComponent.add(new JLabel("I'm tab " + tabCount));
tabbedPane.addTab("Tab " +tabCount, newTabComponent);
}
});
frame.add(addButton, BorderLayout.SOUTH);
addButton.doClick(); //add the first tab
frame.setSize(800, 300);//frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
Execution result:

call revalidate() on your center, then repaint.

Related

Dynamic changing of layouts in swing

I am almost certain this question was asked before here: Java Swing: How to change GUI dynamically , but I seem to just have some fundamental misunderstanding in how it works.
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
public class JTest extends JFrame
{
public static void main(String[] args)
{
JTest t = new JTest();
}
Container pane;
public JTest()
{
setSize(500,500);
setTitle("JTest");
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane = getContentPane();
pane.setLayout(new GridLayout(1,2));
JButton old = new JButton("old");
old.addActionListener(new OldButton());
pane.add(old);
JScrollPane scroll = new JScrollPane(new JTextArea(50,20));
pane.add(scroll);
setVisible(true);
}
private class OldButton implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
pane.setLayout(new GridLayout(1,2));
JButton old = new JButton("new");
old.addActionListener(new NewButton());
pane.add(old);
JScrollPane scroll = new JScrollPane(new JTextArea(50,20));
pane.add(scroll);
pane.validate();
}
}
private class NewButton implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
pane.setLayout(new GridLayout(1,2));
JButton old = new JButton("old");
old.addActionListener(new OldButton());
pane.add(old);
JScrollPane scroll = new JScrollPane(new JTextArea(50,20));
pane.add(scroll);
pane.validate();
}
}
}
This code should replace the preexisting layout with a new one anytime the button in the corner is pressed, but instead, it just adds the new layout to the frame. Can someone tell me what I'm doing wrong?
EDIT:
Adding some information. A picture for reference:
I'm making a set of components like this inside the scroll pane. whenever I press the "Make new field" button, I want it to add a "field" (the name of the field followed by a textarea or some such) to the set in that scrollpane. This means changing the layout of the area inside the scrollpane to include the new field.
OK -- so it looks like what you want to do (and please correct me if I'm wrong) is to add a new component to a JPanel that is displayed within a JScrollPane. If so, then you do not want to change or swap layouts, and you certainly don't want to keep adding new JScrollPanes. Instead consider doing:
Create one JScrollPane and add to your GUI. Don't re-add this as you'll only need one.
add a JPanel to the JScrollPane's viewport that uses a layout that allows multiple components to be easily added to it. Perhaps a GridLayout or a BoxLayout, depending on what you need.
Also consider not adding the above JPanel directly to the viewport but rather adding it to another JPanel, one that uses BorderLayout, adding the first JPanel to the BorderLayout-using JPanel's BorderLayout.PAGE_START position, and then add this to the JScrollPane's viewport. This way the first JPanel won't stretch to fill the viewport initially.
Then in your button's ActionListener, add your components to the first JPanel by calling .add(...) on it, and then call revalidate() and repaint() on that first JPanel to layout the newly added components and repaint the JPanel and its contents.
Ok, so it turns out this wasn't a layout problem at all. I had failed to realize that setting a new layout doesn't cause the previous layout's components to disappear, you have to remove them before adding the new components. That's why I was getting duplication.
Thanks for pointing me in the right direction, though.

Hide a swing component without revalidating the layout?

If I set up a JFrame with some components and a layout manager, which initially looks perfectly fine, and then later due to some condition (say, clicking a button) I hide one of those components - the layout manager shuffles all the components around again.
See example code - initially 3 buttons appear. When you click the Hide button, the Hide button is hidden - but the two outer buttons then squash together. When you click the show button, they move apart again to make space. How can I stop that from happening, so that after I call pack (), components stay where they are no matter if they later become hidden?
In my real code I'm doing this with GridBagLayout, but used FlowLayout in the example below because its simpler and less code, and shows exactly the same behaviour.
I can only think of nasty ways of doing this, like using .setEnabled (false) instead of .setVisible (false), and then overriding the component's paintComponent () method to not draw the component when it is disabled.
It seems the exact opposite problem to here - Hide a button from Layout in Java Swing - where is complaining that hidden buttons do still take up space :) But there's no sample code there to show it working in that way.
Many thanks for any suggestions :)
Example:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RevalidateWhenSetChildInvisibleExample
{
private JButton button1;
private JButton button2;
private JButton button3;
public void run ()
{
// Set up action
Action hideButtonAction = new AbstractAction ()
{
#Override
public void actionPerformed (ActionEvent e)
{
button2.setVisible (false);
}
};
hideButtonAction.putValue (Action.NAME, "Hide");
Action showButtonAction = new AbstractAction ()
{
#Override
public void actionPerformed (ActionEvent e)
{
button2.setVisible (true);
}
};
showButtonAction.putValue (Action.NAME, "Show");
// Set up buttons
button1 = new JButton ("Dummy");
button2 = new JButton (hideButtonAction);
button3 = new JButton (showButtonAction);
// Set up content pane
JPanel contentPane = new JPanel ();
contentPane.setLayout (new FlowLayout ());
contentPane.add (button1);
contentPane.add (button2);
contentPane.add (button3);
// Set up frame
JFrame frame = new JFrame ();
frame.setContentPane (contentPane);
frame.pack ();
frame.setVisible (true);
}
public static void main (String args [])
{
SwingUtilities.invokeLater (new Runnable ()
{
public void run ()
{
new RevalidateWhenSetChildInvisibleExample ().run ();
}
});
}
}
You could use a CardLayout and then swap the button with an empty JPanel.
Read the section from the Swing tutorial on How to Use CardLayout for more information and examples.
The problem is the layout manager, which is not really a problem here because it is just doing its job. You could set the layout to null and then set the bounds for every button; this way they will NEVER move unless you change their position.
panel.setLayout(null);
button1.setBounds(10,10,50,20);
button2.setBounds(70,10,50,20);
button3.setBounds(xPos,yPos,width,height);
Another way is to use the GridLayout:
contentPane.setLayout(new GridLayout());
I tested it, and it worked fine, since the component did not get removed it stays the same.
Also, you should add the following to your code:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
This makes the program exits when the JFrame is closed; without it the program still runs at the background.

GUI - How do I switch between panels or frames while saving user input

So I have a layout made with buttons,textfields, and labels. A user is supposed to put input into the textfields. When he hits a button, I want it so that the input is cleared and a new "page" is shown with the layout i have made. The user can input as much information into new "pages" as he wants until he hits an "finished" button. In short, I want to switch between panels or frames (i dont know which, probably panels??). Now, I was thinking of using card layout to do this but since i'm reading user input it wouldn't really make sense since cardlayout is made based on a predetermined amount of panels and what will be in the panels. Since I won't know when the user is "finished", I won't know how many panels to use.
Anyways, I'm just a beginner with GUI so any help would be great!
Now, I was thinking of using card layout to do this but since i'm
reading user input it wouldn't really make sense since cardlayout is
made based on a predetermined amount of panels and what will be in the
panels. Since I won't know when the user is "finished", I won't know
how many panels to use.
You can dinamically add components to CardLayout on next button's click. If all the pages have the same structure you can have a class for those pages and add a new one every time next button is pressed. When finish button is pressed do something with all those pages iterating over the panel (with CardLayout) components. Take a look to Container.getComponents() method. You don't even need to keep any kind of array nor list because the container already do so.
Example
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Demo {
private void createAndShowGUI() {
final JPanel cardPanel = new JPanel(new CardLayout());
cardPanel.add(new Page(), "1");
final JButton nextButton = new JButton("Next");
nextButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardPanel.add(new Page(), String.valueOf(cardPanel.getComponentCount() + 1));
CardLayout layout = (CardLayout)cardPanel.getLayout();
layout.next(cardPanel);
}
});
final JButton finishButton = new JButton("Finish");
finishButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
nextButton.setEnabled(false);
for(Component comp : cardPanel.getComponents()) {
if(comp instanceof Page) {
Page page = (Page)comp;
page.printData();
}
}
}
});
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(nextButton);
buttonsPanel.add(finishButton);
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(cardPanel, BorderLayout.CENTER);
frame.getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class Page extends JPanel {
final private JTextField data;
public Page() {
super();
add(new JLabel("Please add some info:"));
data = new JTextField(20);
add(data);
}
public void printData() {
System.out.println(data.getText());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().createAndShowGUI();
}
});
}
}
As far as I understand from your description you do not need multiple panels. I am assuming that you have some sort of object hierarchy for your model layer. So, let's say you use those input values to create AnObject objects.
You can create an ArrayList<AnObject> in your top class. And as user inputs and clicks done you just create one more AnObject with given input and add it to the ArrayList you defined in top class.
BTW, you can also define ArrayList whereever it is reachable. But you must think carefully, to keep your data persistent. If the object of the class that you defined ArrayList is "gone", your data is also "gone". I think this should be clear enough.
The next step is just trivially clearing out those input fields.
This is the most straightforward way, it may not be the smartest way to do that depending on your use case. But it would give you an idea for what to look and learn for.

Setting JPanel layout

(Say) I've created a JPanel with three buttons. I want to place the buttons as follows (I've done this using netbeans GUI editor. But I need to write the whole GUI manually).
Can some one show me a way to achieve this.
(In words, I need to place some buttons right aligned, some other left aligned.)
I guess you want the Configure button to be as far to the left as possible, and the ok and cancel grouped together to the right. If so, I would suggest using a BorderLayout and place the Configure button in WEST, and a flow-layout for Ok, Cancel and place that panel in the EAST.
Another option would be to use GridBagLayout and make use of the GridBagConstrant.anchor attribute.
Since you're taking the time to avoid the NetBeans GUI editor, here's a nice example for you :-)
Code below:
import java.awt.BorderLayout;
import javax.swing.*;
public class FrameTestBase {
public static void main(String args[]) {
// Will be left-aligned.
JPanel configurePanel = new JPanel();
configurePanel.add(new JButton("Configure"));
// Will be right-aligned.
JPanel okCancelPanel = new JPanel();
okCancelPanel.add(new JButton("Ok"));
okCancelPanel.add(new JButton("Cancel"));
// The full panel.
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.add(configurePanel, BorderLayout.WEST);
buttonPanel.add(okCancelPanel, BorderLayout.EAST);
// Show it.
JFrame t = new JFrame("Button Layout Demo");
t.setContentPane(buttonPanel);
t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
t.setSize(400, 65);
t.setVisible(true);
}
}

What's wrong with JSplitPanel (or JTabbedPane)?

I have two panels that i wish to display to the user. I decided to add them to a JTabbedPane. I also want to allow the user to have a side by side view of them both at the same time. So I added the two panels to my JTabbedPane and then i created a JSplitPanel as such :
tabs.addTab("Align Image Points", imageControlPanel);
tabs.addTab("Align Map Points", mapControlPanel);
JSplitPane splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT, true, imageControlPanel,
mapControlPanel);
tabs.addTab("Side by side view", splitPane);
The resulting JTabbedPane only has one tab! When i remove the JSplitPane everything works ok. Two tabs. I then tried simplifying the problem to post here and i came up with this :
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
JButton b1 = new JButton("First");
JButton b2 = new JButton("Second");
JSplitPane s = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,b1,b2);
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("First", b1);
tabs.addTab("Second", b2);
tabs.addTab("Both", s);
f.getContentPane().add(tabs);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
This gave me another problem! It displays 3 tabs but the third tab's split pane displays no buttons!
So my question is What is wrong with JSplitPanel? You can't have it display objects that are already displayed in another tab? It makes no sense. Please help me.
(Note: i don't want to duplicate the components that i am about to display as i want them to be the same reference)
Swing UIs are hierarchical and you can only add a component to the hierarchy once. If you add a component to more than one container you'll get unpredictable results. You are correct to not want to duplicate the components, but you'll need a listener on the JTabbedPane to add and remove each component from the tab or the JSplitView as the selection of the tabs changes.
tabs.addChangeListener( new ChangeListener() {
public void stateChanged(ChangeEvent e) {
// Reorganise the display based on the current tab selection.
}
}
I had the same problem that you had. what I had resolved for this issue, I made the each GUI as MVC pattern(Model-view-controller) that controller knows how to iterative with gui components.
I created a new instance of GUIs(View) on each Tab;however, I injected the same instance of controller for that GUI as constructor parameter since the controller knows how to handle GUI flow and behaviors.
for example,
GUIView1Controller controller1 = new GUIView1Controller();
GUIView2Controller controller2 = new GUIView2Controller();
// Add new instance GUI ; however , use the same instance of controller
JSplitPane s = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,
new GUIView1(controller1), new GUIView2(controller2));
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("First", new GUIView1(controller1));
tabs.addTab("Second", new GUIView2(controller2));
tabs.addTab("Both", s );
GUIView1 and GUIView2 will register all GUI listeners to the controller, so the controller will be notified and take an action for the listeners. whatever the GUIView1 on "First" tab is changed, the GUIView1 on "Both" tab also is updated as the same behaviors of the GUIView1 on "First" tab.
The drawback was you have to create a new instance of the GUIView on the tab and JSplitPane; however, the controller can control and share all gui events and behaviors.
I hope it helps.
Tiger.

Categories