I have a show button to show a JTable on click but the table is not visible.
Note: when I remove the JScrollPane the code works properly but the header of the table is not shown, so any help please to make this code work properly without removing the JScrollPane
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class Training extends JFrame {
public Training() {
getContentPane().setLayout(new FlowLayout());
JTable table = new JTable();
table.setModel(new DefaultTableModel(new Object[][] { { "joe", "joe" },
{ "mickel", "mickel" }, }, new String[] { "LastName",
"FirstName" }));
final JScrollPane pane = new JScrollPane(table);
pane.setVisible(false);
getContentPane().add(pane);
JButton btn = new JButton("show");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
pane.setVisible(true);
}
});
}
public static void main(String[] args) {
Training app = new Training();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setSize(600, 600);
app.setVisible(true);
}
}
After pane.setVisible(true); add the following:
getContentPane().validate();
getContentPane().repaint();
A few things to note:
Never extends JFrame class unnecessarily, or else you might need to extend another class which is very necessary but in java a single class may not extend more than one other class (no multiple inheritance).
Always create Swing components on Event Dispatch Thread via SwingUtilities.invokeLater(Runnable r) block.
Do not use setSize(..) call JFrame#pack() before setting JFrame visible
No need for getContentPane.add(..) or getContentPane().setLayout(..), simply call add(..) or setLayout(..) on JFrame instance as these calls are fowared to the contentPane.
The problem you have is you do not refresh you frame/container after setting pane visible. I disagree with #Dan. Do not use validate() (getContentPane() is not necesarry either) rather:
revalidate();
repaint();
as revalidate() covers validate(). Also validate is used when new JComponents are added to a visible component, whereas revalidate() is used when JComponent is removed/added from a visible component.
Here is a fixed version of the code with the above implemented:
After button pressed:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class Training {
private JFrame frame;
public Training() {
frame = new JFrame();
frame.setLayout(new FlowLayout());
JTable table = new JTable();
table.setModel(new DefaultTableModel(new Object[][]{{"joe", "joe"},
{"mickel", "mickel"},}, new String[]{"LastName",
"FirstName"}));
final JScrollPane pane = new JScrollPane(table);
pane.setVisible(false);
frame.add(pane);
JButton btn = new JButton("show");
frame.add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.setVisible(true);
frame.pack();//this is so the frame will resize after adding pane
frame.revalidate();
frame.repaint();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Training();
}
});
}
}
UPDATE:
Also for a more reusable Layout, why not add all components to a JPanel, and add that JPanel to the JFrame, thus if you ever need to add more stuff its simple.
Related
I want to add a JPanel to a JLayeredPane when the user clicks enter, but the JPanel is not showing up.
If i add the JPanel to the JLayeredPane in the JFrame's constructor, everything is working correctly.
What do i have to do, that the JPanel is showing up, when the user clicks 'enter'?
Here's the code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class Test extends javax.swing.JFrame {
public static void main(String[] args) {
Test test = new Test();
test.setSize(800, 500);
test.setVisible(true);
}
public Test() {
setLayout(new BorderLayout());
//LayeredPane on JFrame
JLayeredPane jlp = new JLayeredPane();
jlp.setLayout(new BorderLayout());
this.add(jlp, BorderLayout.CENTER);
//Adds a JPanel to the North
JPanel jPNorth = new JPanel();
jPNorth.setBackground(Color.RED);
jlp.add(jPNorth, BorderLayout.NORTH, JLayeredPane.DEFAULT_LAYER);
//Adds Enter Keybinding
InputMap key_input_map = jlp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap key_action_map = jlp.getActionMap();
key_input_map.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "add_jpanel");
key_action_map.put("add_jpanel", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JPanel jPSouth = new JPanel();
jPSouth.setBackground(Color.YELLOW);
jlp.add(jPSouth, BorderLayout.SOUTH, JLayeredPane.DEFAULT_LAYER);
System.out.println("enter");
}
});
}
}
Thanks,
Jumagoro
You did everything correct, the solution is very simple. When you dynamically add swing Components to each other, you must to use component.repaint(); and component.revalidate(); to redraw the elements. Add the two commands after everything is added. So your actionPerformed method should be changed to the following:
public void actionPerformed(ActionEvent e) {
JPanel jPSouth = new JPanel();
jPSouth.setBackground(Color.YELLOW);
jlp.add(jPSouth, BorderLayout.SOUTH, JLayeredPane.DEFAULT_LAYER);
//Need these to here!
jlp.repaint();
jlp.revalidate();
System.out.println("enter");
}
My question is about layout in Java Swing.
I want to make a screen like shown below. I saw this video on youtube and made a gif of the part I want.
I want 2 panels and a button like this:
When i clicked the button the JPanel will be hidden and JTable's width will be 100% like html/css like this; (And when button clicked again JPanel will be shown etc..)
How can I do this? Which layout should I use?
There is more than one way to do it, but here's an example that uses BorderLayout as the main layout, and places the button in a left aligning FlowLayout:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
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.SwingConstants;
import javax.swing.SwingUtilities;
public class LayoutDemo {
private LayoutDemo() {
JFrame frame = new JFrame("Demo");
frame.setLocationByPlatform(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel buttonHolder = new JPanel(new FlowLayout(FlowLayout.LEADING));
frame.add(buttonHolder, BorderLayout.NORTH);
JButton button = new JButton("Toggle visibility");
buttonHolder.add(button);
final JPanel left = new JPanel();
left.setPreferredSize(new Dimension(100, 200));
left.setBackground(Color.BLUE);
frame.add(left, BorderLayout.LINE_START);
JLabel table = new JLabel("This pretends to be a table", SwingConstants.CENTER);
table.setPreferredSize(new Dimension(400, 200));
frame.add(table);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
left.setVisible(!left.isVisible());
}
});
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LayoutDemo();
}
});
}
}
I used setPreferredSize() to give the components some reasonable default size, but usually it should be automatically calculated by the layout manager from the sizes of the child components, or in case of a custom component, you should override getPreferredSize() return what is appropriate for the component.
The result looks like:
I wanted to create a fairly simple GUI program that switches between panels depending on buttons the user clicked. I searched around and came up with CardLayout being the best suggestion.
Basically in the examples of CardLayout, you create a "card" (a JPanel) and then add each component, like buttons, etc... and switch between the cards.
What I want to create is an object that is a "card" with all the components set up already, in a separate class, and just create an instance of that in the main program. I am a beginner and do not know the best design practices, so I didn't want to create my own class that extended JPanel, which I am pretty sure is terrible design.
You do it just like you would if you had create an instance of JPanel and add the components directly to it.
You need to ensure that the custom class extends from something JComponent or JPanel (preferably) and add them to the container like any other component, for example...
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestCard {
public static void main(String[] args) {
new TestCard();
}
public TestCard() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final CardLayout cardLayout = new CardLayout();
final JPanel cardPane = new JPanel(cardLayout);
cardPane.add(new Card01(), "Card01");
cardPane.add(new Card02(), "Card02");
JToggleButton btnCard01 = new JToggleButton("#1");
JToggleButton btnCard02 = new JToggleButton("#2");
ButtonGroup bg = new ButtonGroup();
bg.add(btnCard01);
bg.add(btnCard02);
JPanel buttons = new JPanel();
buttons.add(btnCard01);
buttons.add(btnCard02);
btnCard01.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(cardPane, "Card01");
}
});
btnCard02.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(cardPane, "Card02");
}
});
btnCard01.setSelected(true);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(cardPane);
frame.add(buttons, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Card01 extends JPanel {
public Card01() {
setLayout(new GridBagLayout());
add(new JLabel("#1"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class Card02 extends JPanel {
public Card02() {
setLayout(new GridBagLayout());
add(new JLabel("#2"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
I am trying to change the look of JComboBox components by extending BasicComboBoxUI class. The problem is that when I use the extended MyComboBoxUI class, combo boxes stop functioning properly.
This SSCCE is demonstrating my problem. The first combo box displays the selected item of the second combo box, and the first combo box doesn't have arrow button painted and items cannot be selected.
Note: I had no problem changing JButton components in this manner.
Main class:
import javax.swing.JFrame;
import javax.swing.UIManager;
public class Main {
public static void main(String[] args) {
UIManager.put("ComboBoxUI", "MyComboBoxUI");
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
ContentPane contentPane = new ContentPane();
frame.setContentPane(contentPane);
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
ContenPane class:
import java.awt.FlowLayout;
import javax.swing.JComboBox;
import javax.swing.JPanel;
public class ContentPane extends JPanel {
public ContentPane() {
setLayout(new FlowLayout());
JComboBox<String> firstComboBox = new JComboBox<>();
firstComboBox.addItem("firstComboBox - 1. item");
firstComboBox.addItem("firstComboBox - 2. item");
firstComboBox.addItem("firstComboBox - 3. item");
add(firstComboBox);
JComboBox<String> secondComboBox = new JComboBox<>();
secondComboBox.addItem("secondComboBox - 1.item");
secondComboBox.addItem("secondComboBox - 2. item");
secondComboBox.addItem("secondComboBox - 3. item");
add(secondComboBox);
}
}
MyComboBoxUI class:
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MyComboBoxUI extends BasicComboBoxUI {
private static MyComboBoxUI myComboBoxUI = new MyComboBoxUI();
public static ComponentUI createUI(JComponent component) {
return myComboBoxUI;
}
}
I think you want:
return new MyComboBoxUI();
When you have a static variable it means every combobox will share the same instance of the UI.
In the Java file below, I create a frame containing a panel, which then nests a second panel. I'm trying to listen for key strokes in the nested panel. My approach is to use an input map and an action map. I've found if I only have an input map for the nested panel, things work as expected. However, if the parent panel also has an input map, key stroke events are not passed to the nested panel. You can observe this behavior by commenting and uncommenting the first call to getInputMap().put. Does anyone have a solution for this?
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class InputMapTest extends JPanel {
public InputMapTest() {
super(new BorderLayout());
JPanel panel = new JPanel();
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
getInputMap().put(ks, "someAction");
getActionMap().put("someAction", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("here1");
}
});
ks = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
panel.getInputMap().put(ks, "someOtherAction");
panel.getActionMap().put("someOtherAction", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("here2");
}
});
add(panel);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.getContentPane().add(new InputMapTest());
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
see Oracle tutorial How to use KeyBindings
you miss there set focus to JPanel panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(...)