JFormattedTextfield only validate when pressed enter - java

For a task I need to make a JFormattedTextField with the following behavior:
If value is edited and isn't equal to the last validated value the background must become yellow.
Value validation may take place at any time
If focus is lost nothing should happen (if background is yellow it should remain yellow,...)
Action should be taken when Enter is pressed
I can't seem to find the correct combination of Listeners to accomplish this. I tried using KeyAdapter, InputVerifier and PropertyChangeListenerbut that gives me very ugly code wich only works for 80%.
How should this be done?
Edit: I wrote a small example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.ParseException;
import javax.swing.AbstractAction;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test extends JPanel {
private JFormattedTextField field;
private JLabel label;
private JButton btn;
public Test() {
super(new BorderLayout());
label = new JLabel("Enter a float value:");
btn = new JButton(new AbstractAction("Print to stdout"){
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(field.getValue());
}
});
field = new JFormattedTextField(new Float(9.81));
field.addKeyListener(new KeyAdapter(){
#Override
public void keyPressed(KeyEvent e){
field.setBackground(Color.YELLOW);
}
#Override
public void keyTyped(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ENTER){
try{
field.commitEdit();
field.setBackground(Color.WHITE);
}catch(ParseException e1){
field.setBackground(Color.RED);
}
}
}
});
field.setInputVerifier(new InputVerifier(){
#Override
public boolean verify(JComponent comp) {
try{
field.commitEdit();
field.setBackground(Color.YELLOW);
return true;
}catch(ParseException e){
field.setBackground(Color.RED);
return false;
}
}
});
add(label, BorderLayout.NORTH);
add(field, BorderLayout.CENTER);
add(btn, BorderLayout.SOUTH);
}
public static void main(String[] args) {
JFrame window = new JFrame("InputVerifier test program");
Container cp = window.getContentPane();
cp.add(new Test());
window.pack();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
This almost does everything I want. But the problem is the ENTER key is never caught. I think it is consumed before it reaches my KeyListener, but how can I prevent this?
Even if this can be prevented, I still have the feeling there should be a cleaner why to accomplish what above code does.

Try your hands on this code sample, tell me is this the desired behaviour, or you expecting something else, other than this :
import java.awt.*;
import java.awt.event.*;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
public class JFormattedExample
{
private String lastValidValue;
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("JFormattedTextField Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
final JFormattedTextField ftf = new JFormattedTextField(
NumberFormat.getNumberInstance());
ftf.setColumns(10);
ftf.setFocusLostBehavior(JFormattedTextField.PERSIST);
ftf.setValue(100);
lastValidValue = "100";
ftf.addCaretListener(new CaretListener()
{
public void caretUpdate(CaretEvent ce)
{
System.out.println("Last Valid Value : " + lastValidValue);
if (ftf.isEditValid())
{
String latestValue = ftf.getText();
System.out.println("Latest Value : " + latestValue);
if (!(latestValue.equals(lastValidValue)))
ftf.setBackground(Color.YELLOW.darker());
else
{
lastValidValue = ftf.getText();
ftf.setBackground(Color.WHITE);
}
}
else
{
System.out.println("Invalid Edit Entered.");
}
}
});
contentPane.add(ftf);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new JFormattedExample().createAndDisplayGUI();
}
});
}
}

Related

Strange error with Drag and Drop in AWT in Java

Allright here is the error: java.awt.dnd.InvalidDnDOperationException: The operation requested cannot be performed by the DnD system since it is not in the appropriate state. The error appears when I drop some File inside the program (grabbed from the desktop). I am using Ubuntu 16.04 with Nautilus.
import javax.swing.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.*;
import java.io.File;
import java.util.List;
class UI extends JFrame {
List<File> droppedFiles;
UI(){
super("My Program");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLayout(null);
this.setVisible(true);
this.setResizable(true);
this.setSize(800, 500);
this.setExtendedState(MAXIMIZED_BOTH);
JTextField dropArea = getDropArea();
this.add(dropArea);
}
private JTextField getDropArea(){
JTextField dropArea = new JTextField("Drop file here");
dropArea.setBounds(50, 50, 200, 200);
dropArea.setDropTarget(createNewDropTarget(dropArea));
return dropArea;
}
private DropTarget createNewDropTarget(JTextField dropArea) {
DropTarget dt = new DropTarget(){
#Override
public synchronized void drop(DropTargetDropEvent dtde) {
super.drop(dtde);
try {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
droppedFiles = (List<File>) dtde.getTransferable().
getTransferData(DataFlavor.javaFileListFlavor);
dropArea.setText(droppedFiles.get(0).getName());
}catch (Exception e){
e.printStackTrace();
}
}
};
return dt;
}
}
The Error appears on the line where droppedFiles is initialized. (in the try catch block).
In a way you set up DropTarget there is no need to call super.drop(dtde);. This is actually the reason for the exception. Here is the implementation of DropTarget.drop():
public synchronized void drop(DropTargetDropEvent dtde) {
clearAutoscroll();
if (dtListener != null && active)
dtListener.drop(dtde);
else { // we should'nt get here ...
dtde.rejectDrop();
}
}
Since you are not initializing DropTarget with a listener the drop is rejected, and the subsequent call getTransferable() fails with InvalidDnDOperationException. If you comment super.drop(dtde); the problem should go away. A cleaner alternative would be to create a listener and pass it to DropTarget. Here is a small example:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class DDTest extends JPanel {
public DDTest() {
setLayout(new BorderLayout());
final JTextField dropArea = new JTextField("Drop file here");
add(dropArea);
new DropTarget(dropArea, new DropTargetAdapter() {
#Override
public void drop(DropTargetDropEvent dtde) {
try {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
List<File> droppedFiles = (List<File>) dtde
.getTransferable().getTransferData(
DataFlavor.javaFileListFlavor);
dropArea.setText(droppedFiles.get(0).getName());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
private static void createAndShowGUI() {
final JFrame frame = new JFrame("DDTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DDTest());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
PS:
Note that using absolute layout can be complex and usually can be avoided. See A Visual Guide to Layout Managers for some ideas.

FocusListener & JOptionPane

There is the code of my simple Program.
There are four textFields.
when cursor is on first textField JOptionPane is Created and when I press ok
cursor moves to next field and OptionPane is created again
and so on
when cursor is on fourth field and I click OK on OptionPane,cursor moves to fifth field "f".
when cursor is in field,I print the possition of the field in array: System.out.println("first or Second or Third or Fourth")
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Hello extends JFrame implements ActionListener, FocusListener {
public JTextField[] fields = new JTextField[4];
public JPanel panel = new JPanel();
public JTextField f = new JTextField(12);
public static void main(String[] args) {
new Hello();
}
public Hello() {
for (int i = 0; i < 4; i++) {
fields[i] = new JTextField(12);
fields[i].addFocusListener(this);
panel.add(fields[i]);
}
add(panel);
add(f);
setTitle("Hello World");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(920, 420);
setLocation(100, 100);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent ae) {
}
#Override
public void focusGained(FocusEvent fe) {
if (fe.getSource() == fields[0]) {
JOptionPane.showMessageDialog(null, "HELLO");
fields[1].requestFocus();
System.out.println("FIRST");
} else if (fe.getSource() == fields[1]) {
JOptionPane.showMessageDialog(null, "HELLO");
fields[2].requestFocus();
System.out.println("SECOND");
} else if (fe.getSource() == fields[2]) {
JOptionPane.showMessageDialog(null, "HELLO");
fields[3].requestFocus();
System.out.println("THIRD");
} else if (fe.getSource() == fields[3]) {
JOptionPane.showMessageDialog(null, "HELLO");
f.requestFocus();
System.out.println("FOURTH")
}
}
#Override
public void focusLost(FocusEvent fe) {
}
}
When there is no OptionPane,the cursor moves forward from first field to the fourth and prints:
FIRST
SECOND
THIRD
FOURTH
but when there is JOptionPane
the output is :
FIRST
SECOND
FIRST
SECOND
THIRD
SECOND
THIRD
FOURTH
THIRD
FOURTH
FOURTH
One can see that after second field it comes back to first,
after third field it comes back to second,instead of to go to fourth
after fourth field it comes back to third.
I want to know why? and how can I fix this
The problem is that every time you click OK on the JOptionPane, the focus is returned to the last JTextField active before the JOptionPane was shown, so a new requestFocus event is added to the event queue for that control. Actually after the first time you click OK while executing your code, several dialogs are fire, you just don't see it because you show the same text (HELLO) every time. I have changed your code to make it work. Hope it helps!
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class testOptionPane extends JFrame implements ActionListener, FocusListener {
public ArrayList<JTextField> fields = new ArrayList<>();
public JPanel panel = new JPanel();
public JTextField f = new JTextField(12);
private int currentField = 0;
private boolean focusReturned = false;
public static void main(String[] args) {
new testOptionPane();
}
public testOptionPane() {
for (int i = 0; i < 4; i++) {
JTextField tf = new JTextField(12);
fields.add(tf);
tf.addFocusListener(this);
panel.add(tf);
}
add(panel);
fields.add(f);
add(f);
setTitle("Hello World");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(920, 420);
setLocation(100, 100);
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent ae) {
}
#Override
public void focusGained(FocusEvent fe) {
if (fe.getSource() == fields.get(currentField)) {
if (!focusReturned) {
JOptionPane.showMessageDialog(this, "focus on field " + String.valueOf(currentField));
System.out.println(currentField);
focusReturned = true;
} else {
currentField++;
focusReturned = false;
if (currentField < fields.size()) {
fields.get(currentField).requestFocus();
}
}
}
}
#Override
public void focusLost(FocusEvent fe) {
}
}

Java JCheckBox ItemListener makes program work incorrectly

I minimized my program to include only the problem and I tried to code exactly as I understood from many examples. When I used the ActionListener, I get problem solved. But I wonder why using ItemListener, checkbox does not operate correctly.
If I run my program without ItemListener, it works correctly. With this ItemListener, checkBox doesn't change state.
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class omaJFrame extends JFrame{
private JCheckBox checkBox1;
public omaJFrame() {
super("Window Title");
TheHandler handler = new TheHandler();
setLayout(new FlowLayout());
checkBox1 = new JCheckBox("Checkbox 1");
add(checkBox1);
checkBox1.addItemListener(handler);
}
private class TheHandler implements ItemListener {
String output = "";
public void itemStateChanged(ItemEvent event) {
if (event.getItem()==checkBox1)
output = String.format("%s", checkBox1.isSelected());
JOptionPane.showMessageDialog(null, output);
}
}
}
import javax.swing.JFrame;
public class EventHandlerMain {
public static void main(String[] args) {
omaJFrame window = new omaJFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(350,200);
window.setVisible(true);
}
}
Works for me. Note also that Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class OmaJFrame extends JFrame {
private JCheckBox checkBox1;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
OmaJFrame f = new OmaJFrame();
}
});
}
public OmaJFrame() {
super("Window Title");
setDefaultCloseOperation(EXIT_ON_CLOSE);
TheHandler handler = new TheHandler();
setLayout(new FlowLayout());
checkBox1 = new JCheckBox("Checkbox 1");
add(checkBox1);
checkBox1.addItemListener(handler);
pack();
setLocationByPlatform(true);
setVisible(true);
}
private class TheHandler implements ItemListener {
String output = "";
#Override
public void itemStateChanged(ItemEvent event) {
if (event.getItem() == checkBox1) {
output = String.format("%s", checkBox1.isSelected());
}
JOptionPane.showMessageDialog(null, output);
}
}
}

Refresh a Jlist from another JFrame

i want to refresh a JList when i push a button in another JFrame.
So i have a JFrame GuiBoss that manages employees (add,delete,update).When i press the button add, another Jframe opens, in wich i create a new employee.
//Open the "add_form" where i give details about a new employee.
private void btnAddActionPerformed(java.awt.event.ActionEvent evt) {
GuiBoss gb = new GuiBoss(contrb,boss);
Add_form af = new Add_form(gb,contrb,boss);
af.setVisible(true);
}
//refresh the list with the new employee added.
public void refresh(Employee e){
System.out.println("I reach this point!");
//if i print e.getName() it works, printing the right name that i give in the "add_form"
listModel.addElement(e);
//listModel.clear(); //don't work either.
}
My problem is that when i submit the details about the new employee i call the function refresh(Employee e) from the GuiBoss frame , the message ("I reach this point!") shows up on the console, the size of the listModel changes, but the list it doesen't refresh.
Also i must say that i set the model properly for the list.
//take data from form and call refresh(Employee e) from the main frame("GuiBoss")
private void btnAddActionPerformed(java.awt.event.ActionEvent evt) {
//String Id = txtID.getText();
String UserName = txtName.getText();
txtHour.setVisible(false);
boolean b = false;
if(rbtnYes.isSelected() == true){
b = true;
}
if(rbtnNo.isSelected() == true){
b = false;
}
if(rbtnYes.isSelected()==false && rbtnNo.isSelected() == false){
System.out.println("Select the presence!");
}
else{
txtOra.setVisible(true);
String Hour = txtHour.getText();
e = new Employee(UserName,b,Hour,boss); //boss i get from main frame when i start this add new employee form
contrb.addEmployee(e);
gb.refresh(e); //gb is of type GuiBoss were i have the function that does
// the refresh
}
}
Please let me know if u have any ideeas.Thanks.
Instead of popping up another frame, why not use a modal JDialog to collect the information about the new employee. When the dialog is closed, you can then extract the details from the dialog and refresh the list from within the current frame.
This prevents the need to expose portions of your API unnecessarily.
Check out How to use Dialogs for details.
Updated
Assuming you've set the model correctly, then your code should work...as per this example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestList03 {
public static void main(String[] args) {
new TestList03();
}
public TestList03() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultListModel model;
public TestPane() {
setLayout(new BorderLayout());
model = new DefaultListModel();
JList list = new JList(model);
add(new JScrollPane(list));
JButton btn = new JButton("Add");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.addElement("New Element");
}
});
add(btn, BorderLayout.SOUTH);
}
}
}
That would suggest that there is something else wrong that you're not showing us...
Updated with possible fix for reference issues
This basically demonstrates passing a reference of the main panel to a sub factory that is responsible for actually adding the value back into the main panel. Normally I'd use a interface of some kind instead of exposing the entire panel to simply provide access to a single method, but this was a quick example.
It uses both a normal implements and inner class as ActionListener to demonstrate the two most common means for passing a reference of "self" to another class.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestList03 {
public static void main(String[] args) {
new TestList03();
}
public TestList03() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements ActionListener {
private DefaultListModel model;
public TestPane() {
setLayout(new BorderLayout());
model = new DefaultListModel();
JList list = new JList(model);
add(new JScrollPane(list));
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.CENTER));
JButton btn1 = new JButton("Add 1");
btn1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Factory(TestPane.this, "Added by Button 1");
}
});
buttons.add(btn1);
JButton btn2 = new JButton("Add 2");
btn2.addActionListener(this);
buttons.add(btn2);
add(buttons, BorderLayout.SOUTH);
}
public void addItem(String text) {
model.addElement(text);
}
#Override
public void actionPerformed(ActionEvent e) {
new Factory(TestPane.this, "Added by Button 2");
}
}
public class Factory {
public Factory(TestPane testPane, String text) {
testPane.addItem(text);
}
}
}

Java tabbed panes within internal frame

I am trying to get an internal frame to contain tabbed panes. However, my code does not seem to be loading the panes into the internal frame. I have my code in the java files, called InternalFrame.java and TabbedPaneSample.java. The code for both files is included below. Can anyone show me how to fix the code below so that it loads the tabbed panes when I run InternalFrame.java?
Here is my code:
The code for InternalFrame.java is:
package test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
public class InternalFrame extends JFrame {
JButton openButton;
JLayeredPane desktop;
JInternalFrame internalFrame;
TabbedPaneSample myTabbedPaneSample = new TabbedPaneSample();
public InternalFrame() {
super("Click button to open internal frame with two panels.");
setSize(500, 400);
openButton = new JButton("Open");
Panel p = new Panel();
p.add(openButton);
add(p, BorderLayout.SOUTH);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
openButton.addActionListener(new OpenListener());
desktop = new JDesktopPane();
desktop.setOpaque(true);
add(desktop, BorderLayout.CENTER);
}
class OpenListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if ((internalFrame == null) || (internalFrame.isClosed())) {
internalFrame = new JInternalFrame("Internal Frame", true, true, true, true);
internalFrame.setBounds(50, 50, 200, 100);
internalFrame.add(myTabbedPaneSample, BorderLayout.CENTER);
internalFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
internalFrame.pack();
internalFrame.setMinimumSize(new Dimension(300, 300));
desktop.add(internalFrame, new Integer(1));
internalFrame.setVisible(true);
}
}
}
public static void main(String args[]) {
InternalFrame myInternalFrame = new InternalFrame();
myInternalFrame.setVisible(true);
}
}
And the code for TabbedPaneSample.java is:
package test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class TabbedPaneSample extends JTabbedPane {
private JTabbedPane tabbedPane = new JTabbedPane();
private ImageIcon closeImage = new ImageIcon("C:/test/shipIcon.gif");
private Dimension closeButtonSize;
private int tabCounter = 0;
public TabbedPaneSample() {
closeButtonSize = new Dimension(closeImage.getIconWidth() + 2, closeImage.getIconHeight() + 2);
}
public void add() {
final JPanel content = new JPanel();
JPanel tab = new JPanel();
tab.setOpaque(false);
JLabel tabLabel = new JLabel("Tab " + (++tabCounter));
JButton tabCloseButton = new JButton(closeImage);
tabCloseButton.setPreferredSize(closeButtonSize);
tabCloseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int closeTabNumber = tabbedPane.indexOfComponent(content);
tabbedPane.removeTabAt(closeTabNumber);
}
});
tab.add(tabLabel, BorderLayout.WEST);
tab.add(tabCloseButton, BorderLayout.EAST);
this.addTab(null, content);
this.setTabComponentAt(this.getTabCount() - 1, tab);
}
public static void main(String[] args) {
TabbedPaneSample main = new TabbedPaneSample();
main.add();
main.add();
}
}
Here's one approach, shown below. A more flexible approach using Action is referenced here.
Addendum: Reviewing your code, you should let the various layout managers and component preferred sizes do more of the work, as shown. In particular, this.setPreferredSize() is done for demonstration purposes. In a real application, you would restore user size and location preferences.
package overflow;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
/** #see https://stackoverflow.com/posts/6514889 */
public class InternalFrame extends JFrame {
JButton openButton;
JLayeredPane desktop;
JInternalFrame internalFrame;
public InternalFrame() {
super("Click button to open internal frame with two tabs.");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(400, 400));
openButton = new JButton("Open");
JPanel p = new JPanel();
p.add(openButton);
this.add(p, BorderLayout.SOUTH);
openButton.addActionListener(new OpenListener());
desktop = new JDesktopPane();
this.add(desktop, BorderLayout.CENTER);
this.pack();
this.setLocationRelativeTo(null);
}
class OpenListener implements ActionListener {
private static final int DELTA = 40;
private int offset = DELTA;
public void actionPerformed(ActionEvent e) {
internalFrame = new JInternalFrame(
"Internal Frame", true, true, true, true);
internalFrame.setLocation(offset, offset);
offset += DELTA;
internalFrame.add(createTabbedPane());
desktop.add(internalFrame);
internalFrame.pack();
internalFrame.setVisible(true);
}
}
private JTabbedPane createTabbedPane() {
JTabbedPane jtp = new JTabbedPane();
createTab(jtp, "One");
createTab(jtp, "Two");
return jtp;
}
private void createTab(JTabbedPane jtp, String s) {
jtp.add(s, new JLabel("TabbedPane " + s, JLabel.CENTER));
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
InternalFrame myInternalFrame = new InternalFrame();
myInternalFrame.setVisible(true);
}
});
}
}
First of all, I think Finally, I think you shouldn't use desktop.add(internalFrame, new Integer(1)) but rather desktop.add(internalFrame) instead, the reason is that JDesktopPane uses its layers (it is a JLayeredPane subclass) internally, and I don't think you should play with layers yourself.
Then, following this problem I had once with JInternalFrame, I would advise you call pack() after adding the internal frame to the desktop pane.
Hence, you should try with your OpenListener class looking like this:
class OpenListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if ((internalFrame == null) || (internalFrame.isClosed())) {
internalFrame = new JInternalFrame("Internal Frame", true, true, true, true);
internalFrame.setBounds(50, 50, 200, 100);
internalFrame.add(myTabbedPaneSample, BorderLayout.CENTER);
internalFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// internalFrame.pack();
internalFrame.setMinimumSize(new Dimension(300, 300));
// desktop.add(internalFrame, new Integer(1));
desktop.add(internalFrame);
internalFrame.pack();
internalFrame.setVisible(true);
}
}
}
Besides, I also agree with trashgod comments on Action of course and the simplifying rework he has done on your snippet.
I preferred to create in my Main Frame class (which extends JFrame) the following function:
private void showIntFrame(Class intFrameClass) {
JInternalFrame targetFrame = null;
int xoff = 0, yoff = 0;
for(JInternalFrame jif : jdp.getAllFrames()) {
if(jif.getClass().equals(intFrameClass))
targetFrame = jif;
if(jif.getLocation().x > xoff)
xoff = jif.getLocation().x;
if(jif.getLocation().y > yoff)
yoff = jif.getLocation().y;
}
if(targetFrame == null) {
try {
Constructor<JInternalFrame> c = intFrameClass.getConstructor(MainFrame.class);
targetFrame = c.newInstance(MainFrame.this);
} catch (Exception ex) {
System.err.println("Exception in MainFrame.showIntFrame() while creating new JInternalFrame instance. " + ex.getLocalizedMessage());
ex.printStackTrace();
return;
}
jdp.add(targetFrame);
targetFrame.setLocation(xoff + 30, yoff + 30);
}
targetFrame.setVisible(true);
try {
targetFrame.setSelected(true);
} catch (PropertyVetoException ex) {
System.err.println("PropertyVetoException in MainFrame.showIntFrame() while activating JInternalFrame instance. " + ex.getLocalizedMessage());
}
}
Here jdp is instance of JDesktopPane, which previously was set as ContentPane of my main JFrame.
Because my programs often contain numbers of different classes, inherited from JInternalFrame, it is easier to call this function from event handlers to show new subclass of JInternalFrame.
Every subclass of JInternalFrame in my programs have one constructor with one parameter - MainFrame (main JFrame).

Categories

Resources