Как правильно изменить поведение рендеринга текста флажка?

Я использую внешний вид Nimbus.

У меня есть два JCheckBox, и по умолчанию текст черный, когда JCheckBox включен, и серый, когда он отключен. Мое новое требование состоит в том, что программа должна игнорировать включенное состояние JCheckBox и вместо этого быть серой, когда isSelected() имеет значение false, и отображать текст в указанном цвете, когда isSelected() имеет значение true.

Я попытался сделать это:

  • Расширение BasicCheckBoxUI
  • Переопределение метода paintText
  • Копирование содержимого BasicButtonUI.paintText() и изменение поведения
  • Вызов setUI для рассматриваемых JCheckBoxes с экземпляром моего нового класса пользовательского интерфейса

    private static class MyCheckBoxUI extends BasicCheckBoxUI
    {
        private Color selectedColor;
    
        public MyCheckBoxUI( Color selectedColor )
        {
            this.selectedColor = selectedColor;
        }
    
        @Override
        protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text )
        {
            ButtonModel model = b.getModel();
            FontMetrics fm = SwingUtilities2.getFontMetrics(b, g);
            int mnemonicIndex = b.getDisplayedMnemonicIndex();
    
            if( model.isSelected() ) 
            {
                /*** paint the text normally */
                g.setColor( selectedColor );
                SwingUtilities2.drawStringUnderlineCharAt(b, g,text, mnemonicIndex,
                                          textRect.x + getTextShiftOffset(),
                                          textRect.y + fm.getAscent() + getTextShiftOffset());
            }
            else 
            {
                /*** paint the text disabled ***/
                g.setColor(b.getBackground().brighter());
                SwingUtilities2.drawStringUnderlineCharAt(b, g,text, mnemonicIndex,
                                          textRect.x, textRect.y + fm.getAscent());
                g.setColor(b.getBackground().darker());
                SwingUtilities2.drawStringUnderlineCharAt(b, g,text, mnemonicIndex,
                                          textRect.x - 1, textRect.y + fm.getAscent() - 1);
            }
        }
    }
    

В конструкторе моей JPanel у меня есть следующее:

        jCheckBox1.setUI( new MyCheckBoxUI( Color.red ) );
        jCheckBox2.setUI( new MyCheckBoxUI( Color.black ) );

Похоже, это работает, как и ожидалось, за исключением того, что есть побочный эффект. Теперь флажок не будет отображать галочку в поле, когда он выбран, как раньше (я не ожидал этого, поскольку я только переопределил метод paintText). Что я пропустил?

Кроме того, меня беспокоит использование SwingUtilities2, так как меня предупредили, что это внутренний проприетарный API, который может быть удален в будущем выпуске. Есть лучший способ сделать это?


person Ogre    schedule 21.07.2014    source источник


Ответы (1)


Вам нужно поиграть со свойствами цвета UIManager...

Object disabledTextForeground = UIManager.get("CheckBox[Disabled].textForeground");
Object enabledTextForeground =  UIManager.get("CheckBox.foreground");
UIManager.put("CheckBox[Disabled].textForeground", UIManager.get("CheckBox.foreground"));
UIManager.put("CheckBox[Enabled].textForeground", disabledTextForeground);
UIManager.put("CheckBox[Selected+Enabled].textForeground", enabledTextForeground);

Этот пример позволит вам изменить значения на глобальном уровне, а это означает, что каждый созданный вами JCheckBox будет иметь одинаковые значения.

Параметры

Например...

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestNimbus {

    public static void main(String[] args) {
        new TestNimbus();
    }

    public TestNimbus() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                Object disabledTextForeground = UIManager.get("CheckBox[Disabled].textForeground");
                Object enabledTextForeground =  UIManager.get("CheckBox.foreground");
                UIManager.put("CheckBox[Disabled].textForeground", UIManager.get("CheckBox.foreground"));
                UIManager.put("CheckBox[Enabled].textForeground", disabledTextForeground);
                UIManager.put("CheckBox[Selected+Enabled].textForeground", enabledTextForeground);

                JCheckBox cb1 = new JCheckBox("Option #1");
                JCheckBox cb2 = new JCheckBox("Option #2");
                JCheckBox cb3 = new JCheckBox("Option #3");

                cb1.setSelected(true);
                cb3.setEnabled(false);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridwidth = gbc.REMAINDER;
                frame.add(cb1, gbc);
                frame.add(cb2, gbc);
                frame.add(cb3, gbc);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

Чтобы повлиять только на данный экземпляр JCheckBoxs, вам нужно будет указать переопределения непосредственно для каждого экземпляра JCheckBox, который вы хотите применить...

LocalOptions

Три верхних значения переопределены, а три нижних остаются неизменными и используют значения по умолчанию пользовательского интерфейса.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestNimbus {

    public static void main(String[] args) {
        new TestNimbus();
    }

    public TestNimbus() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                UIDefaults checkBoxDefaults = new UIDefaults();

                Object disabledTextForeground = UIManager.get("CheckBox[Disabled].textForeground");
                Object enabledTextForeground = UIManager.get("CheckBox.foreground");
                checkBoxDefaults.put("CheckBox[Disabled].textForeground", UIManager.get("CheckBox.foreground"));
                checkBoxDefaults.put("CheckBox[Enabled].textForeground", disabledTextForeground);
                checkBoxDefaults.put("CheckBox[Selected+Enabled].textForeground", enabledTextForeground);

                JCheckBox cb1 = new JCheckBox("Option #1");
                JCheckBox cb2 = new JCheckBox("Option #2");
                JCheckBox cb3 = new JCheckBox("Option #3");

                JCheckBox cb4 = new JCheckBox("Normal #1");
                JCheckBox cb5 = new JCheckBox("Normal #2");
                JCheckBox cb6 = new JCheckBox("Normal #3");

                configure(cb1, checkBoxDefaults);
                configure(cb2, checkBoxDefaults);
                configure(cb3, checkBoxDefaults);

                cb1.setSelected(true);
                cb4.setSelected(true);
                cb3.setEnabled(false);
                cb6.setEnabled(false);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridwidth = gbc.REMAINDER;
                frame.add(cb1, gbc);
                frame.add(cb2, gbc);
                frame.add(cb3, gbc);
                frame.add(cb4, gbc);
                frame.add(cb5, gbc);
                frame.add(cb6, gbc);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

            private void configure(JCheckBox checkbox, UIDefaults uiDefaults) {
                checkbox.putClientProperty("Nimbus.Overrides", uiDefaults);
//                checkbox.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
            }
        });
    }

}
person MadProgrammer    schedule 22.07.2014
comment
После удаления строки CheckBox[Disabled].textForeground и добавления checkBoxDefaults.put(CheckBox[Selected+Disabled].textForeground, enabledTextForeground); он ведет себя так, как я хотел, за исключением того, что мне нужно, чтобы один из них был красным, когда он выбран, и серым, когда он не выбран. Я попытался изменить цвет переднего плана в графическом редакторе, но теперь он всегда красный. Почему это работает правильно, только если цвет переднего плана черный? - person Ogre; 22.07.2014
comment
Вам нужно изменить пользовательский интерфейс по умолчанию для выбранного цвета переднего плана, чтобы использовать Color.RED. Изменение свойства переднего плана в редакторе форм переопределяет значения по умолчанию переднего плана для всех состояний. - person MadProgrammer; 22.07.2014
comment
Боюсь, вы меня там потеряли. Не могли бы вы указать мне документацию, которая объясняет, как это сделать? Есть ли документация по строкам, которые можно использовать для UIManager.get()? - person Ogre; 22.07.2014
comment
Nimbus по умолчанию может быть хорошим началом - person MadProgrammer; 22.07.2014
comment
@mKorbel Я люблю Nimbus ‹вставьте сарказм› - person MadProgrammer; 22.07.2014
comment
@MadProgrammer в последних моих двух-трех постах работает только с одной из трех клавиш (Win8_64b/Java7), количество успешно уменьшается периодически с каждым из обновлений Java - person mKorbel; 22.07.2014