【问题标题】:Java Swing - JCheckbox with 3 states (full selected, partially selected and deselected) [duplicate]Java Swing - 具有 3 种状态的 JCheckbox(完全选中、部分选中和取消选中)[重复]
【发布时间】:2011-02-11 17:01:15
【问题描述】:

我想要一个JCheckbox,它有如下所示的 3 个状态:

  1. 部分选择
  2. 未选中
  3. 全选

第一季度。我可以将JCheckbox 用于上述目的还是必须使用一些自定义摆动组件?

【问题讨论】:

  • 我考虑过使用相同的方法来过滤列表。在查看论坛后,我发现它不是常见的元素(树木除外)并且可能会使用户感到困惑。所以我重新考虑使用 JComboBox 作为我的过滤器,它并没有我最初想象的那么糟糕。

标签: java swing jcheckbox


【解决方案1】:

哦,是的,但是您必须创建一个自定义组件。

看看Java Specialist 中的这篇文章,看看它是否适合你。

不过,它需要在 OSX 中进行一些工作。

WinXP:

【讨论】:

  • @Oscar Reyes:我的答案中的链接包含revised version of this newsletter that works for Java 5 upwards :)
【解决方案2】:

这是一个 TristateCheckBox 的示例:

http://www.javaspecialists.co.za/archive/Issue145.html

【讨论】:

  • @Peter 两个问题:首先,我在你告诉我的网站上没有找到 TristateCheckBox 的 jar 文件的链接。我是否需要根据网站上提到的代码手动制作 jar(或类)?其次,复选框的选中状态和中间状态的外观几乎没有区别,普通最终用户无法快速弄清楚。有什么方法可以将它修改为我在问题中显示的那个。 WinXp 使用我在问题中展示的相同的中间外观。
  • @Yatendra Goel:没有 jar 文件,您必须自己使用文件(或构建自己的 jar)。由于 JCheckBox 不知道这第三个状态,所以在此示例中使用状态pressed 来呈现第三个状态,因此恐怕没有简单的方法可以更改图形表示以匹配标准 XP 样式。
  • 我会避免使用 setArmed/setPressed 作为解决方法,因为它会在 OSX 上生成错误的外观。
【解决方案3】:

我刚刚在 Java 8 中创建了一个 ThreeStateCheckBox 类,该类使用/模仿 JCheckBox,具有可调整的行为(例如,它是否应该对全宽、全高做出反应,以及如果组件被布局拉伸,它应该放置在哪里? 用户应该能够影响不确定状态吗?用户应该能够影响它吗?),并且在所有标准 LookAndFeels 中看起来都正确(但如果使用 Nimbus,则必须提供额外的参数)。花了我最后 5 个小时。

演示代码包含在类的顶部。 编辑: screenshot

概念:

使用不带文本的普通 JCheckBox,以便外观和行为是经典的,并且可以通过 paintComponent 代码测量框的大小。为不确定状态绘制一个填充的矩形。 JCheckBox 以编程方式设置,由于使用了 ActionListener,因此不会触发事件。

布尔值用于表示状态。该类本身不扩展组件,因此它只有基本方法。所有使用的组件都暴露出来。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;




/**
 * ThreeStateCheckBox by dreamspacepresident.com
 * <p>
 * v[1, 2015-11-20 13!00 UTC]
 */
final public class ThreeStateCheckBox {


    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    // MAIN METHOD FOR TESTS/DEMONSTRATION
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////


    /**
     * Two checkboxes are shown. The upper one is TreeStateCheckBox, the lower one is the classic JCheckBox.
     */
    public static void main(final String[] args) {

        SwingUtilities.invokeLater(() -> {

            // PICK A LOOKANDFEEL
            final String lookAndFeelName;
            lookAndFeelName = "Nimbus";
            //            lookAndFeelName = "Metal";
            //            lookAndFeelName = "CDE/Motif";
            //            lookAndFeelName = "Windows";
            //            lookAndFeelName = "Windows Classic";
            final UIManager.LookAndFeelInfo[] lafis = UIManager.getInstalledLookAndFeels();
            boolean foundLAFI = false;
            for (UIManager.LookAndFeelInfo lafi : lafis) {
                if (lafi.getName().equalsIgnoreCase(lookAndFeelName)) {
                    try {
                        UIManager.setLookAndFeel(lafi.getClassName());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    foundLAFI = true;
                    break;
                }
            }
            if (!foundLAFI) {
                throw new Error("Unknown LookAndFeel: " + lookAndFeelName);
            }

            // CREATE AND INITIALIZE COMPONENTS
            final JFrame window = new JFrame("L&F: " + lookAndFeelName);
            final JPanel contentPane = new JPanel(new GridLayout(0, 1));
            final String cbDemoText = "bold";
            final List<JComponent> cbList = new ArrayList<>(); // Originally, I had more demos planned. I'll leave this here in case you need it.
            ThreeStateCheckBox cbThreeState;
            JCheckBox cbClassic;
            //            contentPane.setBackground(Color.MAGENTA);

            cbThreeState = new ThreeStateCheckBox(cbDemoText, null, false, true, null, null, null, null, false, false, TSCBOrientation.WEST, true, null); // NIMBUS DEMO (because of the gap)
            //            cbThreeState = new ThreeStateCheckBox(cbDemoText);
            cbClassic = new JCheckBox(cbDemoText);

            cbList.add(cbThreeState.getComponent());
            cbList.add(cbClassic);

            cbThreeState.setClientCode(b -> System.out.println("CURRENT STATE: " + b.getState() + "; PREVIOUS STATE: " + b.getPreviousState()));
            cbClassic.addActionListener(e -> System.out.println("CURRENT STATE: " + cbClassic.isSelected()));

            //            cbThreeState.setEnabled(false);
            //            cbClassic.setEnabled(false);

            //            cbClassic.setBackground(Color.ORANGE);
            //            cbThreeState.setBackground(Color.ORANGE);

            //            cbClassic.setOpaque(true);
            //            cbThreeState.setOpaque(true);

            //            cbThreeState.setText("italic");
            //            cbClassic.setText("italian");

            cbThreeState.setUserCanEffectIndeterminateState(true);


            // LAYOUT COMPONENTS
            cbList.forEach(contentPane::add);


            // INITIALIZE AND SHOW WINDOW
            window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            window.setContentPane(contentPane);
            //            window.pack();
            window.setSize(new Dimension(300, 300));
            window.setResizable(false);
            window.setLocationRelativeTo(null);
            window.setVisible(true);


            cbThreeState.setState(null);
            cbThreeState.setState(false);
            cbThreeState.setState(true);
            cbThreeState.callClientCode();
        });
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    // THE ACTUAL CLASS BEGINS HERE
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////




    public enum TSCBOrientation {
        NORTHWEST, NORTH, NORTHEAST, WEST, CENTER, EAST, SOUTHWEST, SOUTH, SOUTHEAST
    }




    final public static Color DEFAULT_COLOR_OF_INDETERMINATE_STATE = new Color(0x222222);
    final public static Color DEFAULT_COLOR_OF_INDETERMINATE_STATE_WHENDISABLED = new Color(0x7f222222, true);
    final public static int DEFAULT_SIZE_OF_INDETERMINATERECTANGLE = 8;
    final public static int MIN_SIZE_OF_INDETERMINATERECTANGLE = 2;
    final private static Map<TSCBOrientation, Integer> GBC_ORIENTATION = new HashMap<>();

    static {
        GBC_ORIENTATION.put(TSCBOrientation.NORTHWEST, GridBagConstraints.NORTHWEST);
        GBC_ORIENTATION.put(TSCBOrientation.NORTH, GridBagConstraints.NORTH);
        GBC_ORIENTATION.put(TSCBOrientation.NORTHEAST, GridBagConstraints.NORTHEAST);
        GBC_ORIENTATION.put(TSCBOrientation.WEST, GridBagConstraints.WEST);
        GBC_ORIENTATION.put(TSCBOrientation.CENTER, GridBagConstraints.CENTER);
        GBC_ORIENTATION.put(TSCBOrientation.EAST, GridBagConstraints.EAST);
        GBC_ORIENTATION.put(TSCBOrientation.SOUTHWEST, GridBagConstraints.SOUTHWEST);
        GBC_ORIENTATION.put(TSCBOrientation.SOUTH, GridBagConstraints.SOUTH);
        GBC_ORIENTATION.put(TSCBOrientation.SOUTHEAST, GridBagConstraints.SOUTHEAST);
    }


    final private JPanel allContainer; // Extra step to ensure JCheckBox will have its minimum size.
    final private JPanel contentContainer;
    final private JCheckBox checkBox;
    final private JLabel label;

    private String text = null;
    private Boolean state;
    private Boolean previousState;
    private boolean userCanEffectIndeterminateState;
    private boolean userCanAffectIndeterminateState;
    private Consumer<ThreeStateCheckBox> clientCode = null;

    final private Color colorOfIndeterminateRectangle;
    final private Color colorOfIndeterminateRectangleWhenDisabled;
    final private int widthOfIndeterminateRectangle;
    final private int heightOfIndeterminateRectangle;

    private boolean enabled = true;


    public ThreeStateCheckBox() {

        this(null, false, false, true, null, null, null, null, false, false, TSCBOrientation.WEST, false, null);
    }


    public ThreeStateCheckBox(final String text) {

        this(text, false, false, true, null, null, null, null, false, false, TSCBOrientation.WEST, false, null);
    }


    /**
     * The all-constructor, called by the others.
     *
     * @param text                                      checkbox text
     * @param initialState                              whether the checkbox initially is not selelected (false),
     *                                                  selected (true), or in indeterminate state (null)
     * @param userCanEffectIndeterminateState           Seriously, it's not that hard.
     * @param userCanAffectIndeterminateState           So learn it already.
     * @param colorOfIndeterminateRectangle             When the state is indeterminate, a filled rectangle (usually
     *                                                  square) will be drawn. The default color (if value here is null)
     *                                                  is a very dark gray which visually works with all LookAndFeels.
     *                                                  (This setting can not be changed later.)
     * @param colorOfIndeterminateRectangleWhenDisabled Same, except for when the component is not enabled. The color is
     *                                                  the same, except it's only half opaque.
     * @param widthOfIndeterminateRectangle             null = default of 8, otherwise the size of the
     *                                                  indeterminate-rectangle can be defined here. (This setting can
     *                                                  not be changed later.)
     * @param heightOfIndeterminateRectangle            Same.
     * @param reactAcrossFullWidth                      The default behavior for standard JCheckBoxes is true - no
     *                                                  matter how far to the right of the box you click, even if there
     *                                                  is no text at all, the box will be clicked. The default behavior
     *                                                  (as per the other constructors) for ThreeStateCheckBox in this
     *                                                  regard is FALSE, however. (This setting can not be changed
     *                                                  later.)
     * @param reactAcrossFullHeight                     The default behavior for standard JCheckBoxes is true - no
     *                                                  matter how empty the space above and below is, the box will be
     *                                                  clicked. The default behavior (as per the other constructors)
     *                                                  for ThreeStateCheckBox in this regard is FALSE, however. If this
     *                                                  setting is true, the same setting for width is effectively true,
     *                                                  too, nothing you can do about that. (This setting can not be
     *                                                  changed later.)
     * @param orientationInAvailableSpace               If the component is stretched via its placement in the layout
     *                                                  hierarchy, as opposed to the JCheckBox, the ThreeStateCheckBox
     *                                                  can be positioned in 9 places. (This setting can not be changed
     *                                                  later.)
     * @param leaveGapBetweenBoxAndLabel                Even though JCheckBox's getIconTextGap() is queried to effect a
     *                                                  proper distance, this works only correctly for Nimbus. In all
     *                                                  other cases, the gap is too big compared to the original
     *                                                  JCheckBox. Setting this to false fixes the behavior for all
     *                                                  other LookAndFeels, because it just leave *no* gap at all.
     * @param clientCode                                The code to be executed if THE USER clicks the checkbox. The
     *                                                  code will receive a reference to the ThreeStateCheckBox, so they
     *                                                  can query the new (=current) and previous state of it.
     */
    public ThreeStateCheckBox(final String text, final Boolean initialState, final boolean userCanEffectIndeterminateState, final boolean userCanAffectIndeterminateState, final Color colorOfIndeterminateRectangle, final Color colorOfIndeterminateRectangleWhenDisabled, final Integer widthOfIndeterminateRectangle, final Integer heightOfIndeterminateRectangle, final boolean reactAcrossFullWidth, final boolean reactAcrossFullHeight, final TSCBOrientation orientationInAvailableSpace, final boolean leaveGapBetweenBoxAndLabel, final Consumer<ThreeStateCheckBox> clientCode) {


        // INITIALIZE CONSTRUCTOR VALUES
        state = initialState;
        previousState = state;
        this.userCanEffectIndeterminateState = userCanEffectIndeterminateState;
        this.userCanAffectIndeterminateState = userCanAffectIndeterminateState;

        if (colorOfIndeterminateRectangle == null) {
            this.colorOfIndeterminateRectangle = DEFAULT_COLOR_OF_INDETERMINATE_STATE;
        } else {
            this.colorOfIndeterminateRectangle = colorOfIndeterminateRectangle;
        }
        if (colorOfIndeterminateRectangleWhenDisabled == null) {
            this.colorOfIndeterminateRectangleWhenDisabled = DEFAULT_COLOR_OF_INDETERMINATE_STATE_WHENDISABLED;
        } else {
            this.colorOfIndeterminateRectangleWhenDisabled = colorOfIndeterminateRectangleWhenDisabled;
        }

        if (widthOfIndeterminateRectangle == null) {
            this.widthOfIndeterminateRectangle = DEFAULT_SIZE_OF_INDETERMINATERECTANGLE;
        } else {
            this.widthOfIndeterminateRectangle = Math.max(MIN_SIZE_OF_INDETERMINATERECTANGLE, widthOfIndeterminateRectangle);
        }
        if (heightOfIndeterminateRectangle == null) {
            this.heightOfIndeterminateRectangle = DEFAULT_SIZE_OF_INDETERMINATERECTANGLE;
        } else {
            this.heightOfIndeterminateRectangle = Math.max(MIN_SIZE_OF_INDETERMINATERECTANGLE, heightOfIndeterminateRectangle);
        }

        Integer orientation = GBC_ORIENTATION.get(orientationInAvailableSpace);
        if (orientation == null) {
            orientation = GridBagConstraints.CENTER;
        }
        this.clientCode = clientCode;


        // CREATE COMPONENTS
        label = new JLabel();
        checkBox = new JCheckBox() {

            @Override
            protected void paintComponent(final Graphics g) {

                super.paintComponent(g);

                if (state == null) {

                    final int w = getWidth();
                    final int h = getHeight();
                    final int wOdd = w % 2;
                    final int hOdd = h % 2;
                    final int centerX = w / 2;
                    final int centerY = h / 2;
                    final int rwHalf = Math.max(1, ThreeStateCheckBox.this.widthOfIndeterminateRectangle / 2);
                    final int rhHalf = Math.max(1, ThreeStateCheckBox.this.heightOfIndeterminateRectangle / 2);
                    final int rw = rwHalf * 2 + wOdd;
                    final int rh = rhHalf * 2 + hOdd;

                    if (isEnabled()) {
                        g.setColor(ThreeStateCheckBox.this.colorOfIndeterminateRectangle);
                    } else {
                        g.setColor(ThreeStateCheckBox.this.colorOfIndeterminateRectangleWhenDisabled);
                    }
                    g.fillRect(centerX - rwHalf, centerY - rhHalf, rw, rh);
                }
            }
        };
        final int gap;
        if (leaveGapBetweenBoxAndLabel) {
            gap = checkBox.getIconTextGap();
        } else {
            gap = 0;
        }

        contentContainer = new JPanel(new BorderLayout(gap, 0));
        allContainer = new JPanel(new GridBagLayout());


        // INITIALIZE COMPONENTS
        setOpaque(false);
        if (state != null && state) {
            checkBox.setSelected(true);
        }
        setText(text);


        // LAYOUT COMPONENTS
        contentContainer.add(checkBox, BorderLayout.WEST);
        contentContainer.add(label, BorderLayout.CENTER);
        final GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = orientation;
        if (reactAcrossFullWidth) {
            gbc.fill = GridBagConstraints.HORIZONTAL;
        }
        gbc.weightx = 1;
        gbc.weighty = 1;
        allContainer.add(contentContainer, gbc);


        // CREATE LISTENERS
        final ActionListener checkBoxListener = e -> {
            if (!enabled) {
                return;
            }
            if (state == null) {
                if (ThreeStateCheckBox.this.userCanAffectIndeterminateState) {
                    setState(false);
                } else {
                    setState(null);
                }
            } else if (!state) {
                setState(true);
            } else {
                if (ThreeStateCheckBox.this.userCanEffectIndeterminateState) {
                    setState(null);
                } else {
                    setState(false);
                }
            }
            callClientCode();
        };
        final MouseListener labelAndPanelListener = new MouseAdapter() {

            final private ButtonModel model = checkBox.getModel();
            private boolean lmbIsDown = false;


            @Override
            public void mousePressed(final MouseEvent e) {

                if (e.getButton() == MouseEvent.BUTTON1) {
                    lmbIsDown = true;
                    model.setPressed(true);
                    model.setArmed(true);
                }
            }


            @Override
            public void mouseReleased(final MouseEvent e) {

                if (e.getButton() == MouseEvent.BUTTON1) {
                    lmbIsDown = false;
                    model.setPressed(false);
                    model.setArmed(false);
                }
            }


            @Override
            public void mouseEntered(final MouseEvent e) {

                model.setArmed(lmbIsDown);
            }


            @Override
            public void mouseExited(final MouseEvent e) {

                model.setArmed(false);
            }

        };


        // ASSIGN LISTENERS
        checkBox.addActionListener(checkBoxListener);
        checkBox.addMouseListener(labelAndPanelListener); // Necessary or the behavior will look different when the mouse moves while button is down.
        label.addMouseListener(labelAndPanelListener);
        contentContainer.addMouseListener(labelAndPanelListener);
        if (reactAcrossFullHeight) {
            allContainer.addMouseListener(labelAndPanelListener);
        }
    }


    public void setEnabled(final boolean enabled) {

        if (enabled == this.enabled) {
            return;
        }
        this.enabled = enabled;
        checkBox.setEnabled(enabled);
        label.setEnabled(enabled);
        contentContainer.setEnabled(enabled);
    }


    public boolean isEnabled() {

        return enabled;
    }


    public Color getBackground() {

        return allContainer.getBackground();
    }


    public void setBackground(final Color color) {

        if (color == null) {
            return;
        }
        checkBox.setBackground(color);
        label.setBackground(color);
        contentContainer.setBackground(color);
        allContainer.setBackground(color);
    }


    public boolean isOpaque() {

        return allContainer.isOpaque();
    }


    public void setOpaque(final boolean opaque) {

        checkBox.setOpaque(opaque);
        label.setOpaque(opaque);
        contentContainer.setOpaque(opaque);
        allContainer.setOpaque(opaque);
    }


    public JPanel getComponent() {

        return allContainer;
    }


    public JPanel getCenterContainer() {

        return contentContainer;
    }


    public JCheckBox getJCheckBox() {

        return checkBox;
    }


    public JLabel getJLabel() {

        return label;
    }


    public boolean canUserEffectIndeterminateState() {

        return userCanEffectIndeterminateState;
    }


    public void setUserCanEffectIndeterminateState(final boolean userCanEffectIndeterminateState) {

        this.userCanEffectIndeterminateState = userCanEffectIndeterminateState;
    }


    public boolean canUserAffectIndeterminateState() {

        return userCanAffectIndeterminateState;
    }


    public void setUserCanAffectIndeterminateState(final boolean userCanAffectIndeterminateState) {

        this.userCanAffectIndeterminateState = userCanAffectIndeterminateState;
    }


    /**
     * Sets the state to the new state IF IT IS DIFFERENT, which is relevant because of the previousState variable.
     * <p>
     * Does NOT call the client code! You would have to do this yourself.
     *
     * @param newState null = indeterminate, false = not selected, true = selected
     */
    public void setState(final Boolean newState) {

        checkBox.setSelected(newState != null && newState);
        if (newState == state) {
            return;
        }
        previousState = state;
        state = newState;
        checkBox.repaint();
    }


    public Boolean getState() {

        return state;
    }


    public Boolean getPreviousState() {

        return previousState;
    }


    public void setClientCode(final Consumer<ThreeStateCheckBox> clientCode) {

        this.clientCode = clientCode;
    }


    public Consumer<ThreeStateCheckBox> getClientCode() {

        return clientCode;
    }


    /**
     * Convenience method so that you don't have to keep the reference around in case you need an extra call.
     */
    public void callClientCode() {

        if (clientCode != null) {
            clientCode.accept(this);
        }
    }


    public void setText(final String newText) {

        if (newText == null || newText.trim().isEmpty()) {
            text = null;
        } else {
            text = newText;
        }
        label.setText(text);
    }


    public String getText() {

        return text;
    }


}

【讨论】:

    【解决方案4】:

    我不知道为什么有人会提供带有额外图标 png 文件的解决方案,而 java api覆盖paintIcon(..) 方法提供了很好的功能。

    确定三态 CheckBox 姿态的最佳轻量级解决方案是 IMO ClientProperty 属性。

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

    用法:TCheckBox tcb = new TCheckBox("My CheckBox");

    【讨论】:

    • 在 Windows 上运行良好,但在 Linux/GTKLookAndFeel 上不行。有什么想法吗?
    【解决方案5】:

    您将需要保存复选框状态的东西。我使用自定义模型。

    @Override
    protected void paintComponent(Graphics g) {
    
        getModel().setArmedForPaint(isPartial());
        getModel().setPressedForPaint(isPartial());
    
        super.paintComponent(g);
    
        getModel().setArmedForPaint(null);
        getModel().setPressedForPaint(null);
    }
    

    在您的自定义 ToggleButtonModel 中

        @Override
        public boolean isArmed() {
            if (armedForPaint != null) {
                return armedForPaint.booleanValue();
            }
    
            return super.isArmed();
        }
    
        @Override
        public boolean isPressed() {
            if (pressedForPaint != null) {
                return pressedForPaint.booleanValue();
            }
    
            return super.isPressed();
        }
    

    【讨论】:

      【解决方案6】:

      你可以在这个相关的答案中得到这个结果:

      https://stackoverflow.com/a/15898068/980442

      【讨论】:

        【解决方案7】:

        UI 元素 JCheckBox 不直接支持,但你可以欺骗它:

        //selected
        checkbox.setSelected(true);
        
        //partially selected
        checkbox.getModel().setPressed(true);
        checkbox.getModel().setArmed(true);
        
        //not selected
        checkbox.setSelected(false);
        

        当然这只是 UI 部分,你需要使用自定义模型来实现你的状态机。

        【讨论】:

          【解决方案8】:

          如果您尝试创建复选框树(这是需要三态复选框的常见原因),请查看Jide Commons

          【讨论】:

          • +1 Jide 是我最喜欢的框架
          • 是的,我想要一个复选框树。我搜索了您提到的网站,但没有找到展示树的页面。其次,我想在我的商业开发中使用它是免费的还是我需要购买许可证?
          • 您可以下载演示 (jidesoft.com/products/1.4/jide_demo.jnlp) 并查看它是否有 CheckboxTree 示例(可能有)。至于许可问题,它是根据 GPL 发布的。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-08-24
          • 1970-01-01
          • 1970-01-01
          • 2017-07-22
          • 1970-01-01
          • 1970-01-01
          • 2015-06-18
          相关资源
          最近更新 更多