2010-06-28 68 views
0

以下面的示例代碼。 Swing中存在一些錯誤,如果組件包含HTML,則不會將禁用的組件禁用爲禁用狀態。除了報告我希望一位同事已經關注的問題之外,是否有解決問題的好辦法?如何複製JCheckBox的禁用外觀?

無論我採取什麼解決方案,我都希望它成爲一個全局修復程序,而不是需要在應用程序中的每個複選框中被攻擊的東西。

我試圖使針對之前和噴漆後調用setForeground的複選框自定義UI,但事實證明,通過調用setForeground了,它會最終導致它調用repaint(),它調用渲染事件。 ..

import java.awt.GridLayout; 
import java.util.Arrays; 

import javax.swing.BorderFactory; 
import javax.swing.JCheckBox; 
import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 

public class TestCheckBoxes extends JFrame 
{ 
    public TestCheckBoxes() 
    { 
     JCheckBox checkBox1 = new JCheckBox("Enabled, plain text"); 
     JCheckBox checkBox2 = new JCheckBox("<html><p>Enabled, HTML"); 
     JCheckBox checkBox3 = new JCheckBox("Disabled, plain text"); 
     checkBox3.setEnabled(false); 
     JCheckBox checkBox4 = new JCheckBox("<html><p>Disabled, HTML"); 
     checkBox4.setEnabled(false); 

     setLayout(new GridLayout(4, 1)); 

     for (JCheckBox checkBox : Arrays.asList(checkBox1, checkBox2, checkBox3, checkBox4)) 
     { 
      checkBox.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); 
      add(checkBox); 
     } 

     ((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); 
     pack(); 
    } 

    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       TestCheckBoxes frame = new TestCheckBoxes(); 
       frame.setDefaultCloseOperation(EXIT_ON_CLOSE); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+0

爲什麼你要HTML的任何具體原因?從上面的代碼片斷,我找不到任何。 – Nivas 2010-06-28 09:27:46

+0

這只是一個複製問題的最小示例。真正的應用程序有一個大膽的詞來強調它,因爲有兩個相似的外觀複選框並排。 – Trejkaz 2010-06-28 22:59:34

回答

0

我建議開始使用renderers而不是那個。您可以使用渲染器(如JLabel的)裏面的HTML和你的問題就會自動消失:)

更多信息在http://java.sun.com/docs/books/tutorial/uiswing/components/combobox.html#renderer

+0

不幸的是,JCheckBox不支持單元格渲染器。否則,我肯定會使用它,製作一個替代默認渲染器的自定義UI。 – Trejkaz 2010-06-28 22:53:19

+0

你是什麼意思?我給你關於如何完成的鏈接。只需創建你自定義的ComboBoxRendeder :)渲染器應該基於ListCellRenderer。它絕對有效 - 我多次使用它。 – 2010-06-28 23:02:30

+0

你怎麼看待我將組合框渲染器應用到複選框? – Trejkaz 2010-06-28 23:25:53

1

你可以分開的複選框,並標註到自己的組件和簡單地做一個沒有標籤的複選框。您也可以將它們添加到它們自己的面板中,並覆蓋面板的方法以簡單地啓用/禁用複選框並更改標籤的顏色。把這個代碼片段例如:

final JCheckBox checkbox = new JCheckBox(); 
final JLabel label = new JLabel(); 
JPanel panel = new JPanel() { 
    @Override 
    public void setEnabled(boolean enabled) { 
     super.setEnabled(enabled); 
     checkbox.setEnabled(enabled); 
     if (enabled) 
      label.setForeground(Color.BLACK); 
     else 
      label.setForeground(Color.GRAY); 
    } 
}; 
panel.add(checkbox); 
panel.add(label); 

注意checkbox和標籤必須是最後在我們小組的的setEnabled()`方法來使用它們。根據您將HTML插入複選框的頻率,您始終可以創建自己的組件類來執行此操作。

public class HTMLCheckBox extends JPanel { 
    private JCheckBox checkbox = new JCheckBox(); 
    private JLabel label = new JLabel(); 
    private Color disabledColor = Color.GRAY; 
    private Color enabledColor = Color.BLACK; 

    public HTMLCheckBox(String text) { 
     label.setText(text); 
     add(checkbox); 
     add(label); 
    } 

    public boolean isSelected() { 
     return checkbox.isSelected(); 
    } 

    @Override 
    public void setEnabled(boolean enabled) { 
     super.setEnabled(enabled); 
     checkbox.setEnabled(enabled); 
     if (enabled) 
      label.setForeground(enabledColor); 
     else 
      label.setForeground(disabledColor); 
    } 
} 

然後根據需要添加自己的構造函數和方法。例如,覆蓋setBackground()以使其爲面板,複選框和標籤設置背景。使用setText()方法來更改標籤文本可能也很方便。無論你想要它做什麼。甚至可以使用enabledColordisabledColor來讓你隨意更改。

+0

修改渲染器也是非常有用的,就像在Eugener的建議中一樣。我遇到了修改渲染器使其顯示我的HTML標記的問題,但這有點棘手有時,但它看起來很乾淨。:) – peppertherj 2010-06-28 14:06:50

+0

這個解決方案聽起來像是需要修改應用程序中的每個視圖。 – Trejkaz 2010-06-28 22:51:10

0

我必須停止回答我自己的問題......必須與時區有關,並有時間在睡眠中思考它。

別的地方的應用...

UIManager.put("CheckBoxUI", "package.for.CustomisedWindowsCheckBoxUI"); 

然後這是實現,但是這仍然是相當哈克,它使用一個實用的方法來生成HTML顏色字符串小於偉大的張貼在這裏。

請注意,這隻適用於Windows L & F.金屬L & F也被證實存在問題,但解決方案是相同的,只是改爲BasicCheckBoxUI的子類。

import java.awt.Graphics; 

import javax.swing.AbstractButton; 
import javax.swing.JComponent; 
import javax.swing.plaf.ColorUIResource; 
import javax.swing.plaf.ComponentUI; 
import javax.swing.plaf.basic.BasicHTML; 
import javax.swing.text.View; 

import com.sun.java.swing.plaf.windows.WindowsCheckBoxUI; 

import com.blah.util.ColourUtils; 

/** 
* Customisation of Windows check box UI to fix bugs. 
*/ 
public class CustomisedWindowsCheckBoxUI extends WindowsCheckBoxUI { 
    /** 
    * Factory method called from Swing. 
    * 
    * @param b the check box. 
    * @return the UI. 
    */ 
    public static ComponentUI createUI(JComponent b) { 
     // TODO: Sun have an AppContext they use to store these once per app. 
     // Might be more sociable to use something like that. 
     return new CustomisedWindowsCheckBoxUI(); 
    } 

    @Override 
    public void paint(Graphics g, JComponent c) { 
     AbstractButton b = (AbstractButton) c; 

     // Works around a bug in BasicButtonUI where a disabled button with HTML markup in the text will 
     // not appear to be disabled. 

     // TODO: Find a way to fix this globally for HTML rendering. It seems odd that it isn't working. 
     // I can see the code in BasicHTML.createHTMLView which uses the foreground colour, which is 
     // obviously why setForeground() works as a workaround. 

     if (b.getForeground() instanceof ColorUIResource) { 
      View view = (View) c.getClientProperty(BasicHTML.propertyKey); 
      if (view != null) { 
       // Ensure that we don't update the renderer if the value hasn't changed. 
       String cachedHtmlFor = (String) c.getClientProperty("cachedHtmlFor"); 
       String key = String.format("%s:%s", c.isEnabled(), b.getText()); 
       if (!key.equals(cachedHtmlFor)) { 
        c.putClientProperty("cachedHtmlFor", key); 
        if (c.isEnabled()) { 
         BasicHTML.updateRenderer(c, b.getText()); 
        } else { 
         BasicHTML.updateRenderer(c, String.format("<html><div style='color: %s'>%s", 
                    ColourUtils.toHtmlColour(b.getBackground().darker()), 
                    b.getText())); 
        } 
       } 
      } 
     } 

     super.paint(g, c); 
    } 
}