Input hint disappearing on mouseclick when component disabled - java

Thanks to the very helpful post by Bart Kiers in someone else's post, I have managed to get my input hints working. Hurray! Unfortunately I can't seem to get a small glitch to stop occurring.
During the normal course of operation, the HintTextFields are disabled and enabled to allow or disallow edits. The input hint should only show when the field is empty and not selected, but when I left click the boxes, even when disabled and containing text, the text disappears in favor of the hint. Then, when I click on something else, I lose the contents entirely.
I have added an extra bit to the if then statements regarding gaining and losing focus to prevent it from running that code if the component is disabled, but the problem still occurs. It must be some small interaction with the JTextField class (or maybe FocusListener?). I have also tried getParent() in favor of this in that clause, in case I was misunderstanding the 'this' keyword, but that didn't help. The class below:
class HintTextField extends JTextField implements FocusListener {
private final String hint;
private boolean showingHint;
public HintTextField(final String hint) {
super(hint);
this.hint = hint;
this.showingHint = true;
super.addFocusListener(this);
}
#Override
public void focusGained(FocusEvent e) {
if(this.getText().isEmpty() && this.isEnabled()) {
super.setText("");
showingHint = false;
}
}
#Override
public void focusLost(FocusEvent e) {
if(this.getText().isEmpty() && this.isEnabled()) {
super.setText(hint);
showingHint = true;
}
}
#Override
public String getText() {
return showingHint ? "" : super.getText();
}
}
Original post if anyone wants to go upvote the wonderful Bart Kiers.
Java JTextField with input hint
Also, here is the initialization. This started as a JDeveloper gui.
startDateField.setHorizontalAlignment(javax.swing.JTextField.CENTER);
startDateField.setToolTipText(notes);
startDateField.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
startDateField.setMaximumSize(new java.awt.Dimension(2, 16));
startDateField.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
startDateFieldMouseClicked(evt);
}
});
private void startDateFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_startDateFieldMouseClicked
if (SwingUtilities.isRightMouseButton(evt)) {
editNote();
}
}
public void editNote() {
int selected = -1;
for (int a = 0; a < spinner.bitem.bldg.stallArray[1].length; a ++) {
if (spinner.bitem.bldg.stallArray[1][a].equals(stallName)) {
selected = a;
}
}
String oldNote = notes;
JTextField xField = new JTextField(oldNote);
xField.setPreferredSize(new Dimension(200,30));
JPanel myPanel = new JPanel();
myPanel.add(new JLabel("Note:"));
myPanel.add(xField);
int result =
JOptionPane.showConfirmDialog(this, myPanel, "Please enter note.",
JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
String note = xField.getText();
if (!note.equals(oldNote)) {
notes = note;
spinner.bitem.bldg.stallArray[9][selected] = notes;
spinner.bitem.master.sendNoteDataToSQL(spinner.bitem.bldg.buildingName, spinner.bitem.bldg.stallArray[1][selected], notes);
spinner.updateScreen();
}
}
}

Related

How can I prevent every click on the edit button from opening a JFrame to edit the selected row?

I am using 2 different classes: one holding a main JFrame with an edit JButton and one holding an edit JFrame that is called when the button is pressed.
First i select a row from a jtable for edit. After i press Edit button and a Jframe opens. If i press repeatedly the button, the same jframe are openning. So i want, after the first press of the button -> Jframe are openning and if i press again button I do not want to open the same frame again.
Here is a link with app image: https://ibb.co/gYfR9a
Here is my code for the Edit button:
JButton btnEdit = new JButton("Edit");
btnEdit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < table.getRowCount(); i++) {
Boolean chkDel = Boolean.valueOf(table.getValueAt(i, 0).toString());
if (chkDel) {
String id = table.getValueAt(i, 1).toString();
String num = table.getValueAt(i, 2).toString();
String pre = table.getValueAt(i, 3).toString();
String name = table.getValueAt(i, 4).toString();
String email = table.getValueAt(i, 5).toString();
EditFrame f = new EditFrame(Integer.valueOf(id), num, pre, name, email);
f.initFrame(Integer.valueOf(id), num, pre, name, email);
}
}
}
});
btnEdit.setBounds(150, 250, 90, 23);
getContentPane().add(btnEdit);`
And here is the code for the Edit Frame:
public class EditFrame extends JFrame {
private JPanel contentPane;
private JTextField idField;
private JTextField numField;
private JTextField preField;
private JTextField nameField;
private JTextField emailField;
private final JButton btnEdit = new JButton("Edit");
/**
* Launch the application.
*/
public void initFrame(int id, String num, String pre, String name, String email) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
EditFrame eframe = new EditFrame(id, num, pre, name, email);
eframe.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
In your actionPerformed method of your button you should memorize if it was already clicked by using a boolean. You can then do something like if(!wasClickedAlready) { ... }. However the boolean needs to be kept in the correct scope (one above the method). For example as member variable of your ActionListener or in the wrapping class, or something like this. Else the state of the boolean can not be memorized between method-calls.
For example see this snippet:
btnEdit.addActionListener(new ActionListener() {
private boolean wasClicked = false;
#Override
public void actionPerformed(ActionEvent e) {
if (wasClicked) {
// Do nothing if clicked already
return;
} else {
// The button was clicked for the first time
wasClicked = true;
}
for (int i = 0; i < table.getRowCount(); i++) {
// Your stuff
...
}
}
});
If your question refers to not opening a frame for the same table row again, then you need to memorize the rows you have already clicked, for example by using a table of boolean like boolean[] or a Map mapping the row-index to a boolean like HashMap<Integer, Boolean>. However the scheme remains the same. If that is the case, just tell me in the comments and I will show you another snippet.
Edit: You commented that you maximally one frame should be shown for each row, not for the whole table. As stated above you can apply the same scheme than before:
btnEdit.addActionListener(new ActionListener() {
private boolean[] wasClickedTable = new boolean[table.getRowCount()];
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < table.getRowCount(); i++) {
Boolean chkDel = Boolean.valueOf(table.getValueAt(i, 0).toString());
// A row should be processed
if (chkDel) {
// Lookup if row was already clicked before
if(wasClickedTable[i]) {
// It was, skip the row and do not process it
continue;
}
// The row was not clicked before
// However it is now, set it
wasClickedTable[i] = true;
// Further process the row
// Your stuff
...
}
}
}
});
I'm not sure if I understood correctly. So you have a button that opens a JFrame, but you don't want the JFrame to be opened every time the button is clicked? What is the expected behavior?

Unchecking checkbox does not undo command?

Its my first time programming checkboxes; I figured out how to make a checkbox appear, and do a command when checked. However, when the box is unchecked, instead of undoing the command, it instead does the command a second time. How can I undo the command when unchecking the box?
Code: (instantiation of checkboxes)
negA = new JCheckBox("Neg");
negA.addActionListener(this);
negA.setActionCommand("A-5");
tossupA = new JCheckBox("Tossup");
tossupA.addActionListener(this);
tossupA.setActionCommand("A10");
powerA = new JCheckBox("Power");
powerA.addActionListener(this);
powerA.setActionCommand("A05");
Command:
public void actionPerformed(ActionEvent e)
{
//get the String value from the button pressed
String result = e.getActionCommand();
char team = result.charAt(0);
//set text on screen to reflect new score
String screenText = "Team "+(team)+"'s total score for this tossup: ";
//add score to subtotal and show on screentext
if(team=='A'){
teamATempScore += Integer.parseInt(result.substring(1));
screenText += teamATempScore;
}
//and now for B
else if(team=='B'){
teamBTempScore += Integer.parseInt(result.substring(1));
screenText += teamBTempScore;
}
When the box is unchecked, I want the score to decrement by the amount that it was incremented, but instead the score just increments again :(
Thanks!
(yes, if you were wondering, this is a scorekeeping program for a game of Quizbowl) :D
The listener just checks to see if the checkBox was clicked -- It doesn't check if it went from unchecked to checked, or vice versa.
Use the .isSelected() method to determine whether the checkbox is checked or not after it becomes clicked.
For example:
if (negA.isSelected())
{
//the checkbox was checked after they clicked it, do something
}
else
{
//the checkbox was unchecked after they clicked it, do something else
}
You have to check the state of the control and then either increment or decrement. The compiler cannot auto generate the reverse of your code.
To check the state of the checkbox, you call the isSelected() method on it like in the example below.
import javax.swing.JOptionPane;
public class CheckboxExample extends javax.swing.JFrame {
private javax.swing.JCheckBox jCheckBox1;
public CheckboxExample() {
jCheckBox1 = new javax.swing.JCheckBox();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new java.awt.FlowLayout());
jCheckBox1.setText("CheckMe");
jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
jCheckBox1ActionPerformed(evt);
}
});
getContentPane().add(jCheckBox1);
pack();
}
// this will get called when the state of the checkbox changes
private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {
if(jCheckBox1.isSelected()) {
JOptionPane.showMessageDialog(this, "Checked", "Message",
JOptionPane.INFORMATION_MESSAGE);
}
else {
JOptionPane.showMessageDialog(this, "Unchecked", "Message",
JOptionPane.INFORMATION_MESSAGE);
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new CheckboxExample().setVisible(true);
}
});
}
}

JRadioButton navigation with arrow keys

I am trying to get a group of JRadioButtons to be navigable using the arrow keys. I was going to implement this manually with KeyListeners, but apparently this behavior is already supposed to work for at least the last 8 years (http://bugs.sun.com/view_bug.do?bug_id=4104452). However, it's not working for me: pressing the arrow keys does nothing. Java version is 7u45 on Windows.
A standalone test case to see what I'm talking about:
import java.awt.*;
import javax.swing.*;
public class Test {
public static void main(final String[] args) {
if (!EventQueue.isDispatchThread()) {
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
main(args);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
return;
}
try {
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Throwable t) {
throw new RuntimeException(t);
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ButtonGroup group = new ButtonGroup();
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
JRadioButton rb;
rb = new JRadioButton("Option A");
panel.add(rb);
group.add(rb);
rb = new JRadioButton("Option B");
panel.add(rb);
group.add(rb);
rb = new JRadioButton("Option C");
panel.add(rb);
group.add(rb);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
I have tried using different look & feels, different containers, and different layout managers, but it still does not work.
You need to add the right/left (up/down?) keys to the focus traversal policy of each radio button. For example to add the right/left arrow keys:
Set set = new HashSet( rb.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS ) );
set.add( KeyStroke.getKeyStroke( "RIGHT" ) );
rb.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, set );
set = new HashSet( rb.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS ) );
set.add( KeyStroke.getKeyStroke( "LEFT" ) );
rb.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, set );
Read the section from the Swing tutorial on How to Use the Focus Subsystem for more information.
I believe you can achieve your goal using KeyBindings instead of KeyListeners. In many cases bindings are actually recommended over KeyListeners, as the second ones can generate many problems (frame catching the key activity must be active one etc.)
Thank you everyone for the answers.
I discovered the reason for my confusion. Apparently, when the Sun bug report system says that a bug's status is "Closed" and its "Resolved Date" is "2005-07-19", that doesn't mean the bug is fixed at all. Apparently, it's just logged as a duplicate of some other (newer?) bug. Nearly 16 years since it was first reported it still isn't fixed. Whatever.
The needed behavior is much more subtle than I realized. I experimented in native Windows dialogs in various programs:
Most button-like components: buttons, checkboxes, and radio buttons, implement the arrow keys for focus navigation. In Java this corresponds to the AbstractButton class. (JMenuItem is also a subclass of that, but that has its own distinct arrow key behavior.)
Only radio buttons get selected/checked during this navigation.
Unfocusable (including disabled or invisible) components must be skipped.
Attempting to navigate before the first button in a group or after the last one is inconsistent: on some dialogs it loops from end to end; on others it moves irreversibly onto non-button components; and on yet others it does nothing. I experimented with all these different behaviors and none of them was particularly better than the others.
I implemented a looping behavior below as it felt slightly more fluent. The navigation silently skips past non-AbstractButton components, forming a sort-of separate focus cycle private to buttons. This is dubious but sometimes needed when a set of related checkboxes or radio buttons are mixed with other components. Testing for a common parent component to identify groups would also be a reasonable behavior, but that didn't work in one dialog where I'd used separate components purely for layout reasons (to implement a line break in a FlowLayout).
As suggested I studied up on InputMaps and ActionMaps instead of using a KeyListener. I've always avoided the maps as they seem overcomplicated but I guess I see the advantage of being able to easily override the binding.
This code uses an auxialiary look and feel to install the desired behavior for all AbstractButton components application-wide (which is a nice technique I found out about here). I've tested it with several different dialog boxes and windows and it seems to be okay. If it causes issues I'll update this post.
Call:
ButtonArrowKeyNavigation.install();
once at application startup to install it.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonArrowKeyNavigation {
private ButtonArrowKeyNavigation() {}
public static void install() {
UIManager.addAuxiliaryLookAndFeel(lookAndFeel);
}
private static final LookAndFeel lookAndFeel = new LookAndFeel() {
private final UIDefaults defaults = new UIDefaults() {
#Override
public javax.swing.plaf.ComponentUI getUI(JComponent c) {
if (c instanceof AbstractButton && !(c instanceof JMenuItem)) {
if (c.getClientProperty(this) == null) {
c.putClientProperty(this, Boolean.TRUE);
configure(c);
}
}
return null;
}
};
#Override public UIDefaults getDefaults() { return defaults; };
#Override public String getID() { return "ButtonArrowKeyNavigation"; }
#Override public String getName() { return getID(); }
#Override public String getDescription() { return getID(); }
#Override public boolean isNativeLookAndFeel() { return false; }
#Override public boolean isSupportedLookAndFeel() { return true; }
};
private static void configure(JComponent c) {
InputMap im = c.getInputMap(JComponent.WHEN_FOCUSED);
ActionMap am = c.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "focusPreviousButton");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "focusPreviousButton");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "focusNextButton");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "focusNextButton");
am.put("focusPreviousButton", focusPreviousButton);
am.put("focusNextButton", focusNextButton);
}
private static final Action focusPreviousButton = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
move((AbstractButton)e.getSource(), -1);
}
};
private static final Action focusNextButton = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
move((AbstractButton)e.getSource(), +1);
}
};
private static void move(AbstractButton ab, int direction) {
Container focusRoot = ab.getFocusCycleRootAncestor();
FocusTraversalPolicy focusPolicy = focusRoot.getFocusTraversalPolicy();
Component toFocus = ab, loop = null;
for (;;) {
toFocus = direction > 0
? focusPolicy.getComponentAfter(focusRoot, toFocus)
: focusPolicy.getComponentBefore(focusRoot, toFocus);
if (toFocus instanceof AbstractButton) break;
if (toFocus == null) return;
// infinite loop protection; should not be necessary, but just in
// case all buttons are somehow unfocusable at the moment this
// method is called:
if (loop == null) loop = toFocus; else if (loop == toFocus) return;
}
if (toFocus.requestFocusInWindow()) {
if (toFocus instanceof JRadioButton) {
((JRadioButton)toFocus).setSelected(true);
}
}
}
}
Here is my example of JRadioButtons can be navigable using the arrow keys(UP and Down) and modified few codes for you.
public class JRadioButton extends JPanel {
private JRadioButton[] buttons;
public JRadioButtonTest(int row) {
ButtonGroup group = new ButtonGroup();
buttons = new JRadioButton[row];
for (int i = 0; i < buttons.length; i++) {
final int curRow = i;
buttons[i] = new JRadioButton("Option " + i);
buttons[i].addKeyListener(enter);
buttons[i].addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
if (curRow > 0)
buttons[curRow - 1].requestFocus();
break;
case KeyEvent.VK_DOWN:
if (curRow < buttons.length - 1)
buttons[curRow + 1].requestFocus();
break;
default:
break;
}
}
});
group.add(buttons[i]);
add(buttons[i]);
}
}
private KeyListener enter = new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == KeyEvent.VK_ENTER) {
((JButton) e.getComponent()).doClick();
}
}
};
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JRadioButton(3));
frame.pack();
frame.setVisible(true);
}
}
The core implement method is calling requestFocus() on the correct JRadioButton when an arrow key is called. Extra KeyListener for when the Enter key is pressed.
You can use this KeyListener to your program and add more key.
Good luck!

Trouble with RadioButtons and booleans connection

Well its weird. I am not good with radiobuttons by the way. But I made a JPanel program in netbeans which includes a RadioButton. You enter all this information with JTextFields(no problem) and then lastly I had a JButton which you click the choice you want. Then I have a JButton that takes all the information and outputs this. For the RadioButton, I first entered the usual:
family = new JRadioButton("Family", true);
friend = new JRadioButton("Friend");
relative = new JRadioButton("Relative");
friendFriend = new JRadioButton("Friend of Friend");
ButtonGroup group = new ButtonGroup();
group.add (friend);
group.add (family);
group.add (relative);
group.add (friendFriend);
(I'm not sure if I needed a listner for the RadioButtons or not but my program still seems to "crash" no matter what).
then I had one action listner for the JButton which included all the textfields and radio buttons. But the RadioButton is the issue.
In the action listner I had:
Object source = event.getSource();
if (source == family)
relation1 = true;
else
if (source == friend)
relation2 = true;
else
if(source == relative)
relation3 = true;
else
if(source == friendFriend)
relation4 = true;
Then I made a relation class:
public class Relation {
private boolean arrayFamily, arrayFriend, arrayRelative, arrayFriendFriend;
public Relation(boolean relation1, boolean relation2, boolean relation3,
boolean relation4)
{
this.arrayFamily = relation1;
this.arrayFriend = relation2;
this.arrayRelative = relation3;
this.arrayFriendFriend = relation4;
}
public String relations ()
{
String relationship = null;
if(arrayFamily && !arrayFriend && !arrayRelative && !arrayFriendFriend == true)
{
relationship = "Family";
}
else
if(arrayFriend && !arrayFamily && !arrayRelative &&
!arrayFriendFriend == true)
{
relationship = "Friend";
}
else
if(arrayRelative && !arrayFamily && !arrayFriend &&
!arrayFriendFriend == true)
{
relationship = "Relative";
}
else
if(arrayFriendFriend && !arrayFamily && !arrayFriend &&
!arrayRelative == true)
{
relationship = "Friend of a Friend";
}
return relationship;
}
}
LASTLY back in the action listner, I implementer this class:
Relation relationship = new Relation(relation1, relation2, relation3
, relation4);
String arrayRelation = relationship.relations();
I lastly included arrayRelation in an array but the array worked fine.
My problem is that the output of the array for my RadioButtons keeps reading "null" (most likey because this code: String relationship = null;). I assume this means that none of my if else statements were satisfied and I really dont know why.
Also important to point out is that if I click submit without clicking any radio button (the button stays on "family"), it reads null. If I click a button once it works perfectly reading the string I intended. But if I click another button afterwards and click submit again, the string goes back to "null".
I know its lengthy but I would really appreciate any help because I am lost.
P.S. some parts of my code are repetitive because I was playing around trying to fix the problem.
I suggest you handle your action events separately, for example:
family.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
familyActionPerformed(evt);
}
});
Then implement familyActionPerformed(evt):
private void familyActionPerformed(java.awt.event.ActionEvent evt) {
// every click on family radio button causes the code here to be executed
relation1 = true;
}
Also write an event handler for the button you click, like this:
submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
// Here test the state of each radio button
relation1 = family.isSelected();
relation2 = friend.isSelected();
relation3 = relative.isSelected();
relation4 = friendFriend.isSelected();
}
MORE EDIT:
Doing what you're doing with NetBeans should be very easy. Here are tutorials that will clear it all up for you:
Tutorial 1
Tutorial 2
I explain the solution again:
Using 'family' button as an example, in your constructor where you have created and initialised your GUI components do this:
JRadioButton family = new JRadioButton();
// do any other thing you want to do to this button and finally..
family.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
familyActionPerformed(evt);
}
});
JButton submit = new JButton("Submit");
submit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
submitActionPerformed(evt);
}
});
Then somewhere create these methods:
private void familyActionPerformed(java.awt.event.ActionEvent evt){
// each time family is selected, you code processes the lines below:
...
}
private void submiteActionPerformed(java.awt.event.ActionEvent evt){
relation1 = family.isSelected();
relation2 = friend.isSelected();
relation3 = relative.isSelected();
relation4 = friendFriend.isSelected();
}
Do something similar for the rest of the RadioButtons.
I think that you're making things way too complex for yourself. If all you want is the String of the JRadioButton pressed, then use the ButtonGroup to get it for you. It can return the ButtonModel of the selected JRadioButton (if any one was selected), and from that you can extract the actionCommand String, although you'll have to remember to set this when you create your JRadioButton.
For example:
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class JRadioExample extends JPanel {
private static final String[] RADIO_TITLES = { "Family", "Friend",
"Relative", "Friend or Relative" };
private ButtonGroup btnGrp = new ButtonGroup();
public JRadioExample() {
for (int i = 0; i < RADIO_TITLES.length; i++) {
JRadioButton rBtn = new JRadioButton(RADIO_TITLES[i]);
rBtn.setActionCommand(RADIO_TITLES[i]); // ***** this is what needs to
// be set
btnGrp.add(rBtn);
add(rBtn);
}
add(new JButton(new BtnAction("Get Chosen Selection")));
}
private class BtnAction extends AbstractAction {
public BtnAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent evt) {
ButtonModel model = btnGrp.getSelection();
if (model != null) {
String actionCommand = model.getActionCommand();
System.out.println("Selected Button: " + actionCommand);
} else {
System.out.println("No Button Selected");
}
}
}
private static void createAndShowGui() {
JRadioExample mainPanel = new JRadioExample();
JFrame frame = new JFrame("JRadioExample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Tristate Checkboxes in Java

I could really use a tri-stated checkbox in Java. It sounds like a simple thing, but I've only seen really ugly implementations [note: link now broken].
Three radio buttons just take up too much real estate and will probably be confusing for the users in my case. It's basically for a search dialog. I need true, false or "don't care" options. Is there a different technique that people use?
Use a drop-down.
I found a way to make a tri-state checkbox by simply adding a listener:
public class TriStateActionListener implements ActionListener{
final protected Icon icon;
public TriStateActionListener(Icon icon){
this.icon=icon;
}
public static Boolean getState(javax.swing.JCheckBox cb){
if (cb.getIcon()==null) return null;
if (cb.isSelected()) return true;
else return false;
}
public void actionPerformed(ActionEvent e) {
javax.swing.JCheckBox cb=(javax.swing.JCheckBox)e.getSource();
if (!cb.isSelected()){
cb.setIcon(icon);
}
else if (cb.getIcon()!=null){
cb.setIcon(null);
cb.setSelected(false);
}
}
}
Then in the application code, it's just a single line:
jCheckBox1.addActionListener(new TriStateActionListener(getResourceMap().getIcon("TriStateIcon")));
After all the feedback, I'm thinking a drop-down may be a better choice. But, I wanted to share my code here for everyone else.
In this implementation the three state can be only set via programmatically. To be Look and Feel portable it use images, that must be placed inside the the same java package.
public class TristateCheckBox extends JCheckBox {
private static final long serialVersionUID = 1L;
private boolean halfState;
private static Icon selected = new javax.swing.ImageIcon(TristateCheckBox.class.getResource("selected.png"));
private static Icon unselected = new javax.swing.ImageIcon(TristateCheckBox.class.getResource("unselected.png"));
private static Icon halfselected = new javax.swing.ImageIcon(TristateCheckBox.class.getResource("halfselected.png"));
#Override
public void paint(Graphics g) {
if (isSelected()) {
halfState = false;
}
setIcon(halfState ? halfselected : isSelected() ? selected : unselected);
super.paint(g);
}
public boolean isHalfSelected() {
return halfState;
}
public void setHalfSelected(boolean halfState) {
this.halfState = halfState;
if (halfState) {
setSelected(false);
repaint();
}
}
}
Sample frame:
public class NewJFrame19 extends javax.swing.JFrame {
private final TristateCheckBox myCheckBox;
public NewJFrame19() {
myCheckBox = new TristateCheckBox();
myCheckBox.setText("123123");
add(myCheckBox);
jButton1 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new java.awt.FlowLayout());
jButton1.setText("jButton1");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
getContentPane().add(jButton1);
pack();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
myCheckBox.setHalfSelected(true);
}
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Windows".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(NewJFrame19.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(NewJFrame19.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(NewJFrame19.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame19.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame19().setVisible(true);
}
});
}
private javax.swing.JButton jButton1;
}
JIDE have open sourced some very nice functionality in their Common Layer, one of which happens to be a tristate checkbox
I would suggest that you go run the webstart demo to see if it meets your needs
That "ugly implementations" is an old link. One of the suggestions on that page was updated a couple of years ago. I haven't tested the old implementation, so I don't know if the new one is any better or worse.
TristateCheckBox Revisited
Tristate check-boxes are standard UI idiom for Treeviews with partially checked children nodes. They are widely used in layer management in complex hierarchial views such as Google Earth.
I dont know why anyone would give the solutions with additional icon png files while java api gives great funcionality for overriding paintIcon(..) method.
The best lightweight solution to remember CheckBox state is IMO ClientProperty attribute.
/*
* Tri-state checkbox example
* #s1w_
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class TCheckBox extends JCheckBox implements Icon, ActionListener {
final static boolean MIDasSELECTED = true; //consider mid-state as selected ?
public TCheckBox() { this(""); }
public TCheckBox(String text) {
super(text);
putClientProperty("SelectionState", 0);
setIcon(this);
addActionListener(this);
}
public TCheckBox(String text, int sel) {
/* tri-state checkbox has 3 selection states:
* 0 unselected
* 1 mid-state selection
* 2 fully selected
*/
super(text, sel > 1 ? true : false);
switch (sel) {
case 2: setSelected(true);
case 1:
case 0:
putClientProperty("SelectionState", sel);
break;
default:
throw new IllegalArgumentException();
}
addActionListener(this);
setIcon(this);
}
#Override
public boolean isSelected() {
if (MIDasSELECTED && (getSelectionState() > 0)) return true;
else return super.isSelected();
}
public int getSelectionState() {
return (getClientProperty("SelectionState") != null ? (int)getClientProperty("SelectionState") :
super.isSelected() ? 2 :
0);
}
public void setSelectionState(int sel) {
switch (sel) {
case 2: setSelected(true);
break;
case 1:
case 0: setSelected(false);
break;
default: throw new IllegalArgumentException();
}
putClientProperty("SelectionState", sel);
}
final static Icon icon = UIManager.getIcon("CheckBox.icon");
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
icon.paintIcon(c, g, x, y);
if (getSelectionState() != 1) return;
int w = getIconWidth();
int h = getIconHeight();
g.setColor(c.isEnabled() ? new Color(51, 51, 51) : new Color(122, 138, 153));
g.fillRect(x+4, y+4, w-8, h-8);
if (!c.isEnabled()) return;
g.setColor(new Color(81, 81, 81));
g.drawRect(x+4, y+4, w-9, h-9);
}
#Override
public int getIconWidth() {
return icon.getIconWidth();
}
#Override
public int getIconHeight() {
return icon.getIconHeight();
}
public void actionPerformed(ActionEvent e) {
TCheckBox tcb = (TCheckBox)e.getSource();
if (tcb.getSelectionState() == 0)
tcb.setSelected(false);
tcb.putClientProperty("SelectionState", tcb.getSelectionState() == 2 ? 0 :
tcb.getSelectionState() + 1);
// test
System.out.println(">>>>IS SELECTED: "+tcb.isSelected());
System.out.println(">>>>IN MID STATE: "+(tcb.getSelectionState()==1));
}
}
usage: TCheckBox tcb = new TCheckBox("My CheckBox");
I'd just use the one you posted.
As long as your complexity is in another class (that works) and it acts just like any other control, who cares? (That seems to be the assumption behind all of swing, most swing classes seem to be about this complicated.)
Change the UI. Tristate check-box is unusual and can really confuse users. The drop down is good option but for more then one occurrence within dialog it will also bring a lot of confusion to user.

Categories

Resources