I have a JComboBox that takes up too much room my GUI because of the long Strings it contains. I only need to see their full length while making a selection. So, I'd like the JComboBox to be full size only then and shorter otherwise.
The following code seems to work. But, since I'm very much a Java newbie, I'm just wondering if there's a better or more standard way of doing this.
Thanks.
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class DynamicJComboBox{
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.setSize(250, 100);
JComboBox box = new JComboBox();
box.addItem("Really Long Line Number One");
box.addItem("Really Long Line Number Two");
box.addItem("Really Long Line Number Three");
box.addItem("Really Long Line Number Four");
box.setPreferredSize(new Dimension(100, 30));
box.addPopupMenuListener (new PopupMenuListener() {
#Override
public void popupMenuCanceled(PopupMenuEvent e) {}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
box.setSize(100,30);
}
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
box.setSize(200,30);
}
});
frame.add(box);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception exc) {}
SwingUtilities.invokeLater(() -> {
createAndShowGUI();
});
}
}
The following code seems to work
Not really because you change the size of the combo box which would cause the combo box to overwrite any component display on the right of it. Also, the combo box arrow is drawn in the middle of the combo box.
Check out Combo Box Popup. This solution also uses a PopupMenuListener (so you were on the right track) but it only increases the width of the popup when it is displayed, not the combo box as well.
Small hack to get pop up menu size bigger enough to show items even though the combo box size could be smaller
Source: http://www.jroller.com/santhosh/entry/make_jcombobox_popup_wide_enough
import java.awt.Dimension;
import java.util.Vector;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
public class ComboBoxFullMenu<E> extends JComboBox<E> {
public ComboBoxFullMenu(E[] items) {
super(items);
addActionListener(this);
}
public ComboBoxFullMenu(Vector<E> items) {
super(items);
addActionListener(this);
}
public ComboBoxFullMenu(ComboBoxModel<E> aModel) {
super(aModel);
addActionListener(this);
}
/**
* Small hack to get pop up menu size bigger enough to show items even though
* the combo box size could be smaller
* */
private boolean layingOut = false;
#Override
public void doLayout(){
try{
layingOut = true;
super.doLayout();
}finally{
layingOut = false;
}
}
#Override
public Dimension getSize(){
Dimension dim = super.getSize();
if ( !layingOut ) {
dim.width = Math.max(dim.width, getPreferredSize().width);
}
return dim;
}
}
Related
I have a GUI with a JSpinner using a SpinnerNumberModel using double values.
As soon as I change the content of the Editor of the JSpinner, I want the background to change to yellow (to show that the currently displayed value is not the one "saved" in the JSpinner respectively its Model.
If that content is not valid (e.g. out of the allowed range specified by my SpinnerNumberModel or a text as "abc") the background should change to red.
I tried to achieve what I want with a FocusListener already but yet have not been successful, also I am not sure if It could work anyway, as I need to check the content somewhere between focussing and defocussing.
I checked Tutorials for all Listeners that exist for Swing components, but could not find a right one that suits the job. (here I informed myself)
I am new to the concept of Listeners and would really appreciate any help that gets me closer to solving the problem but also helps generally understanding Listeners and how to use them in this context better!
My really basic code example with the mentioned poor attempt using a focus listener:
public class test implements FocusListener{
JFrame frame;
SpinnerNumberModel model;
JSpinner spinner;
JComponent comp;
JFormattedTextField field;
public test() {
JFrame frame = new JFrame("frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
model = new SpinnerNumberModel(0., 0., 100., 0.1);
spinner = new JSpinner(model);
comp = spinner.getEditor();
field = (JFormattedTextField) comp.getComponent(0);
field.addFocusListener(this);
frame.getContentPane().add(spinner);
frame.getContentPane().add(new JButton("defocus spinner")); //to have something to defocus when testing :)
frame.pack();
frame.setVisible(true);
}
#Override
public void focusGained(FocusEvent e) {
// TODO Auto-generated method stub
//when the values of the field and the spinner don't match, the field should get yellow
if(!field.getValue().equals(spinner.getModel().getValue())) {
field.setBackground(Color.YELLOW);
}
}
#Override
public void focusLost(FocusEvent e) {
// TODO Auto-generated method stub
//if they match again, reset to white
if(!field.getValue().equals(spinner.getModel().getValue())) {
field.setBackground(Color.RED);
}
}
}
A JSpinner uses a text field as the editor for the spinner
So, you can add a DocumentListener to the Document of the text field that is used as the editor.
Something like:
JTextField textField = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField());
textField.getDocument.addDocumentListener(...);
Then when text is added/removed a DocumentEvent will be generated and you can do your error checking. Read the section from the Swing tutorial on Listener For Changes on a Document for more information and working examples.
You can use CaretListener , here is a start:
import java.awt.Color;
import java.awt.Component;
import javax.swing.BoxLayout;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
public class SpinerTest{
JSpinner spinner;
public SpinerTest() {
JFrame frame = new JFrame("frame");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
SpinnerNumberModel model = new SpinnerNumberModel(0., 0., 100., 0.1);
spinner = new JSpinner(model);
setCaretListener();
frame.getContentPane().add(spinner);
frame.pack();
frame.setVisible(true);
}
private void setCaretListener() {
for(Component c : spinner.getEditor().getComponents()) {
JFormattedTextField field =(JFormattedTextField) c;
field.addCaretListener(new CaretListener(){
#Override
public void caretUpdate(CaretEvent ce) {
if (field.isEditValid()) {
//add aditional test as needed
System.out.println("valid Edit Entered " + field.getText());
field.setBackground(Color.WHITE);
}
else {
System.out.println("Invalid Edit Entered" + field.getText());
field.setBackground(Color.PINK);
}
}
});
}
}
public static void main(String[] args) {
new SpinerTest();
}
}
I was able to fulfill the task with a combination of a KeyListener, a DocumentListener and a FocusListener. The solution might not be the easiest, but finally I coded sth. that works. Comments in the file appended should explain how I dealt with the problem.
I expanded the original task with a CommaReplacingNumericDocumentFilter expands DocumentFilter class that was not written by me, I got the code from my professor and edited it to my needs only. Now only digits, minus and e, E are accepted as entries in the JSpinner.
Commas are replaced with dots also.
Code:
import java.awt.*;
import java.awt.event.*;
import java.util.Locale;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class test implements DocumentListener, ChangeListener, KeyListener{
boolean keyPressed;
JFrame frame;
SpinnerNumberModel model;
JSpinner spinner;
JComponent comp;
JFormattedTextField field;
public test() {
JFrame frame = new JFrame("frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
model = new SpinnerNumberModel(0., 0., 100000., .1);
spinner = new JSpinner(model);
//disable grouping for spinner
JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner);
editor.getFormat().setGroupingUsed(false);
spinner.setEditor(editor);
comp = spinner.getEditor();
field = (JFormattedTextField) comp.getComponent(0);
field.getDocument().addDocumentListener(this);
field.addKeyListener(this);
spinner.addChangeListener(this);
frame.getContentPane().add(spinner);
frame.pack();
frame.setVisible(true);
}
#Override
public void insertUpdate(DocumentEvent e) {
DocumentEventHandler(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
DocumentEventHandler(e);
}
#Override
public void changedUpdate(DocumentEvent e) {
DocumentEventHandler(e);
}
public static boolean isNumeric(String str)
{
try
{
double d = Double.parseDouble(str);
}
catch(NumberFormatException nfe)
{
return false;
}
return true;
}
public static void main(String[] args) {
//to get the right format for double precision numbers
Locale.setDefault(Locale.US);
test test = new test();
}
#Override
public void stateChanged(ChangeEvent e) {
System.out.println("valuechanged: " + spinner.getValue().toString());
if(keyPressed) {
field.setBackground(Color.WHITE);
}
keyPressed = false;
}
public void DocumentEventHandler(DocumentEvent e) {
//as soon as update is inserted, set background to yellow
if (keyPressed) {
field.setBackground(Color.YELLOW);
//check if input is numeric and in bounds
String text = field.getText();
if (isNumeric(text)) {
double value = Double.parseDouble(text);
if (value < (Double)model.getMinimum() || value > (Double)model.getMaximum()) {
field.setBackground(Color.RED);
}
}
else { //set background to red
field.setBackground(Color.RED);
}
}
keyPressed = false;
//System.out.println(e.toString());
//System.out.println("Text: " + field.getText());
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
/** If not done yet, replaces the DocumentFilter with one replacing commas by decimal points.
* This can't be done at the very beginning because the DocumentFilter would be changed to a
* javax.swing.text.DefaultFormatter$DefaultDocumentFilter when setting up the JSpinner GUI. */
public void keyPressed(KeyEvent e) {
PlainDocument document = (PlainDocument)(field.getDocument());
if(!(document.getDocumentFilter() instanceof CommaReplacingNumericDocumentFilter))
document.setDocumentFilter(new CommaReplacingNumericDocumentFilter());
/*Tell the other handlers that a key has been pressed and the change in the document does
* not come from using the JSpinner buttons or the MouseWheel.
*/
keyPressed = true;
}
}
/** A javax.swing.text.DocumentFilter that replaces commas to decimal points
* and ignores non-numeric characters except 'e' and 'E'. This is called before
* modi */
class CommaReplacingNumericDocumentFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr)
throws BadLocationException {
text = filter(text);
if (text.length() > 0)
super.insertString(fb, offset, text, attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text,
AttributeSet attrs) throws BadLocationException {
text = filter(text);
if (text.length() > 0)
super.replace(fb, offset, length, text, attrs);
}
String filter(String text) {
return text.replace(',', '.').replaceAll("[^0-9eE.-]","");
}
}
I'm quite new on JAVA, and i have a question (i hope my english is not too bad).
Here is my process :
Open a first JFrame in the Main, with a JButton (to open the second
JFrame).
On click, with ActionLister, i call the process to open my second
window, with a black background (this works very well).
BUT, if i add a long process (in my code, just a sleep(5000)) just after setVisible() my second JFrame, this one will appear in white, and waits for the sleep(5000) to end before being black.
Questions :
Can someone tell me why the second JFrames appears white until the
end of process ? Maybe i make something wrong when i build my JFrame
?
Can someone tell me how to show my second JFrame black BEFORE the process ends ?
I searched for a long time, and saw that if my second window is built direct in the main thread it's ok even with the sleep before end of process.
But when i am in another thread (like when i click on the button), that doesn't work good !
SECOND PART :
On click on the button from the first window :
The second window shows up (empty with black background). then, the result's calcul is launched.
Calculate the result cant take 20sec, and will find 1 element each 5 seconds.
Each times an element is found, i want it to be shown in the second window.
For that, i added an observer on this result from the JFrame, which will add an element each time one element is found. I hope you understand.
Here picture of what i want to make :
Process
Here my project .JAR : http://dl.free.fr/b5IUSStBJ
Here my result's calcul :
public void launchCalculateResult(){
String[] tabelements = {"test1","test2", "test3", "test4", "test5"};
for (int i=0; i < 5; i++){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
_elementslist.add(tabelements[i]);
notifyObservers();
}
}
you can see that it adds an element in a list each 2 seconds, and then notify the observers (my second window), then the observer adds an element :
public void refresh(Observable o) {
_otherwindow.addResultElement(_result.getLastElement());
}
The behaviour I got :
The Result calculates good, and in the end the second window looks good, with its 5 elements. But during the result's search, my second windows remains empty and white . . .
I repeat the aim :
Each time an element is added in the Result, i want to show it in my second window.
You're calling the long process on the Swing event thread, and this will tie up the thread preventing it from doing its important jobs, including painting your new JFrame.
The canonical solution is to use a background thread for your long processes, and for Swing GUI's, you'd want to use a SwingWorker -- if the background process needs to communicate with the GUI (which is usually the case).
For the details on this problem and solution, please check out: Concurrency in Swing
Side issue: you'll usually not want to show multiple JFrames in your application. For why this is important and for how you can improve this design, please check out Multiple JFrames
For example
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class SwingExample extends JPanel {
private JButton openDialogBtn = new JButton(new OpenDialogAction("Open Dialog"));
private JDialog dialog;
private DialogPanel dialogPanel = new DialogPanel();
public SwingExample() {
setPreferredSize(new Dimension(400, 400));
add(openDialogBtn);
}
private class OpenDialogAction extends AbstractAction {
public OpenDialogAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
dialogPanel.setText("");
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(SwingExample.this);
dialog = new JDialog(win, "Dialog", ModalityType.MODELESS);
dialog.add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(win);
}
new SwingWorker<Void, Integer> () {
private final int maxI = 5;
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < maxI; i++) {
publish(i);
Thread.sleep(1000);
}
return null;
}
protected void process(java.util.List<Integer> chunks) {
for (Integer chunk : chunks) {
dialogPanel.setText("Time: " + chunk);
}
};
protected void done() {
dialogPanel.setText("Done!");
};
}.execute();
dialog.setVisible(true);
}
}
private class DialogPanel extends JPanel {
private JTextField textField = new JTextField(10);
public DialogPanel() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(200, 200));
add(textField);
}
public void setText(String text) {
textField.setText(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
SwingExample mainPanel = new SwingExample();
JFrame frame = new JFrame("SwingExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
Example 2: handles Strings being passed into a JList<String> using a SwingWorker<Void, String>
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SwingExample extends JPanel {
private JButton openDialogBtn = new JButton(new OpenDialogAction("Open Dialog"));
private JDialog dialog;
private DialogPanel dialogPanel = new DialogPanel();
public SwingExample() {
setPreferredSize(new Dimension(400, 400));
add(openDialogBtn);
}
private class OpenDialogAction extends AbstractAction {
public OpenDialogAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
dialogPanel.clearList();
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(SwingExample.this);
dialog = new JDialog(win, "Dialog", ModalityType.MODELESS);
dialog.add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(win);
}
new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
String[] tabelements = { "test1", "test2", "test3", "test4", "test5" };
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publish(tabelements[i]);
}
return null;
}
protected void process(java.util.List<String> chunks) {
for (String chunk : chunks) {
dialogPanel.addText(chunk);
}
};
protected void done() {
dialogPanel.addText("Done!");
};
}.execute();
dialog.setVisible(true);
}
}
private class DialogPanel extends JPanel {
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> jList = new JList<>(listModel);
public DialogPanel() {
jList.setPrototypeCellValue("ABCDEFG HIJKLMNOP");
jList.setVisibleRowCount(6);
JScrollPane scrollPane = new JScrollPane(jList);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBackground(Color.BLACK);
setPreferredSize(new Dimension(200, 200));
add(scrollPane);
}
public void clearList() {
listModel.clear();
}
public void addText(String text) {
listModel.addElement(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
SwingExample mainPanel = new SwingExample();
JFrame frame = new JFrame("SwingExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
//Make constructor class for both JFrame then
//write this code into your JFrame where your button is accesing another JFrame
//Note:- jb=button var name,
// jf=JFrame vatr name,
// addnew()=JFrame Class to be open.
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
new addnew();
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
});
It might work as well.
I have a JPanel that currently has a swing.box in it, which in itself houses an arbitrary number of JButtons.
Now one of the JButtons has the ability to create a new JButton, which goes fine.
However, after creating said new button, I want the button to go in the list at the right position (alphabetically sorted) which also goes fine. I am however sure that it is never the last button as I add some standard buttons at the end.
Now the problem is that I cannot nuke the box that has said buttons in them after which I rebuild the box.
(also see https://github.com/Diamundo/PhotoSelectorSys/ButtonPanel.java and specifically at line 42, start of function initButtons() )
(problem I also have, is that I add buttons with a preferred size which does take effect if I put them straight in the panel, but not if I put them in the box... so if you have a solution which lets me delete all buttons instead of the box that's also very welcome :D )
package photoselectorsys;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.ArrayList;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class ButtonPanel extends JPanel {
private AppFrame listener;
private String path;
private Box box;
public ButtonPanel(int width, int height){
setPreferredSize(new Dimension(width, height));
setVisible(true);
box = Box.createVerticalBox();
}
public void setPath(String path){
this.path = path;
initButtons();
// pushButtons();
this.revalidate();
}
/*public void pushButtons() {
for(JButton jb : buttons) { //buttons was an ArrayList of button names. Is now the box.
this.remove(jb); //remove the button from the panel.
}
for(JButton jb : buttons) { //buttons should be changed to the box
add(jb);
}
}*/
public void initButtons(){
/* if(box.hasButtons()) {
box.nuke();
box = new Box(..);
}
*/
JButton jb;
File directory = new File(path);
File[] listFiles = directory.listFiles();
ArrayList<String> btns = new ArrayList<>();
for (File file : listFiles) {
if (file.isDirectory()) {
btns.add(file.getName());
}
}
btns.add("Add a new Button");
btns.add("Rotate CW");
btns.add("Rotate CCW");
btns.add("Rotate 180*");
System.out.println("buttons " + btns.size());
if(btns.size() > 4) { //if there's at least one folder, size > 4. Else add one big button or ask to create subfolder
for(String btn : btns) {
jb = new JButton(btn);
jb.setFont(new Font("Arial", Font.PLAIN, 20));
jb.setPreferredSize(new Dimension(this.getWidth()-20, 40));
jb.setVisible(true);
jb.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
onButtonPress((JButton) arg0.getSource());
}
#Override
public void mouseEntered(MouseEvent e) { /* unused */ }
#Override
public void mouseExited(MouseEvent e) { /* unused */ }
#Override
public void mousePressed(MouseEvent e) { /* unused */ }
#Override
public void mouseReleased(MouseEvent e) { /* unused */ }
});
/*box.*/add(jb);
}
} else {
// do add folder magic from the start.
jb = new JButton("Add a new Button");
jb.setFont(new Font("Arial", Font.PLAIN, 20));
jb.setPreferredSize(new Dimension(this.getWidth()-20, this.getHeight()-30));
jb.setSize(new Dimension(this.getWidth()-20, this.getHeight()));
jb.setVisible(true);
jb.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
onButtonPress((JButton) arg0.getSource());
}
#Override
public void mouseEntered(MouseEvent e) { /* unused */ }
#Override
public void mouseExited(MouseEvent e) { /* unused */ }
#Override
public void mousePressed(MouseEvent e) { /* unused */ }
#Override
public void mouseReleased(MouseEvent e) { /* unused */ }
});
/*box.*/add(jb);
}
// add(box);
}
public void onButtonPress(JButton jb){
// not interesting for this question. See github for function.
}
public void addListener(AppFrame listener) {
this.listener = listener;
}
}
To change the buttons in your box you don't have to create a new box but rather do one of the following 2 things:
a) add the button at the correct position using box.add( button, index )
b) remove all buttons using box.removeAll() and (re)add the buttons as needed
I'd prefer variant a) but there might be cases where rebuilding the box contents from scratch might be the better way.
Normally that alone should be sufficient since the component should realize it's layout is invalid and needs to be recalculated. If this doesn't happen or you need to revalidate the parent container as well for some reason call the invalidate() method on the relevant component.
how do you trap the event before the new tab is switched to?
In every Tab I have JTable and i do something with it's data(delete, add , update). I would like to do data validation(save or cancel changes) before switching to the new tab. I use Java 1.5.
class ViewPanel extends JPanel
{
private void Components() {
setPreferredSize(new Dimension(700, 400));
tabbedPane.addTab("DC", ANSFER.getIcon(),new DcTabPanel(this), "DC");
tabbedPane.addTab("PC", THUMB4.getIcon(),new PcTabPanel(this), "PC");
tabbedPane.addChangeListener(this);
add(tabbedPane);
}
public void stateChanged(ChangeEvent e) {
}
}
JTabbedPane is backed by a SingleSelectionModel. If you extend DefaultSingleSelectionModel, you can override the setSelectedIndex method and implement your logic.
// in new selection model:
public void setSelectedIndex(int index) {
// do pre-switch things here
super.setSelectedIndex(index);
}
// in ViewPanel, on tabbedPane create:
tabbedPane.setModel(newSelectionModel);
The reason you can't simply use a ChangeListener is because that fires on change. By extending the selection model, you fire before the tab change.
You can prevent tab switching by extending JTabbedPane and override setSelectedIndex(int). Here is a small example illustrating that. It simply prevents from switching between non-contiguous tabs:
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class Test2 {
private static class BlockingTabbedPane extends JTabbedPane {
public static interface TabSwitchAllower {
public boolean allowTabSwitch(int from, int to);
}
private TabSwitchAllower allower;
public BlockingTabbedPane(TabSwitchAllower allower) {
super();
this.allower = allower;
}
#Override
public void setSelectedIndex(int index) {
if (allower == null || allower.allowTabSwitch(getSelectedIndex(), index)) {
super.setSelectedIndex(index);
}
}
}
protected static void initUI() {
final JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BlockingTabbedPane.TabSwitchAllower allower = new BlockingTabbedPane.TabSwitchAllower() {
#Override
public boolean allowTabSwitch(int from, int to) {
if (Math.abs(from - to) == 1) {
return true;
} else {
JOptionPane.showMessageDialog(frame, "You can only switch between contiguous tabs");
}
return false;
}
};
JTabbedPane tabbedPane = new BlockingTabbedPane(allower);
for (int i = 0; i < 10; i++) {
tabbedPane.addTab("Tab-" + i, new JLabel("Hello tab " + i));
}
frame.add(tabbedPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initUI();
}
});
}
}
java actionlistener on a tab
How to Write a Change Listener (Oracle Docs)
JTabbedPane API (Oracle Docs)
Those two links should help you out. I haven't really worked with tabbedPanes, but I am assuming that the getSelectedComponent() will return the current selected tab. So you can have a handle to the currentTab which will be set during instantiation. Then you can have something like this.
class TabListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
// Replace JSlider with whatever your tab's data type is
JSlider source = (JSlider)e.getSource();
// Use the 'currentTab' handle to do what you want.
currentTab = getSelectedComponent();
// I'm assuming that the 'selected component' by the time this stuff
// runs is going to be the new selected tab.
}
}
I am not too confident about my answer, but I certainly hope that this will point you towards the right direction! Please say if you need any clarification or anything! If I happen to discover anything that I think might be useful, I'll be certain to edit my answer!
I want to make an event which will be triggered after i make the selection to the JComboBox.
the problem I'm now facing is that when i added an ActionListener, it was triggered when the user clicked on the box but BEFORE he actually chose the new item, thus the action listener was activated all the time on the previous value which was selected in the box. what i want to do is simply changing the title of an JTextArea according to the selection.
I tried doing something like this:
jBox.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
String alt = GetAlgoAreaTitleByChoice();
panel.remove(jArea);
currentBest = setArea("",alt);
currentBest.setBounds(50, 350, 1000, 290);
panel.add(jArea);
}
});
and the method inside:
private String GetArgsAreaTitleByChoice(){
String chi = jBox.getSelectedItem().toString();
if(chi.equals(generalChoice)){
return "Hello";
}
else if(chi.equals(algoChoice)){
return "World";
}
else if(chi.equals(argsChoice)){
return "Hello";
}
return null;
}
I've tried using the SELECTED events now like this:
public void itemStateChanged(ItemEvent e) {
JComboBox cb = (JComboBox)e.getSource();
// Get the affected item
String item = cb.getSelectedItem().toString();
if (e.getStateChange() == ItemEvent.SELECTED) {
panel.remove(jBox);
textArea = setArea("", item);
panel.add(jBox);
}
but it seems to remove the area from the panel without adding it back... why is this happening?
Here is a simple demonstration with a sample code :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Tester {
public Tester(){
JComboBox box = new JComboBox();
box.addItem("One");
box.addItem("Two");
box.addItem("Three");
box.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e){
if(e.getStateChange()==ItemEvent.SELECTED){
e.getItem(); //Do what ever you want :))
}
}
});
JFrame frame = new JFrame();
frame.getContentPane().add(box);
frame.pack();
frame.setVisible(true);
}
public static void main(String [] args) {
Tester tester = new Tester();
}
}
For listening of events from JComboBox is better implements ItemListener, returns two events SELECTED/DESELECTED
EDIT
if you remove/add JComponent(s) on Runtime and in already visible container, then you have to call (as least code lines)
revalidate();
repaint();