Swing - удалить выбранную границу и изменить цвет стрелки JComboBox - PullRequest
0 голосов
/ 26 января 2020

Я пытаюсь удалить выбранную границу JComboBox (верхняя стрелка) и изменить цвет стрелки (нижняя стрелка)

Если возможно, как удалить внешнюю границу? (темно-серый)

Как мне go сделать это?

1 Ответ

0 голосов
/ 26 января 2020

Таким образом, вы можете сделать это, реализовав ComboBoxUI, или, собственно, подкласс BasicComboBoxUI, например. Второй вариант лучше, потому что вам нужно только немного настроить код в некоторых местах, и вы готовы (вместо того, чтобы реализовывать свой собственный ComboBoxUI с нуля). Далее следует код, который выполняет то, что вы просили (надеюсь):

import java.awt.Color;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicComboBoxUI;

public class MainComboBoxUI {
    public static class MyComboBoxUI extends BasicComboBoxUI {
        @Override
        protected void installDefaults() {
            super.installDefaults();
            LookAndFeel.uninstallBorder(comboBox); //Uninstalls the LAF border for both button and label of combo box.
        }

        @Override
        protected JButton createArrowButton() {
            //Feel free to play with the colors:
            final Color background = Color.CYAN.darker();     //Default is UIManager.getColor("ComboBox.buttonBackground").
            final Color pressedButtonBorderColor = Color.RED; //Default is UIManager.getColor("ComboBox.buttonShadow"). The color of the border of the button, while it is pressed.
            final Color triangle = Color.BLACK;               //Default is UIManager.getColor("ComboBox.buttonDarkShadow"). The color of the triangle.
            final Color highlight = background;               //Default is UIManager.getColor("ComboBox.buttonHighlight"). Another color to show the button as highlighted.
            final JButton button = new BasicArrowButton(BasicArrowButton.SOUTH, background, pressedButtonBorderColor, triangle, highlight);
            button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
            return button;
        }
    }

    public static void main(final String[] args) {
        final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
        combo.setUI(new MyComboBoxUI());

        final JPanel panel = new JPanel(new GridBagLayout());
        panel.add(combo);
        panel.setBackground(Color.RED.darker());

        final JFrame frame = new JFrame("MainComboBoxUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Прежде всего, мы удаляем границу из поля со списком внутри метода installDefaults(). Это удалит границу как с кнопки, так и с метки. Затем мы имитируем c createArrowButton() метод BasicComboBoxUI, чтобы создать собственную кнопку. Реализация по умолчанию BasicComboBoxUI.createArrowButton() создает BasicArrowButton с некоторыми цветами во время строительства. Эти цвета изменяют цвет треугольника кнопки, например, и цвет фона.

Это для создания BasicArrowButton. Хотя, если вы хотите лучше контролировать кнопку (например, вместо треугольника вам нужен значок и / или некоторый текст на кнопке), вы можете просто создать простой JButton внутри createArrowButton() вместо BasicArrowButton. Затем вы можете инициализировать его внутри того же метода или, предпочтительно, изменить его внутри configureArrowButton(). Как и в следующем коде:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicComboBoxUI;

public class MainComboBoxUI2 {
    public static class MyComboBoxUI extends BasicComboBoxUI {
        @Override
        protected void installDefaults() {
            super.installDefaults();
            LookAndFeel.uninstallBorder(comboBox);
        }

        @Override
        protected JButton createArrowButton() {
            final JButton button = new JButton("V");
            button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
            return button;
        }

        @Override
        public void configureArrowButton() {
            super.configureArrowButton(); //Do not forget this!
            arrowButton.setBackground(Color.CYAN.darker());
            arrowButton.setForeground(Color.BLUE);
        }
    }

    public static void main(final String[] args) {
        final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
        combo.setUI(new MyComboBoxUI());
        combo.setPreferredSize(new Dimension(150, 45)); //Needed to be able to show the button's text.

        final JPanel panel = new JPanel(new GridBagLayout());
        panel.add(combo);
        panel.setBackground(Color.RED.darker());

        final JFrame frame = new JFrame("MainComboBoxUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Единственная проблема с обычным подходом JButton (ie второй подход) состоит в том, что LayoutManager JComboBox не будет учитывать предпочтительный размер кнопки со стрелкой, если защищенный флаг внутри пользовательского интерфейса (с именем squareButton ) равен true. Если этот флаг равен true, то ширина кнопки устанавливается равной высоте кнопки, которая, в свою очередь, устанавливается равной высоте поля со списком. Вот почему во втором подходе я вручную установил предпочтительный размер поля со списком, чтобы задать ему высоту 45 пикселей, что достаточно для отображения текста внутри кнопки (в этом конкретном случае c, где текст равно "V").

Вы можете установить значение этого флага для отдельного экземпляра пользовательского интерфейса вручную, переопределив метод installDefaults() в BasicComboBoxUI и установив его, например, false. В противном случае вы можете установить его значение для всех полей со списком, вызвав UIManager.put("ComboBox.squareButton", Boolean.FALSE); в своем коде достаточно рано (ie перед созданием каждого поля со списком). Но, как оказалось, из-за того, что я проверял это, при установке этого значения на false размер кнопки перекрывает размер области отображения ( область отображения - это то, где отображается выбранное значение поля со списком ).

После еще нескольких копаний я обнаружил, что причина этой проблемы в том, что метод getMinimumSize(...) интерфейса пользователя не учитывает вставки кнопки ... Это означает, что граница по умолчанию JButton вокруг текста / значка не учитывается. Поэтому после установки флага squareButton на false вам также потребуется переопределить этот метод.

Собрав их все вместе, вы получите следующий окончательный подход / код:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicComboBoxUI;

public class MainComboBoxUI3 {
    public static class MyComboBoxUI extends BasicComboBoxUI {
        @Override
        protected void installDefaults() {
            super.installDefaults();
            LookAndFeel.uninstallBorder(comboBox);
        }

        @Override
        protected JButton createArrowButton() {
            final JButton button = new JButton("Drop");
            button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
            return button;
        }

        @Override
        public void configureArrowButton() {
            super.configureArrowButton(); //Do not forget this!
            arrowButton.setBackground(Color.CYAN.darker());
            arrowButton.setForeground(Color.BLUE);
            //arrowButton.setBorder(javax.swing.BorderFactory.createEmptyBorder());
        }

        //Overrided getMinimumSize to take into account the button's insets for both width and height:
        @Override
        public Dimension getMinimumSize(final JComponent c) {
            final Dimension mindim = super.getMinimumSize(c);
            final Insets buttonInsets = arrowButton.getInsets();
            return new Dimension(mindim.width + buttonInsets.left + buttonInsets.right, mindim.height + buttonInsets.top + buttonInsets.bottom);
        }
    }

    public static void main(final String[] args) {
        UIManager.put("ComboBox.squareButton", Boolean.FALSE); //Set all the combo boxes' button to non-square...
        final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
        combo.setUI(new MyComboBoxUI());

        final JPanel panel = new JPanel(new GridBagLayout());
        panel.add(combo);
        panel.setBackground(Color.RED.darker());

        final JFrame frame = new JFrame("MainComboBoxUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

И результат выглядит так:

Combo Box final approach result

Наконец, если вы хотите меньшую границу для кнопки выпадающего меню (ie один с текстом «Drop» на изображении выше), вы можете установить границу arrowButton (например) javax.swing.BorderFactory.createEmptyBorder() внутри configureArrowButton() метода ...

...