I'm getting to grips with Java again and playing with Swing. Currently trying to understand JList.
When selecting items in a JList, I enable a button (the "Remove" button). When there's no selection, the button should be disabled (can't remove an item if it's not selected, the JVM tends to vomit if you don't have an item index).
When adding a list item through a JTextField default action (enter), the button enables but there's no selection in the JList. The code to enable the button doesn't even run. How is this happening? Maybe I'm missing a listener somewhere?
All the listeners are in the main class, but it doesn't seem to be a problem with the others. I can't see the class instantiating twice, so it's not that. Any ideas would be great?
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.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class GUI extends JFrame implements Runnable, ActionListener, DocumentListener, ListDataListener, ListSelectionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
//declare and instantiate interface components
DefaultListModel<String> lm=new DefaultListModel<String>();
JList<String> jl=new JList<String>(lm);
JTextField jt=new JTextField(50);
JButton b_add=new JButton("Add");
JButton b_del=new JButton("Remove");
#Override
public void run() {
//FROM RUNNABLE INTERFACE
//set GUI (JFrame) attributes
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLocationByPlatform(true);
setName("List of Lists");
setSize(1280, 500);
setLayout(new FlowLayout());
//set component attributes
jl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); //single selection list
jt.getDocument().addDocumentListener(this);
//other interface components
JScrollPane sp=new JScrollPane(jl);
JButton b_exit=new JButton("Exit");
//other component attributes
sp.setSize(500, 500);
b_add.setEnabled(false);
b_del.setEnabled(false);
//set component listeners
b_add.addActionListener(this);
b_del.addActionListener(this);
b_exit.addActionListener(this);
jt.setActionCommand("TXT");
jt.addActionListener(this);
lm.addListDataListener(this);
jl.addListSelectionListener(this);
//add GUI components
add(jt);
add(sp);
add(b_add);
add(b_del);
add(b_exit);
//set GUI visible
setVisible(true);
}
//FROM ACTIONLISTENER INTERFACE
#Override
public void actionPerformed(ActionEvent ae) {
//default action on text field (enter key)
if (ae.getActionCommand()=="TXT") {
lm.addElement(jt.getText());
jt.setText(null);
} else {
//actions from buttons
switch (ae.getActionCommand()) {
//add button
case "Add":
lm.addElement(jt.getText());
jt.setText(null);
break;
//remove button
case "Remove":
if (jl.getSelectedIndex()>=0) {
lm.remove(jl.getSelectedIndex());
}
break;
//exit button
case "Exit":
setVisible(false);
dispose();
break;
//just in case
default:
break;
}
}
}
//FROM DOCUMENTEVENTLISTENER INTERFACE
#Override
public void changedUpdate(DocumentEvent de) {
b_add.setEnabled(de.getDocument().getLength()>0);
}
#Override
public void insertUpdate(DocumentEvent de) {
b_add.setEnabled(de.getDocument().getLength()>0);
}
#Override
public void removeUpdate(DocumentEvent de) {
b_add.setEnabled(de.getDocument().getLength()>0);
}
//FROM LISTDATALISTENER INTERFACE
#Override
public void contentsChanged(ListDataEvent ld) {
b_del.setEnabled(lm.getSize()>0);
}
#Override
public void intervalAdded(ListDataEvent ld) {
b_del.setEnabled(lm.getSize()>0);
}
#Override
public void intervalRemoved(ListDataEvent ld) {
b_del.setEnabled(lm.getSize()>0);
}
//FROM LISTSELECTIONLISTENER INTERFACE
#Override
public void valueChanged(ListSelectionEvent ls) {
b_del.setEnabled(jl.getSelectedIndex()>=0);
}
}
What should happen is that the "Remove" button should only enable when an item in the JList is highlighted (selected).
What should happen is that the "Remove" button should only enable when an item in the JList is highlighted (selected).
Then you have too many listeners in your code.
There is no need for the DocumentListener. It doesn't matter if the data in the text field is changed. When you add the data from the text field to the JList, the selection does not change automatically.
There is no need for the ListDataListener. Again, you don't care if the data changes.
The only listener that is relevant to this requirement is the ListSelectionListener. This listener will fire an event when the selection changes. Then in the listener code you enable/disable the button based on the selection in the list.
Basic code in the listener might be:
int index = list.getSelectedIndex();
button.setEnabled( index = -1 ? false : true );
Read the section from the Swing tutorial on How to Write a ListSelectionListener for more information and a working example.
Just add b_del.setEnabled(false); in Add case.
//add button
case "Add":
lm.addElement(jt.getText());
jt.setText(null);
b_del.setEnabled(false);
break;
Also, I recommend using system.exit(0) instead of dispose (). For memory reasons only (Obviously, it depends on your needs :) ).
Related
This application shows a frame that contains different JComboBoxes and a JLabel.
An event should be generated when the user clicks the last one(style combobox) and the text in the JLabel should be formatted according to the selected choices in each combobox.
When I click on the Style combobox nothing happens.
There's also another error that I couldn't figure out:(
OUTPUT
package labtasksix;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Color;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class MyFrame extends JFrame {
String NameO []= {"Select name:","TimesRoman","Serif","SansSerif","Monospaced"};
String ColorO[]={"Select color:","RED","BLUE","GREEN"};
String SizeO []={"Select size:","8","12","16","20"};
String StyleO[]={"Select style:","BOLD","ITALIC","PLAIN"};
JLabel lbl= new JLabel("Text Formatted");
JComboBox Name= new JComboBox(NameO);
JComboBox Colour= new JComboBox(ColorO);
JComboBox Size= new JComboBox(SizeO);
JComboBox Style= new JComboBox(StyleO);
public MyFrame() {
super("Format Frame");
setLayout(new FlowLayout());
add(Name);
add(Size);
add(Style);
add(Colour);
add(lbl);
Name.setMaximumRowCount(3);
Size.setMaximumRowCount(3);
Style.setMaximumRowCount(3);
Colour.setMaximumRowCount(3);
EventHandler handler= new EventHandler();
Style.addItemListener(handler);
}
class EventHandler implements ItemListener{
#Override
public void itemStateChanged(ItemEvent e) {
//When user chooses from the last combobox (style)
if(e.getSource()==Style)
{
if(Name.getSelectedItem().equals("BOLD"))
{
lbl.setFont(new Font((String)Name.getSelectedItem(),Font.BOLD, (int) Size.getSelectedItem()));
}
if(Name.getSelectedItem().equals("ITALIC"))
{
lbl.setFont(new Font((String)Name.getSelectedItem(),Font.ITALIC, (int) Size.getSelectedItem()));
}
if(Name.getSelectedItem().equals("PLAIN"))
{
lbl.setFont(new Font((String)Name.getSelectedItem(),Font.PLAIN, (int) Size.getSelectedItem()));
}
if(Colour.getSelectedItem().equals("RED"))
{
lbl.setForeground(Color.red);
}
if(Colour.getSelectedItem().equals("BLUE"))
{
lbl.setForeground(Color.BLUE);
}
if(Colour.getSelectedItem().equals("GREEN"))
{
lbl.setForeground(Color.GREEN);
}
}
}
}
}
It is listening, but you're checking the ComboBox.getSelectedItem(), which hasn't been updated at the time the event is fired. The item the event relates to is referenced in the event itself; call e.getItem() to retrieve it:
Object item = e.getItem();
if (item.equals("BOLD")) {
lbl.setFont(new Font((String) item, Font.BOLD, /* wrong: (int) Size.getSelectedItem() */ 8));
}
Your size calculation (commented above) is wrong too. Your size selection box holds Strings, so you'll have to parse them (or change the model to ints).
Also... you'll get two events for each change, ItemEvent.DESELECTED first (for the old item), then ItemEvent.SELECTED. You should check for the event you're interested in:
if (e.getSource() == Style && e.getStateChange() == ItemEvent.SELECTED) {
I've been making a program, and one part of it is the ability to choose an item from a JList and have it display a specific icon in a JLabel. I've made it work so that the user has to select the item from the list and then press a button to initiate the action.
I'm just wondering if there's any way to make it so the button isn't necessary? In other words, the user simply has to click the list item and it immediately initiates the action.
Here is my code:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class Example extends JFrame
{
JList list = null;
Example()
{
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
ArrayList data = new ArrayList();
data.add("Py");
data.add("Piper");
list = new JList(data.toArray());
list.addListSelectionListener(new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent evt)
{
// To avoid double value selected
if (evt.getValueIsAdjusting())
return;
System.out.println("Selected: " + list.getSelectedValue());
}
});
cp.add(new JScrollPane(list), BorderLayout.CENTER);
}
public static void main(String[] s)
{
Example l = new Example();
l.pack();
l.setVisible(true);
}
}
So right here:
list.addListSelectionListener(new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent evt)
{
// To avoid double value selected
if (evt.getValueIsAdjusting())
return;
System.out.println("Selected: " + list.getSelectedValue());
}
});
So what you are actually doing is to "plug" a ListSelectionListener to your JList so that can be notified for any events that occur to the JList. Within the valueChanged you add the code need to be executed when the user select something in the JList.
There is this wonderful resource with the JList JavaDoc.
I have two classes mainpanel.java and subpanel.java. The subpanel.class contains a checkbox and some labels. I want to change the setSelected() and setText() of these components when i click some buttons in the mainpanel.java .
I have created a method in subpanel.java which i call from mainpanel.java and pass the boolean values.
public void schedulerchange(boolean check){
System.out.println("checked"+check);
scheduleenabler.setEnabled(check);
scheduleenabler.setSelected(check);
scheduleinfo.setText("Scheduler in On");
//subpanel21.updateUI();
}
When i call this function from mainpanel.java the function is called but the values don't change unless i make jcheckbox and jlabel static. But from what i learned we should not use static components unless very necessary.
Is there some other way to change the components?
If I have understood your question then I think you want to write a separate ActionListener class and perform action there which will enable or disable the JCheckBox in the UI-class. The below code shows that. Pass your checkbox reference to that PerformAction class and make it enabled or disabled by clicking on the button.
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainClass {
MainClass() {
JFrame jfrm = new JFrame("JTable Demo");
jfrm.setLayout(new FlowLayout());
jfrm.setSize(460, 180);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JCheckBox check = null;
// Get the Panel from the subclass;
JPanel panel = new CheckBox().getCheckBoxPanel();
// From the compoenents present in the panel get the CheckBox compoenent.
for(int i = 0; i < panel.getComponentCount(); i++) {
if(panel.getComponent(i) instanceof JCheckBox) {
check = (JCheckBox) panel.getComponent(i);
}
}
JButton button = new JButton("Click");
// Pass the CheckBox Compoenent to the ActionListener.
button.addActionListener(new PerformAction(check));
jfrm.add(button);
jfrm.add(panel);
jfrm.setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MainClass();
}
});
}
}
class PerformAction implements ActionListener {
JCheckBox check = null;
public PerformAction(JCheckBox checkBox) {
check = checkBox;
}
#Override
public void actionPerformed(ActionEvent e) {
boolean checkStatus = check.isSelected();
if(checkStatus == true) {
check.setEnabled(false);
check.setSelected(false);
} else {
check.setEnabled(true);
check.setSelected(true);
}
}
}
class CheckBox {
public JPanel getCheckBoxPanel() {
JPanel checkPanel = new JPanel();
JCheckBox check = new JCheckBox();
checkPanel.add(new JLabel("CheckBox"));
checkPanel.add(check);
return checkPanel;
}
}
This is not an appropriate use of updateUI(), which "Resets the UI property to a value from the current look and feel." Using revalidate(), as suggested in a comment, would be helpful only if components are added to, or removed from, the enclosing Container. Instead, invoke repaint() directly on the sub-panel instance. For greater flexibility, use the observer pettern suggested here.
Addendum: This example use Action to encapsulate the button's behavior. Because the checkbox's selected state is a bound property, the component is repainted automatically, but you can invoke repaint() explicitly if needed.
Addendum: Update to pass a reference as a parameter.
Addendum: In this variation, the parameter is a reference to the exported Action.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see https://stackoverflow.com/a/14412516/230513 */
public class Example {
private void display() {
JFrame f = new JFrame("Example");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(0, 1));
JPanel panel = new JPanel();
final JCheckBox check = new JCheckBox("Check");
Action checkAction = new AbstractAction("Update") {
#Override
public void actionPerformed(ActionEvent e) {
check.setSelected(!check.isSelected());
}
};
panel.add(check);
f.add(panel);
f.add(new SubPanel(checkAction));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class SubPanel extends JPanel {
public SubPanel(final Action action) {
this.add(new JButton(action));
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Example().display();
}
});
}
}
I have a JList, where i am displaying some ID's. I want to capture the ID the user clicked and dis play it on a JLabel.
String selected = jlist.getSelectedItem().toString();
The above code gives me the selected JList value. But this code has to be placed inside a button event, where when i click the button it will get the JList value an assign it to the JLabel.
But, what i want to do is, as soon as the user clicks an item of the JList to update the JLabel in real time. (without having to click buttons to fire an action)
A simple example would be like below using listselectionlistener
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class JListDemo extends JFrame {
public JListDemo() {
setSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
final JLabel label = new JLabel("Update");
String[] data = { "one", "two", "three", "four" };
final JList dataList = new JList(data);
dataList.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent arg0) {
if (!arg0.getValueIsAdjusting()) {
label.setText(dataList.getSelectedValue().toString());
}
}
});
add(dataList);
add(label);
setVisible(true);
}
public static void main(String args[]) {
new JListDemo();
}
}
Why don't you put a ListSelectionListener on your JList, and add your above code in to it.
I'm assuming you already know how to create listeners on JButtons, based on your question, so you just need to tweak it to create a ListSelectionListener instead, then assign the listener to your JList using jlist.addListSelectionListener(myListener);
There is a nice tutorial here that should get you started, or refer to the documentation
You should be aiming for something like this...
jlist.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()){
JList source = (JList)event.getSource();
String selected = source.getSelectedValue().toString();
}
}
});
Use a ListSelectionListener:
JList list = new JList(...);
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
if (!evt.getValueIsAdjusting()) {
// code here
}
}
});
Right now when a user right clicks on a selected JList item in my program the resulting JPopupMenu clears the selection (at least visually) until the popup menu is closed. This isn't consistent with the native look and feel of any platform I know of. The item should stay visually selected or have a selected-color border around it. But I can't find anythin in the API about popup menus changing selection appearance. Is there any way I can control this behavior?
How are you implementing your Mouse Listener that shows the popup? I have created a test application to demonstrate the behaviour of List selections and popup menus that I would typically expect. On Windows with Java 1.5/6 this behaves correctly.
Maybe this will help you with your particular problem.
package jlist;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class Test extends JPanel implements ListSelectionListener {
private static final String ACTION_FEED = "Feed";
private JList list;
private JPopupMenu menu;
// Initialise a JList and add to JPanel.
public Test() {
super(new BorderLayout());
list = new JList(new Object[]{"Badger", "Ferret", "Stoat", "Weasel"});
initActions();
list.addListSelectionListener(this);
// Add mouse listener
list.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) showPopup(e);
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) showPopup(e);
}
private void showPopup(MouseEvent e) {
menu.show(e.getComponent(), e.getX(), e.getY());
}
});
add(new JScrollPane(list), BorderLayout.CENTER);
valueChanged(null);
}
// Triggered when List Selection changes. Used to control Actions enabled state.
public void valueChanged(ListSelectionEvent e) {
boolean selected = list.getSelectedValue() != null;
getActionMap().get(ACTION_FEED).setEnabled(selected);
}
// Initialise Actions and Popup Menu
private void initActions() {
menu = new JPopupMenu();
Action feed = new AbstractAction(ACTION_FEED) {
public void actionPerformed(ActionEvent e) {
String value = (String) list.getSelectedValue();
JOptionPane.showMessageDialog(Test.this, "Fed " + value);
}
};
getActionMap().put(ACTION_FEED, feed);
menu.add(feed);
}
public static void main(String [] args) {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new Test());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}