I have created a JDialog which contains a JComboBox and a panel underneath which should display a different content based on the value selected in the JComboBox. I have created a JPanel (panel_1) which is added to the content pane of the dialog and an additional JPanel for each of the possible items in the JComboBox (for example panel_item_1 and panel_item_2 if it does have only 2 items). I have attached the following listener class in the JComboBox:
public class SelectedListener implements ActionListener {
private SettingsDialog dialog;
public SelectedListener(SettingsDialog dialog){
this.dialog = dialog;
}
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
String selected_settings = (String)cb.getSelectedItem();
if(selected_settings.compareTo("Option 1") == 0){
dialog.panel_1 = dialog.panel_item_1;
dialog.panel_1.updateUI();
}else if(selected_settings.compareTo("Option 2") == 0 ){
dialog.panel_1 = dialog.panel_item_2;
dialog.panel_1.updateUI();
}
}
}
However this doesn't make the panel update with the new content. Any suggestion? Thanks in advance
which should display a different content based on the value selected in the JComboBox.
Read the section from the Swing tutorial on How to Use Card Layout which has a working example that does exactly what you want.
Edit:
dialog.panel_1 = dialog.panel_item_1;
The real problem is that you can't just change the reference to a variable and expect the component to show up on the panel. You still need to add the component to the panel before you do a revalidate() on the panel. So your code is like:
panel.remove(...);
panel.add(...);
panel.revalidate();
panel.repaint();
However, the better solution is to use a CardLayout which does all this work for you.
Related
I have a JPanel which consists of a dropdown and a text field inside my JFrame. There is a button in my JFrame, when user clicks on that button, application adds new JPanel with the same components i.e. drop down and a text field. So, for this I have created a function which gets called on clicking on the button using ActionListener.
Everything works fine from GUI side but the problem is when user is done with adding JPanels and entering the values in these drop downs and text fields, it will click on Submit button. Upon clicking on Submit button, I should be able to fetch the values from all drop downs and text fields. This is a challenge, since I am using the same functions to create JPanels, I can't call its name to get the values since that will give me the last JPanel values.
Any suggestion how I should go about this? I have added the screenshot of my JFrame and the function to create the JPanel. Any help is appreciated. Thanks.
public static void AddPanel(final Container pane) {
panel1 = new JPanel();
String text = "<html><b>Property" + nooftimes + " :</b></html>";
JLabel label = new JLabel(text);
label.setPreferredSize(new Dimension(80, 30));
panel1.add(label);
panel1.add(new JLabel("Please enter the property"));
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>();
model.addElement("value1");
model.addElement("value2");
model.addElement("value3");
model.addElement("value4");
model.addElement("value5");
final JComboBox<String> comboBox1 = new JComboBox<String>(model);
AutoCompleteDecorator.decorate(comboBox1);
comboBox1.setPreferredSize(new Dimension(120, 22));
panel1.add(comboBox1);
final JTextField txtfield1 = new JTextField(
"Please enter your value here");
txtfield1.setPreferredSize(new Dimension(200, 22));
panel1.add(txtfield1);
txtfield1.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
txtfield1.setText("");
}
public void focusLost(FocusEvent e) {
// nothing
}
});
container.add(panel1);
nooftimes++;
frame.revalidate();
frame.validate();
frame.repaint();
}
Screenshot:
}
You could return the JPanel and store it in a List<JPanel>. When you click your submit-Button you are able to iterate through the JPanels and its Components.
public class Application {
private static List<JPanel> panels = new ArrayList<>();
private static Container someContainer = new Container();
public static void main(String[] args) {
panels.add(addPanel(someContainer));
panels.add(addPanel(someContainer));
panels.add(addPanel(someContainer));
submit();
}
public static JPanel addPanel(final Container pane) {
JPanel panel1 = new JPanel();
// shortened code
final JComboBox<String> comboBox1 = new JComboBox<String>();
panel1.add(comboBox1);
final JTextField txtfield1 = new JTextField("Please enter your value here");
txtfield1.setText(String.valueOf(Math.random()));
panel1.add(txtfield1);
return panel1;
}
private static void submit() {
for (JPanel panel : panels) {
Component[] components = panel.getComponents();
for (Component comp : components) {
// Cast comp to JComboBox / JTextField to get the values
if (comp instanceof JTextField) {
JTextField textField = (JTextField) comp;
System.out.println(textField.getText());
}
}
}
}
}
You could simply have a class (extending JPanel) with specific methods to add your components , and to get inputs from user (i.e. get the combo box selected index and text from textfield ).
Every time you add a panel, you don't call a static method, but you create an instance of this class, keeping the reference somewhere (for example adding it to an arraylist).
But you could consider a different scenario: personally i don't like to add components "on fly", you could have a component (for example another JComboBox), where user can select the number of values he needs.
You decide a default value (for example 4), so at the beginning you create 4 panels of your class, and you can use a simple array containing them.
If the user changes the number of panels, you could dispose frame and create a new one.
Of course this solution does not woork good if you want to keep inputs inserted, or if the frame construction takes a lot of time.
Here there is a screenshot of a gui i created: user can select the number of partials, when the choice changes i just recreate the panels below,containing the textfields (which are memorized in a two-dimensional array).
help,
my questions are:
why isn't itemStateChanges triggered, I tried to put it in the inner class ButtonHandler and also in RadioButtonHandler Im having trouble with it, what is the right way to do it?
I want to trigger and check the marked JRadioButtons after the user click the "check" button.
What is the right way to check which button was clicked, I feel like comparing the strings is bad programming practise. Maybe using an ID ?
How should I make a "reset" button(start over), I want to uncheck all radio buttons and run the constructor once again.
Thank you for your help !
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class ExamFrame extends JFrame {
static ArrayList<Question> qArrList;
JRadioButton a1,a2,a3,a4;
public ExamFrame() {
super("Quiz");
setLayout(new GridLayout(0, 1));
GridBagConstraints gbc = new GridBagConstraints();
Exam exam = new Exam();
qArrList = exam.getExam();
int count=0;
for(Question q : qArrList){
count++;
JLabel questionLabel = new JLabel(count+". "+q.getQustion()); //swing constant ?
ArrayList<String> ansRand = q.getAllRandomAns();
a1 = new JRadioButton(ansRand.get(0));
a2 = new JRadioButton(ansRand.get(1));
a3 = new JRadioButton(ansRand.get(2));
a4 = new JRadioButton(ansRand.get(3));
add(questionLabel);
add(a1);add(a2,gbc);add(a3);add(a4);
ButtonGroup radioGroup = new ButtonGroup(); //logical relationship
radioGroup.add(a1);radioGroup.add(a2);radioGroup.add(a3);radioGroup.add(a4);
}
//buttons:
JButton checkMe = new JButton("Check Exam");
JButton refresh = new JButton("Start Over");
ButtonHandler handler = new ButtonHandler();
checkMe.addActionListener(handler);
refresh.addActionListener(handler);
add(checkMe);
add(refresh);
}
/** Listens to the radio buttons. */
public class ButtonHandler implements ActionListener
{
public void actionPerformed (ActionEvent e) {
if(e.getActionCommand().equals("Start Over")){ //id?
//how to do this?
}
else{
RadioButtonHandler handler = new RadioButtonHandler();
a1.addItemListener(handler);
System.out.println("success?");
}
JOptionPane.showMessageDialog(ExamFrame.this, String.format("You pressed: %s", e.getActionCommand()));
}
public void itemStateChanged(ItemEvent e) //can i add it here?
{
JOptionPane.showMessageDialog(ExamFrame.this, String.format("yes?"));
System.out.println("success!");
}
}
public class RadioButtonHandler implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
JOptionPane.showMessageDialog(ExamFrame.this, String.format("radio state changed"));
}
}
}
why "itemStateChanges" isn't triggered, i tried to put it in the inner
class "ButtonHandler" and also in "RadioButtonHandler" Im having
troubles with it, what is the right way to do it? I want to trigger
and check the marked JRadioButtons after the user click the "check"
button.
ButtonHandler is implemented with ActionListener only:
public class ButtonHandler implements ActionListener{}
The itemStateChanged(ItemEvent) function belongs to ItemListener. This function is triggered if state of a source component to which this listener is registered gets changed. So implement the ItemListener. However, one more thing to note, that JButton doesn't respond to ItemListener but JRadioButton will. Because this Item events are fired by components that implement the ItemSelectable interface. Some example of such components are: check boxes, check menu items, toggle buttons and combo boxes including Radio Buttons as mentioned above.
What is the right way to check which button was clicked, i feel like
comparing the strings is wrong programming. Maybe using an ID
Well using the event source function: e.getSource(), check whither the type of the source is your expected type and cast it to appropriate type. And then you can use getName(String) function and check the name you were expecting. Of-course you should assign the name using setName(String) after initialization of component. Or using the component reference directly if it is declared in the Class context and you have direct access to the component.
#Override
public void itemStateChanged(ItemEvent e) {
if(e.getSource() instanceof JCheckBox)
{
JCheckBox checkBox = (JCheckBox)e.getSource();
if(checkBox.getName().equals("expectedName"))
; // do my thing
}
}
How should i make a "reset" button(start over), i want to uncheck all
radio buttons and run the constructor once again.
Well you are working with ButtonGroup. And ButtonGroup has a nice function: clearSelection() to help with whatever(I could not understand the part: run the constructor part) you want.
Edit: As you wanted me to see an ItemListener implemented class, Yes i can see that But:
i can not see that you have actually registered an instance of that class(a1.addItemListener(handler);) to any component before performing any action on the component to which ButtonHandler is registered to: checkMe, refresh
In addition to that, in this action performed function, you are checking with
action command, which you haven't even set with JButton.setActionCommand(String) function. You should not assign a (Item)listener depending on event-occurrence of another (Action)listener.
Tutorial:
How to Write an ItemListener
How to Write an ActionListener
How to Use the ButtonGroup Component
I've got a JFrame that looks like this:
It's got two JTextFields on it, one JComboBox between them and a JPanel at the bottom (that you can't see).
One of the features of the JComboBox is that it can be given a custom editor. These implement the ComboBoxEditor interface. In each of the following three cases, the GUI looks exactly the same, and I would have expected them all to behave exactly the same:
I do not specify a custom editor, and use the default one.
I create a custom editor whose editor component is a JTextField.
I create a custom editor whose editor component is a JPanel with a JTextField on it (using a BorderLayout).
When the editor for the editable combo box is set to the default, pressing Tab moves the focus from the top JTextField into the editing area on the JComboBox and then into the other JTextField. If I create a custom editor whose editor component is a JTextField and otherwise does what you would expect, the same thing happens.
BUT, if I instead create a custom editor whose editor component is a JPanel with a JTextField added to it, the focus makes one additional stop. If the focus is on the top JTextField, then pressing Tab moves the focus to the little arrow at the right of the editable combo box before moving into the text area.
Why is this happening? The focus never moves on to the JPanel at the bottom of the frame, so why does the presence of a JPanel holding the JTextField affect the tab order on the combo box?
The following is an S(-ish)SCCE, which has one text field and all three types of combo box on it:
import javax.swing.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
public class ComboBoxTest extends JFrame
{
private JPanel layoutPanel;
private JTextField meaninglessTextField;
private JComboBox defaultEditorComboBox;
private JComboBox textFieldEditorComboBox;
private JComboBox panelEditorComboBox;
public ComboBoxTest()
{
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
layoutPanel = new JPanel();
layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));
meaninglessTextField = new JTextField();
defaultEditorComboBox = new JComboBox(); // Just a default JComboBox.
defaultEditorComboBox.setEditable(true);
textFieldEditorComboBox = new JComboBox();
textFieldEditorComboBox.setEditable(true);
textFieldEditorComboBox.setEditor(new TextFieldEditor());
panelEditorComboBox = new JComboBox();
panelEditorComboBox.setEditable(true);
panelEditorComboBox.setEditor(new PanelEditor());
layoutPanel.add(Box.createRigidArea(new Dimension(500,0)));
layoutPanel.add(meaninglessTextField);
layoutPanel.add(defaultEditorComboBox);
layoutPanel.add(textFieldEditorComboBox);
layoutPanel.add(panelEditorComboBox);
Container contentPane = getContentPane();
contentPane.add(layoutPanel, BorderLayout.CENTER);
pack();
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run()
{
new ComboBoxTest().setVisible(true);
}
});
}
private class PanelEditor extends JPanel implements ComboBoxEditor
{
public JTextField inputTextField = new JTextField();
public PanelEditor()
{
setLayout(new BorderLayout());
add(inputTextField, BorderLayout.CENTER);
}
#Override
public String getItem()
{
return inputTextField.getText();
}
#Override
public void setItem(Object newText)
{
if (newText != null) {
inputTextField.setText(newText.toString());
}
else {
inputTextField.setText("");
}
}
#Override
public Component getEditorComponent()
{
return this;
}
#Override
public void removeActionListener(ActionListener listener)
{
inputTextField.removeActionListener(listener);
}
#Override
public void addActionListener(ActionListener listener)
{
inputTextField.addActionListener(listener);
}
#Override
public void selectAll()
{
inputTextField.selectAll();
}
}
private class TextFieldEditor extends PanelEditor implements ComboBoxEditor
{
// The same, except that the editor component is now just the JTextField
// rather than the whole panel.
public TextFieldEditor()
{
}
#Override
public JTextField getEditorComponent()
{
return inputTextField;
}
}
}
Note: this behaviour becomes a problem if I want to add a JLabel to the editor. Then I have to put a JPanel there to hold both the label and the text field.
The basic problem is that the combo's ui delegate can't handle compound editor components. There are several places where it assumes that the editor component is the target of whatever configuration it needs to do. The concrete mis-behaviour here is that it explicitly sets the editor's focusability to that of the combo itself
// in BasicComboBoxUI
protected void configureEditor() {
....
editor.setFocusable(comboBox.isFocusable());
....
]
The implications
by default, the panel's focusable is true because the combo's is true
forcing the panel's focusable to false in its constructor has no effect (the ui resets it later on and whenever the LAF is switched)
disabling combo's focusable disables the panel's as well
To fix on the level of the editor, you can implement its isFocusable to return false unconditionally:
private class PanelEditor extends JPanel implements ComboBoxEditor
public boolean isFocusable() {
return false;
}
...
}
An aside: for code hygiene, better not extend a view to implement its role as ComboBoxEditor (even though here you need a subclassed JPanel to avoid the problem, so it's arguably borderline :-) - instead implement the editor and let it use the tweaked panel.
Also beware that you might stumble into more problems with the compound editor (check the code of BasicComboUI for more places where it assumes a plain childless component), so you might consider not doing it at all but think of a different way to achieve your requirement.
Try this:
public PanelEditor()
{
// other code...
addFocusListener(new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e)
{
inputTextField.requestFocusInWindow();
}
});
}
The focus isn't transferring to the JPanel; it's transferring to the JComboBox itself.
You can stop a component from receiving the focus by using its setFocusable method. If you add the line
setFocusable(false)
to the constructor of the PanelEditor in the example above, then the strange behaviour is still there, since the PanelEditor implements JPanel, so the setFocusable method of the JPanel overrides that of the JComboBox. Since the setFocusable method of a JPanel essentially does nothing, nothing changes.
If instead you add the line
panelEditorComboBox.setFocusable(false)
to the constructor of the JFrame itself then the JComboBox will not be able to receive the focus, but the JTextField inside the editor will. This isn't a perfect fix, since it would be better if the editor itself were responsible for turning off the focusability of the JComboBox, so you could always pass in the parent JComboBox as a parameter to the constructor of the editor, and have the focusability turned off there.
I don't know why the behaviour is different when you've got a JTextField as the editor. Some weird Swing thing.
I was wondering if there is a way or a method that allows me to get the current JEditorPane being displayed. For example I have a JFrame where I can create several tabs. Whenever a tab is created a new JEditorPane object is created and the content of that pane are displayed in the tab. I've implemented a ChangeListener that currently just gets me the index of the current tab whenever I open a new one, close one or navigate between tabs. What I want to do is whenever a new tab is opened or navigated to I want to get the current JEditorPane object that resides at this tab. Is there any way in which I can achieve that?
Sorry if the question is a bit vague.
Thanks in advance.
The best way to do this would be to subclass JPanel and add your custom JPanel to the tabbed pane instead:
public class EditorPanel extends JPanel {
private JEditorPane editorPane;
// ...
public EditorPanel() {
// ...
editorPane = new JEditorPane( ... );
super.add(editorPane);
// ...
}
// ...
public JEditorPane getEditorPane() {
return editorPane;
}
}
Adding a new tab:
JTabbedPane tabbedPane = ... ;
tabbedPane.addTab(name, icon, new EditorPanel());
And then when you need to access it using the tabbed pane:
Component comp = tabbedPane.getComponentAt(i);
if (comp instanceof EditorPanel) {
JEditorPane editorPane = ((EditorPanel) comp).getEditorPane();
}
This is a better alternative to maintaining a separate list and trying to maintain it alongside the tabbed pane's indices.
I am having trouble designing GUI's in an object oriented manner. The following code will help me express my question more clearly:
import javax.swing;
import java.awt.*;
import java.awt.event.*;
public class QuoteOptionsPanel extends JPanel
{
private JLabel quote;
private JRadioButton comedy, philosophy, carpentry;
private String comedyQuote, philosophyQuote, carpentryQuote;
//-----------------------------------------------------------------
// Sets up a panel with a label and a set of radio buttons
// that control its text.
//-----------------------------------------------------------------
public QuoteOptionsPanel()
{
comedyQuote = "Take my wife, please.";
philosophyQuote = "I think, therefore I am.";
carpentryQuote = "Measure twice. Cut once.";
quote = new JLabel (comedyQuote);
quote.setFont (new Font ("Helvetica", Font.BOLD, 24));
comedy = new JRadioButton ("Comedy", true);
comedy.setBackground (Color.green);
philosophy = new JRadioButton ("Philosophy");
philosophy.setBackground (Color.green);
carpentry = new JRadioButton ("Carpentry");
carpentry.setBackground (Color.green);
ButtonGroup group = new ButtonGroup();
group.add (comedy);
group.add (philosophy);
group.add (carpentry);
QuoteListener listener = new QuoteListener();
comedy.addActionListener (listener);
philosophy.addActionListener (listener);
carpentry.addActionListener (listener);
add (quote);
add (comedy);
add (philosophy);
add (carpentry);
setBackground (Color.green);
setPreferredSize (new Dimension(300, 100));
}
//*****************************************************************
// Represents the listener for all radio buttons.
//*****************************************************************
private class QuoteListener implements ActionListener
{
//--------------------------------------------------------------
// Sets the text of the label depending on which radio
// button was pressed.
//--------------------------------------------------------------
public void actionPerformed (ActionEvent event)
{
Object source = event.getSource();
if (source == comedy)
quote.setText (comedyQuote);
else
if (source == philosophy)
quote.setText (philosophyQuote);
else
quote.setText (carpentryQuote);
}
}
}
The above code simply creates a panel with three radio buttons, each corresponding to a quote. It also creates a label which displays a quote. Whenever a button is selected, the text in the label is set to the corresponding quote. I understand this code just fine. I run into trouble trying to modify it. Let's say I want to create the same program, but with the radio buttons stacked vertically on top of one another. Let's also say that I decide to go about this by adding the radio buttons to a panel with a BoxLayout, which I define in its own BoxPanel class. (I would then add the BoxPanel to my QuoteOptionsPanel, which would still contain my quote JLabel.)
So my BoxPanel code might look something like this:
import java.awt.*;
import javax.swing.*;
public class BoxPanel extends JPanel
{
private JRadioButton comedy, philosophy, carpentry;
public BoxPanel()
{
setLayout (new BoxLayout (this, BoxLayout.Y_AXIS));
setBackground (Color.green);
comedy = new JRadioButton ("Comedy", true);
comedy.setBackground (Color.green);
philosophy = new JRadioButton ("Philosophy");
philosophy.setBackground (Color.green);
carpentry = new JRadioButton ("Carpentry");
carpentry.setBackground (Color.green);
ButtonGroup group = new ButtonGroup();
group.add (comedy);
group.add (philosophy);
group.add (carpentry);
QuoteListener listener = new QuoteListener();
comedy.addActionListener (listener);
philosophy.addActionListener (listener);
carpentry.addActionListener (listener);
}
//*****************************************************************
// Represents the listener for all radio buttons.
//*****************************************************************
private class QuoteListener implements ActionListener
{
//--------------------------------------------------------------
// Sets the text of the label depending on which radio
// button was pressed.
//--------------------------------------------------------------
public void actionPerformed (ActionEvent event)
{
Object source = event.getSource();
I do not know what to do here.
}
}
}
So as you can see, I did not know how to define my QuoteListener class. I want it to perform the same function as in the original program I posted, but am unsure of how to make it do so. The label which displays the quote is located in QuoteOptionsPanel, so I do not have access to it. In essence I am asking for the optimal way to change a label on one panel with an event listener belonging to a component on a different panel. I would be immensely grateful for any help you may be able to provide. Please let me know if I have not expressed my question clearly enough.
There are several ways to solve this, but for most all the key is to get and use references. Say the class that holds the JLabel as a private field has a public method,
public void setQuoteLabelText(String text) {
quoteLabel.setText(text);
}
Then you have to pass the reference to the visualized object of this class to your BoxPanel class, either through a constructor parameter or a setXXX(...) setter method. Then your ActionListener can call methods on the object of this class.
1. You can create an instance of the class whose private instance variable you need to
access.
2. Follow the one of the many use of Encapsulation, that is to have private Instance variable
and public getter-setter for that instance variable.
3. Now you can access the private member, by calling the public method on the instance of
the class.
4. One more thing, try using the Group Layout created by NetBeans team in 2005. Use the Window Builder Pro, now free from google.