What's wrong with JSplitPanel (or JTabbedPane)? - java

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.

Related

How to implement pages in Java Swing app.?

I have some experience in Java creating Apps and would like to learn more, and so have decided to create an application that will have different pages. For example the initial frame will show a menu of buttons that will lead to different frames, showing different components and layouts.
I'm not too sure the best practice of implementing pages. I think I could store the JFrame windows in a list, then use a button handler class to maybe change the visibility of the different frames, only allowing the relevant frame to be visible when the user clicks on a button. I think this method could work, but is there a more efficient/practical way of doing this?
I am aware of CardLayout, however for this program I am trying to learn MigLayout; so that won't be possible (as far as I'm aware). I hope this question is not too vague, I'd just like to know the best practice when it comes to creating applications in Java with different pages.
Can use Tabbed Panes, it is the best for storing pages.
https://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html
Also I noticed that you need to consider top level containers properly, because you don't need to create every time a JFrame for each Page, at least if it was necessary(For example: an editor, create a new window so you need to create a new JFrame, in your case I don't think so) so please consider the link below.
https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
JInternalFrame is a part of Java Swing . JInternalFrame is a container that provides many features of a frame which includes displaying title, opening, closing, resizing, support for menu bar, etc. Internal frames with components example
Code to create multiple internal frames:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
class solution extends JFrame {
// frame
static JFrame f;
// label to diaplay text
static JLabel l, l1;
// main class
public static void main(String[] args) {
// create a new frame
f = new JFrame("frame");
// set layout of frame
f.setLayout(new FlowLayout());
// create a internal frame
JInternalFrame in = new JInternalFrame("frame 1", true, true, true, true);
// create a internal frame
JInternalFrame in1 = new JInternalFrame("frame 2", true, true, true, true);
// create a Button
JButton b = new JButton("button");
JButton b1 = new JButton("button1");
// create a label to display text
l = new JLabel("This is a JInternal Frame no 1 ");
l1 = new JLabel("This is a JInternal Frame no 2 ");
// create a panel
JPanel p = new JPanel();
JPanel p1 = new JPanel();
// add label and button to panel
p.add(l);
p.add(b);
p1.add(l1);
p1.add(b1);
// set visibility internal frame
in.setVisible(true);
in1.setVisible(true);
// add panel to internal frame
in.add(p);
in1.add(p1);
// add internal frame to frame
f.add(in);
f.add(in1);
// set the size of frame
f.setSize(300, 300);
f.show();
}
}

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.

How to extend JTabbedPane to always have an "Add Tab" tab?

I am trying to create a JTabbedPane so that the final "tab" is actually a button that will open up a dialog box to add something to the tabbed pane. I tried looking around the source for JTabbedPane, but I can't find where the tab objects (the row of clickable "buttons" that when clicked change the currently visible component) actually are. There is a private list of Page objects (so they can't be accessed by child classes anyway) but they only contain the info about the tab and aren't the tab object themselves. My goal is to be able to add a button that comes after the tab list (horizontally).
I also tried using JTabbedPane.setTabComponentAt() to change the string of the final tab to a JButton with a component of null, but that still adds the tab component. If you click slightly to the right/left of the button in the tab, a blank tab will show because there's always padding around the component. Perhaps there's a way to get rid of this? But I suppose in some Look and Feels that have tabs like this: /---\ instead of this: |---| you could still click in the tab but not click the button.
Does anyone know how I can get what I'm looking for without writing my own version of JTabbedPane?
Thanks!
My goal is to be able to add a button that comes after the tab list (horizontally).
The proper solution is to write a custom UI, but that can be complicated and I'm not sure what code to change.
As a simple hack you can us a panel with an OverlayLayout to display the button at the top/right of the tabbed pane:
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class TabbedPaneWithComponent
{
private static void createAndShowUI()
{
JPanel panel = new JPanel();
panel.setLayout( new OverlayLayout(panel) );
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.add("1", new JTextField("one"));
tabbedPane.add("2", new JTextField("two"));
tabbedPane.setAlignmentX(1.0f);
tabbedPane.setAlignmentY(0.0f);
JCheckBox checkBox = new JCheckBox("Check Me");
checkBox.setOpaque( false );
checkBox.setAlignmentX(1.0f);
checkBox.setAlignmentY(0.0f);
panel.add( checkBox );
panel.add(tabbedPane);
JFrame frame = new JFrame("TabbedPane With Component");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setLocationByPlatform( true );
frame.setSize(400, 100);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Works pretty good except when the width of the tabbed pane becomes too small and the button overlaps the tabs.

Updating JTabbedPane when new tab is added

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.

GUI Disappearing when I add JComboBox

Alright I'm relatively new to programming and it may be just something simple that I'm missing but the other threads related to this topic the poster didn't give adequate information relative to their issue for others to provide quality answers so I will give it a shot.
public BenchUI(JFrame j){
jf = j;
init();
add(mainPanel);
topPanelButtons();
selectedCustomer();
rentalOptions();
clientListBox();
}
At this point i can point out that everything works perfectly until I add the clientListBox() method. (below)
public void clientListBox(){
clientList = new JComboBox(moo);
clientList.setPreferredSize(new Dimension(460,30));
gbc.gridx = 0;
gbc.gridy = 0;
leftSide.add(clientList,gbc);
}
i can comment it out and get my whole GUI back working perfectly but without a JComboBox.
moo is String [] moo = {"Fish","Goat", "Monkey"};
a dummy string just for testing purposes and initialized at the start.
So any idea why my GUI completely disappears when I place in the clientList?
If anything else is necessary I'll be watching this thread and can provide additional information.
As a side note I keep getting warnings for "Raw Types" but it works without specifiying, could I potentially run into trouble by not specifying my JComboBox?
EDIT:
ok I believe I've duplicated whatever the issue is in this code
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;
public class main {
public static void main(String[] args){
JFrame jf = new JFrame();
jf.setExtendedState(JFrame.MAXIMIZED_BOTH);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setResizable(false);
BenchUI bu = new BenchUI(jf);
jf.add(bu);
}
}
public class BenchUI extends JPanel{
JPanel one;
JFrame jf;
JComboBox<String> clientList;
String[] moo = {"Goat", "Fish", "Donkey"};
public BenchUI(JFrame j){
jf = j;
one = new JPanel(new GridBagLayout());
one.setBackground(Color.blue);
one.setPreferredSize(new Dimension(300,300));
clientList = new JComboBox<String>(moo);
one.add(clientList);
add(one);
}
}
with the clientList stuff commented out I get my silly little blue panel and once it is added I lose the blue panel and the combobox doesnt show up as well...betting on this is a facepalm issue at this point >.<
EDIT: to include the main class.
EDIT: took out the comment marks for the JComboBox constructor and implementer
Your posted sort of sscce-like (not a real SSCCE by the way since we can't run it) code doesn't add any such as the JComboBox to the JPanel and adds no components such as the current JPanel to the JFrame.
public class BenchUI extends JPanel{
JPanel one;
JFrame jf;
JComboBox<String> clientList;
String[] moo = {"Goat", "Fish", "Donkey"};
public BenchUI(JFrame j){
jf = j;
one = new JPanel(new GridBagLayout());
one.setBackground(Color.blue);
one.setPreferredSize(new Dimension(300,300));
//clientList = new JComboBox<String>(moo);
//one.add(clientList);
add(one);
}
}
and so it makes sense that none of the components will show up on any JFrame. You will want to read the Swing tutorials on how to add components to other components (or containers) and how to create and show a JFrame. Have a look at How to Use Swing Components.
Edit
Your latest code now does in fact add the BenchUI JPanel to the JFrame, but still you add no components to the BenchUI JPanel, and in fact you don't even construct your JComboBox but only create a JComboBox variable. Again, I strongly urge you to read the Swing tutorials which I've linked to above as well as the general Java tutorials.
Edit 2
Some general advice:
If you want to add a component to a GUI you must first create the component object. You are declaring your clientList JComboBox, but you never create the object.
Then you must add the component object to a container that eventually will be part of the hierarchy leading to a top level window such as a JFrame, JDialog, JApplet and such. You never add a clientList object to the GUI.
You should add your components to the top level window before calling pack() on the top level window -- which tells all the layout managers to lay out all the components they hold.
You should then call setVisible(true). One problem with your code (other than not creating important components and not adding them to the GUI!) is that you're calling setVisible(true) on your JFrame way too early before adding anything to the GUI.
Read the Swing tutorial, but especially the one on using layout managers and on adding components to a top level window.
Edit 3
OK, now you're creating your JComboBox, but you still are adding all components to your JFrame after setting it visible. Please re-check my 3rd and 4th bullets in the bullet list above.

Categories