Tristate Checkboxes in Java
Asked Answered
C

9

15

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?

Cytoplasm answered 11/8, 2009 at 22:25 Comment(0)
C
7

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.

Cytoplasm answered 12/8, 2009 at 17:15 Comment(0)
G
13

Use a drop-down.

Gelatinous answered 11/8, 2009 at 22:26 Comment(6)
+1 good idea. if a UI component is hard to code and nonstandard, it will feel that way to the user as well. When was the last time you used a tri-state checkbox in that way? I never have.Wield
Although I agree with @byron's point, a tri-state check box is actually fairly standard from a users point of view.Elgin
Which users? I'm a developer and power user, and I despise tri-state checkboxes, usually because it's not clear what the third state means; typically, it means some detail is being hidden from you. It's a usability and accessibility nightmare.Lvov
Most of the time it means that the checkbox applies to a class of things and that those things have different states and you don't want them changed. Checking it checks them all, leave it in the third state to not change it. Very common even for power users.Elgin
Typical usage of tri-state checkboxes: changing properties on multi object selection, grayed+selected third state meaning "some items have this property and some have not, and I won't change any of them". Most common in Microsoft applications.Epimenides
Most usefull use for a tristate is this: No state, On and off. When you use the standard 2state you only have on and off and when you want to make sure the user actually choose one you need the third state,Ultann
C
7

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.

Cytoplasm answered 12/8, 2009 at 17:15 Comment(0)
O
6

enter image description here

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.

enter image description here enter image description here enter image description here

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;
}
Omalley answered 9/4, 2013 at 9:18 Comment(1)
We used this approach for a checkbox denoting the aggregation of on/off states. Interaction toggles all on/off, the intermediate state is reachable only via interaction with the individual elements. Works quite nicely and I think it's more sound in terms of UI design.Bradfield
R
4

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.

Replication answered 30/9, 2009 at 7:14 Comment(0)
H
4

JIDE have open sourced some very nice functionality in their Common Layer, one of which happens to be a tristate checkbox

Tristate checkboxes

I would suggest that you go run the webstart demo to see if it meets your needs

Halle answered 24/3, 2012 at 11:43 Comment(0)
M
4

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.

enter image description here

/*
 * 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");

Mastoid answered 5/11, 2014 at 4:3 Comment(1)
So why are you using ClientProperty instead of a member variable? Where's the trick?Ithunn
T
3

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

Telex answered 11/8, 2009 at 23:57 Comment(1)
Yes, i think Heinz Kabutz, done this very well. I would also recommend that!Oily
E
0

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.)

Elgin answered 11/8, 2009 at 22:55 Comment(0)
C
0

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.

Compartmentalize answered 12/8, 2009 at 9:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.