I'm experimenting with desktop panes so I can use them in my work projects. The problem here is that I want to use an JInternalFrame within a JDesktopPane, in a normal JPanel it shows normally but cannot move it, using the desktop pane doesn't display any component added.
Here is the code of the last try, is simple just for learning how it works:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Internal_FrameShowtst extends JFrame{
Internal_FrameShowtst(){
BorderLayout bl = new BorderLayout();
JDesktopPane p = new JDesktopPane();
JPanel p1 = new JPanel();
JButton b = new JButton("click");
JInternalFrame in = new JInternalFrame("Test");
Internal_Frametst ift = new Internal_Frametst();
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//p1.add(new JLabel("hola"));
//in.add(p1);
//in.setVisible(true);
ift.setVisible(true);
}
});
p1.add(b);
bl.addLayoutComponent(p,BorderLayout.CENTER);
//p.add(in);
p.add(ift);
p.repaint();
setLayout(bl);
add(p);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Internal_FrameShowtst().setVisible(true);
}
});
}
}
custom internal frame class:
import javax.swing.*;
public class Internal_Frametst extends JInternalFrame {
Internal_Frametst(){
JPanel p = new JPanel();
JLabel label = new JLabel("Halo");
setIconifiable(true);
//setIcon(true);
setClosable(true);
p.add(label);
p.setSize(300,300);
add(p);
//setVisible(true);
}
}
I've read and tried the following:
Components inside JDesktopPane not showing
JDesktopPane not displaying components when adding JInternalFrame
I've tried adding the components directly, adding a JPanel, adding the internal frame, trying without it, creating the internal frame in the main class, creating my own internal frame in its own class, using layout managers with both panels (normal and desktop), all with the same result.
Your code creates several components that are never added to the visible UI at all. In the version you have posted, the internal frame is invisible and the button to make it visible is not part of the frame. But there are also problems with the initial sizes of the components.
I strongly recommend to keep the creation of a component, the setting of its initial properties, and the code to add it to a parent component close together in your source code.
Further, consider the points discussed in Prefer composition over inheritance? Your subclasses are entirely unnecessary.
Here is a revised version of your code that will open the internal frame when the button is clicked:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.*;
public class UiExample {
public static void main(String[] args) {
EventQueue.invokeLater(UiExample::initializeUI);
}
static void initializeUI() {
JPanel p1 = new JPanel();
JButton b = new JButton("Show Internal Frame");
p1.add(b);
JInternalFrame ift = initializeInternalFrame();
b.addActionListener(e -> {
ift.pack();
ift.setVisible(true);
});
JDesktopPane p = new JDesktopPane();
p.add(ift);
JFrame mainFrame = new JFrame();
mainFrame.setSize(300, 200);
mainFrame.getContentPane().add(p, BorderLayout.CENTER);
mainFrame.getContentPane().add(p1, BorderLayout.PAGE_START);
mainFrame.setVisible(true);
}
static JInternalFrame initializeInternalFrame() {
JInternalFrame iFrame = new JInternalFrame();
iFrame.setIconifiable(true);
// setIcon(true);
iFrame.setClosable(true);
iFrame.setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE);
JPanel p = new JPanel();
p.add(new JLabel("Hello"));
iFrame.add(p);
return iFrame;
}
}
Note that setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE) is necessary for being able to show the frame again via setVisible(true) after the internal frame has been closed.
Related
I have created my own version of a panel so I can create some dragable tables but at the moment nothing is being added on to the panel I have created
panel class:
import javax.swing.*;
import java.awt.event.*;
import java.awt.Dimension;
import java.awt.Point;
public class Workspace extends JPanel implements MouseListener,MouseMotionListener{
private JTable t;
private DatabaseHandler d;
public Workspace(DatabaseHandler d ){
super();
this.d = d;
setPreferredSize(new Dimension(1000, 1000));
this.setLayout(null);
addMouseListener(this);
addMouseMotionListener(this);
}
public void load(String table){
t = new JTable(d.getTable(table));
//JScrollPane js=new JScrollPane(t);
this.add(t);
}
}
the code which calls it:
public class Display{
private JPanel leftPanel = new JPanel(new BorderLayout());
public JList list;
public JFrame frame;
private DatabaseHandler d = new DatabaseHandler("imdb");
private Workspace w = new Workspace(d);
public Display(){
//create the window
frame = new JFrame("FYP - Database Refactoring");
frame.getContentPane().add(w, BorderLayout.CENTER);
frame.setSize(1000,1000);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
list = new JList(d.getTableNames());
list.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
JList l = (JList)e.getSource();
w.load((String)l.getSelectedValue());
frame.setVisible(true);
}
});
leftPanel.add(list);
JLabel l = new JLabel("workbench");
w.add(l);
frame.getContentPane().add(leftPanel, BorderLayout.LINE_START);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Display();
}
}
Any help would be appricated
Avoid null layouts, and in fact this one is messing you up, since with null layouts, you must fully specify call component sizes and locations. Just don't do it.
Don't re-add JTables in the MouseListener. Rather give your JPanel a JTable, and change its model from the MouseListener. Otherwise you're adding multiple JTables which doesn't make sense.
Put your JTable into a JScrollPane
And best to have your JPanel use BorderLayout and put the JScrollPane into the BorderLayout.CENTER position.
so I can create some dragable tables
If you need the ability to drag a JTable I would suggest that you should be using a JDesktopPane with JInternalFrames. You can easily drag an internal frame around the desktop. Then you just add the JTable\JScrollPane to the internal frame like you would to a normal JFrame.
Read the section from the Swing tutorial on How to Use Internal Frames for more information and working examples.
I'm trying to make a little game that will first show the player a simple login screen where they can enter their name (I will need it later to store their game state info), let them pick a difficulty level etc, and will only show the main game screen once the player has clicked the play button. I'd also like to allow the player to navigate to a (hopefully for them rather large) trophy collection, likewise in what will appear to them to be a new screen.
So far I have a main game window with a grid layout and a game in it that works (Yay for me!). Now I want to add the above functionality.
How do I go about doing this? I don't think I want to go the multiple JFrame route as I only want one icon visible in the taskbar at a time (or would setting their visibility to false effect the icon too?) Do I instead make and destroy layouts or panels or something like that?
What are my options? How can I control what content is being displayed? Especially given my newbie skills?
A simple modal dialog such as a JDialog should work well here. The main GUI which will likely be a JFrame can be invisible when the dialog is called, and then set to visible (assuming that the log-on was successful) once the dialog completes. If the dialog is modal, you'll know exactly when the user has closed the dialog as the code will continue right after the line where you call setVisible(true) on the dialog. Note that the GUI held by a JDialog can be every bit as complex and rich as that held by a JFrame.
Another option is to use one GUI/JFrame but swap views (JPanels) in the main GUI via a CardLayout. This could work quite well and is easy to implement. Check out the CardLayout tutorial for more.
Oh, and welcome to stackoverflow.com!
Here is an example of a Login Dialog as #HovercraftFullOfEels suggested.
Username: stackoverflow Password: stackoverflow
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import javax.swing.*;
public class TestFrame extends JFrame {
private PassWordDialog passDialog;
public TestFrame() {
passDialog = new PassWordDialog(this, true);
passDialog.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new TestFrame();
frame.getContentPane().setBackground(Color.BLACK);
frame.setTitle("Logged In");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
});
}
}
class PassWordDialog extends JDialog {
private final JLabel jlblUsername = new JLabel("Username");
private final JLabel jlblPassword = new JLabel("Password");
private final JTextField jtfUsername = new JTextField(15);
private final JPasswordField jpfPassword = new JPasswordField();
private final JButton jbtOk = new JButton("Login");
private final JButton jbtCancel = new JButton("Cancel");
private final JLabel jlblStatus = new JLabel(" ");
public PassWordDialog() {
this(null, true);
}
public PassWordDialog(final JFrame parent, boolean modal) {
super(parent, modal);
JPanel p3 = new JPanel(new GridLayout(2, 1));
p3.add(jlblUsername);
p3.add(jlblPassword);
JPanel p4 = new JPanel(new GridLayout(2, 1));
p4.add(jtfUsername);
p4.add(jpfPassword);
JPanel p1 = new JPanel();
p1.add(p3);
p1.add(p4);
JPanel p2 = new JPanel();
p2.add(jbtOk);
p2.add(jbtCancel);
JPanel p5 = new JPanel(new BorderLayout());
p5.add(p2, BorderLayout.CENTER);
p5.add(jlblStatus, BorderLayout.NORTH);
jlblStatus.setForeground(Color.RED);
jlblStatus.setHorizontalAlignment(SwingConstants.CENTER);
setLayout(new BorderLayout());
add(p1, BorderLayout.CENTER);
add(p5, BorderLayout.SOUTH);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jbtOk.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (Arrays.equals("stackoverflow".toCharArray(), jpfPassword.getPassword())
&& "stackoverflow".equals(jtfUsername.getText())) {
parent.setVisible(true);
setVisible(false);
} else {
jlblStatus.setText("Invalid username or password");
}
}
});
jbtCancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
parent.dispose();
System.exit(0);
}
});
}
}
I suggest you insert the following code:
JFrame f = new JFrame();
JTextField text = new JTextField(15); //the 15 sets the size of the text field
JPanel p = new JPanel();
JButton b = new JButton("Login");
f.add(p); //so you can add more stuff to the JFrame
f.setSize(250,150);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Insert that when you want to add the stuff in. Next we will add all the stuff to the JPanel:
p.add(text);
p.add(b);
Now we add the ActionListeners to make the JButtons to work:
b.addActionListener(this);
public void actionPerforemed(ActionEvent e)
{
//Get the text of the JTextField
String TEXT = text.getText();
}
Don't forget to import the following if you haven't already:
import java.awt.event*;
import java.awt.*; //Just in case we need it
import java.x.swing.*;
I hope everything i said makes sense, because sometimes i don't (especially when I'm talking coding/Java) All the importing (if you didn't know) goes at the top of your code.
Instead of adding the game directly to JFrame, you can add your content to JPanel (let's call it GamePanel) and add this panel to the frame. Do the same thing for login screen: add all content to JPanel (LoginPanel) and add it to frame. When your game will start, you should do the following:
Add LoginPanel to frame
Get user input and load it's details
Add GamePanel and destroy LoginPanel (since it will be quite fast to re-create new one, so you don't need to keep it memory).
I have a simple task.
There is a frame. There are two panel in that frame. In second panel there is a button. When user click that button first panel must change its content.
Here is a code:
package test;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
class MyJPanel1 extends JPanel {
MyJPanel1() {
this.add(new JButton("MyJPanel1"));
}
}
class MyJPanel2 extends JPanel {
MyJPanel2() {
this.add(new JButton("MyJPanel2"));
}
}
class MyFrame extends JFrame {
JPanel topPanel = null;
MyFrame() {
super("Test");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new GridLayout(0, 1, 20, 20));
topPanel = new MyJPanel1();
this.add(topPanel);
JPanel bottomPanel = new JPanel();
this.add(bottomPanel);
JButton button = new JButton("switch");
button.addMouseListener(new MouseClickListener());
bottomPanel.add(button);
this.pack();
this.setVisible(true);
}
class MouseClickListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
topPanel = new MyJPanel2();
System.out.println("switch");
topPanel.invalidate();
topPanel.validate();
topPanel.repaint();
MyFrame.this.invalidate();
MyFrame.this.validate();
MyFrame.this.repaint();
}
}
}
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MyFrame();
}
});
}
}
But that don't work. After I click on button I see text in console, but first panel remain the same. I read that I must use invalidate() validate() and repaint() methods and I did, but it isn't help.
Any help would be appreciated.
If you want to "switch" panels then you should be using a CardLayout. The CardLayout allows 2 (or more) components to share the same space in a container but only one is ever visible at a time.
Read the section from the Swing tutorial on How to Use CardLayout for more information and working examples.
In your mouseClicked() method you create a new topPanel, but you don't do anything with it. Perhaps you meant to remove the original topPanel from myFrame, create a new topPanel, and then add the new toipPanel to myFrame.
Note that this may not be the best strategy (creating a new topPanel).
I've looked around a while and also played around trying to add multiple panels to a JTabbedPane.
My question is: Is it possible to add the same Jpanel to multiple TabbedPanes. Everything way that I tried, it doesn't seem to work correctly. This is how it it works.
public MainGUI() {
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
getContentPane().add(tabbedPane, BorderLayout.CENTER);
JEditorPane instructionalEditorPane = new JEditorPane();
tabbedPane.addTab("Instructional", instructionalEditorPane);
JPanel codePanel = new JPanel();
JPanel drawPanel = new JPanel();
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, codePanel, drawPanel);
splitPane.setResizeWeight(0.75);
tabbedPane.addTab("Code Panel", splitPane);
JEditorPane unifiedInstPane = new JEditorPane();
JPanel unifiedCodePanel = new JPanel();
JPanel unifiedDrawPanel = new JPanel();
JSplitPane unifiedSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, unifiedCodePanel, unifiedDrawPanel);
unifiedSplitPane.setResizeWeight(0.75);
JSplitPane unifiedPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT,unifiedInstPane, unifiedSplitPane);
unifiedPanel.setResizeWeight(0.40);
tabbedPane.addTab("Unified Tab", unifiedPanel);
}
What I would like to do is just add the instructionalEditorPane and the splitPane to multiple tabbedPanes but when I do I loose the original Individual tabbedPanes. If I have to I can do it this way but I would then have to write to both the unifiedInstPane & the instructionalEditorPane to keep them updated. I would also have to do this for the 2 splitPanes which have the codePanel and drawPanels embedded. This will make it harder to keep all the panels in sync.
Any suggestions?
"Is it possible to add the same Jpanel to multiple TabbedPanes." -- no. You can only add a component to one container at a time. Your JPanels should share models but use unique components. The model will likely be a non-GUI class of your creation.
For example, here's a very simplistic rendering of my recommendations:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
public class MainGui2 extends JPanel {
private static final int TAB_COUNT = 3;
private JTabbedPane tabbedPane = new JTabbedPane();
private PlainDocument doc = new PlainDocument();
private Action btnAction = new ButtonAction("Button");
public MainGui2() {
for (int i = 0; i < TAB_COUNT; i++) {
tabbedPane.add("Tab " + (i + 1), createPanel(doc, btnAction));
}
setLayout(new BorderLayout());
add(tabbedPane);
}
private JPanel createPanel(PlainDocument doc, Action action) {
JTextArea textArea = new JTextArea(doc);
textArea.setColumns(40);
textArea.setRows(20);
JPanel panel = new JPanel();
panel.add(new JScrollPane(textArea));
panel.add(new JButton(action));
return panel;
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String title) {
super(title);
}
#Override
public void actionPerformed(ActionEvent evt) {
try {
String text = "Button Pressed!\n";
doc.insertString(doc.getLength(), text, null);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MainGui2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MainGui2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Better would be to create a formal model class that gets injected into each view, each tabbed pane's individual panes.
Edit
You state in comment:
Yes I can fix that by making calls to the instances but then I'm back to my original problem of having to make calls to each instance to affect a change in all the panel. Say for example I have a drawing panel and I need to call repaint(), I would have to make a call to 2 different instances to get both tabbedPanes to update. Is there any way around this?
Yes, the solution is to use an MVC, or model-view-control, structure. Your model holds your overall program logic, the views are what the user sees, and the control interacts between the two.
Consider having your model notify either the control or the views that its been changed, and then this stimulates a repaint an all observer views.
I'm using Container.getComponents() to get an array of Components stored inside the Container. I'm then modifying one of these Components (which happens to be a JLabel), but the changes are not showing on the GUI.
So I'm thinking maybe the method creates new instances of each Component which prevents me from making changes to the original component?
Here's my code:
Component[] components = source.getComponents();
if(components.length >= 2) {
if(components[1] instanceof JLabel) {
JLabel htmlArea = (JLabel) components[1];
htmlArea.setText("<html>new changes here</html>");
htmlArea.revalidate();
}
}
It is either another problem outside of the code, or you are doing this from the wrong thread.
Any changes on Swing components should be done in the event dispatch thread. Often is it most easy to surround the changing code with EventQueue.invokeLater(...) (or SwingUtilities.invokeLater, this is the same).
And make sure your component is actually visible on the screen.
There is no need to revalidate() or repaint() anything (unless you are doing something really strange)!
Where is your SSCCE that demonstrates your problem???
It works fine for me:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class TabbedPaneLabel extends JFrame
{
JTabbedPane tabbedPane;
public TabbedPaneLabel()
{
tabbedPane = new JTabbedPane();
add(tabbedPane);
tabbedPane.addTab("First", createPanel("<html>label with text</html>"));
tabbedPane.addTab("Second", createPanel("another label"));
JButton remove = new JButton("Change Label on first tab");
add(remove, BorderLayout.SOUTH);
remove.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Component[] components = tabbedPane.getComponents();
JPanel panel = (JPanel)components[0];
JLabel label = (JLabel)panel.getComponent(0);
String date = new Date().toString();
label.setText("<html>" + date + "</html>");
}
});
}
private JPanel createPanel(String text)
{
JPanel panel = new JPanel();
panel.add( new JLabel(text) );
return panel;
}
public static void main(String args[])
{
TabbedPaneLabel frame = new TabbedPaneLabel();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}