【问题标题】:Tristate Checkboxes in JavaJava 中的三态复选框
【发布时间】:2010-11-18 19:09:15
【问题描述】:

我真的可以在 Java 中使用三态复选框。这听起来很简单,但我只见过ugly implementations [注意:链接现已损坏]。

三个单选按钮占用了太多空间,就我而言,可能会让用户感到困惑。它基本上是用于搜索对话框。我需要真、假或“不在乎”选项。人们使用不同的技术吗?

【问题讨论】:

    标签: java swing checkbox


    【解决方案1】:

    使用下拉菜单。

    【讨论】:

    • +1 好主意。如果 UI 组件难以编码且非标准,那么用户也会有这种感觉。你最后一次以这种方式使用三态复选框是什么时候?我从来没有。
    • 虽然我同意@byron 的观点,但从用户的角度来看,三态复选框实际上是相当标准的。
    • 哪些用户?我是开发人员和高级用户,我鄙视三态复选框,通常是因为不清楚第三态的含义;通常,这意味着某些细节对您隐藏。这是可用性和可访问性的噩梦。
    • 大多数情况下,这意味着该复选框适用于一类事物,并且这些事物具有不同的状态,您不希望它们更改。检查它会检查所有内容,将其保留在第三状态以不更改它。即使对于高级用户也很常见。
    • 三态复选框的典型用法:更改多对象选择时的属性,灰色+选中的第三个状态意味着“有些项目有这个属性,有些没有,我不会改变它们中的任何一个” .在 Microsoft 应用程序中最常见。
    【解决方案2】:

    我找到了一种通过简单地添加监听器来制作三态复选框的方法:

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

    然后在应用程序代码中,只有一行:

    
    jCheckBox1.addActionListener(new TriStateActionListener(getResourceMap().getIcon("TriStateIcon")));
    

    在收到所有反馈后,我认为下拉菜单可能是更好的选择。但是,我想在这里与其他人分享我的代码。

    【讨论】:

      【解决方案3】:

      在这个实现中,这三种状态只能通过编程方式设置。为了外观和感觉可移植,它使用图像,这些图像必须放在同一个 java 包中。

      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();
              }
          }
      }
      

      示例帧:

      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;
      }
      

      【讨论】:

      • 我们将这种方法用于表示开/关状态聚合的复选框。交互切换所有的开/关,中间状态只能通过与单个元素的交互来达到。效果很好,我认为它在 UI 设计方面更合理。
      【解决方案4】:

      JIDE 在他们的Common Layer 中开源了一些非常好的功能,其中一个恰好是三态复选框

      我建议你去运行webstart demo 看看它是否满足你的需求

      【讨论】:

        【解决方案5】:

        三态复选框是带有部分选中子节点的 Treeview 的标准 UI 习惯用法。它们广泛用于复杂层次视图(例如 Google 地球)中的图层管理。

        【讨论】:

          【解决方案6】:

          我不知道为什么有人会提供带有额外图标 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");

          【讨论】:

          • 那么为什么要使用 ClientProperty 而不是成员变量呢?诀窍在哪里?
          【解决方案7】:

          “丑陋的实现”是一个旧链接。该页面上的一项建议是几年前更新的。我还没有测试过旧的实现,所以我不知道新的实现是更好还是更差。

          TristateCheckBox Revisited

          【讨论】:

          • 是的,我认为 Heinz Kabutz 做得很好。我也会推荐!
          【解决方案8】:

          我会使用你发布的那个。

          只要您的复杂性在另一个类中(有效)并且它的行为就像任何其他控件一样,谁在乎? (这似乎是所有 Swing 背后的假设,大多数 Swing 课程似乎都这么复杂。)

          【讨论】:

            【解决方案9】:

            更改用户界面。三态复选框是不寻常的,确实会混淆用户。下拉菜单是一个不错的选择,但如果在对话框中出现不止一次,它也会给用户带来很多困惑。

            【讨论】:

              猜你喜欢
              • 2010-12-16
              • 1970-01-01
              • 1970-01-01
              • 2016-10-26
              • 2011-03-07
              • 1970-01-01
              • 2011-05-16
              • 2019-01-14
              • 2019-04-02
              相关资源
              最近更新 更多