5

次の状況があります。JComboboxでは、推奨サイズは最大アイテムサイズに基づいています。ただし、この計算では、に対してレンダリングされた値は考慮されませんnull。モデル内の値のみを考慮します。したがって、null値をレンダリングするためのテキストが他の要素よりも大きい場合、ラベルは切り捨てられ、最後に3つのドット(...)があります。そのような状況は避けたいと思います。

これが私が話していることの小さなデモです:

レンダリングされた切り捨てられたnull値

import java.awt.Component;
import java.awt.GridBagLayout;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TestComboBox {

    protected void initUI() {
        JFrame frame = new JFrame(TestComboBox.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridBagLayout());
        JComboBox comboBox = new JComboBox(new Object[] { "Something", "Stuff", "Beep" });
        comboBox.setRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value == null) {
                    setText("No selection");
                }
                return comp;
            }
        });
        comboBox.setSelectedItem(null);
        panel.add(comboBox);
        frame.add(panel);
        frame.setSize(200, 100);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestComboBox().initUI();
            }
        });
    }
}

何か提案はありませんか?これまでのところ、私のアイデアは、JComboBoxを拡張し、優先サイズをオーバーライドし、null値のレンダリングも実行し、super.preferredSizeの呼び出しとnull値のレンダリングの最大の次元を取ることでした。しかし、これは少し残念です。

ドロップダウンに表示される値がわからないため、prototypeDisplayValueを使用しないことをお勧めします。

4

3 に答える 3

2

私はこれをコードでテストしませんでしたが、私のアプローチは次のようになります。

  1. レンダラーによって返されたのと実際ののpreferredSizeとの差を決定します。ハードコードされた値を使用するのではなく、1つのアイテムと既知のレンダラーのみを含む舞台裏を作成し、の推奨サイズを既知のレンダラーによって返されるサイズと比較するだけです。ComponentpreferredSizeJComboBoxJComboBoxJComboBoxComponent
  2. リスナーをに接続してルックアンドフィールを変更するたびに、手順1を繰り返します。UIManager
  3. getPreferredSize実際ののをオーバーライドし、JComboBoxの幅の最大値を返しますsuper.getPreferredSize()getPreferredSize( rendererComponent ) + calculatedDifference

JComboBoxこれにより、ルックアンドフィールの問題が処理され、不要な計算が回避され、この機能を含む拡張機能を簡単に作成できます。

于 2012-07-20T19:00:57.073 に答える
1

Componentから返されるDefaultListCellRenderer.getListCellRendererComponentのはDefaultListCellRendererオブジェクト自体であり、それはのインスタンスであることがわかっているという事実を利用しますJLabel

また、ルックアンドフィールは、と同様に、通常の方法でコンボボックスの推奨サイズを計算すると想定していますBasicComboBoxUI

その情報では、このソリューションはおそらく醜く非効率的ですが、機能します。

    comboBox.setRenderer(new DefaultListCellRenderer() {

        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value == null) {
                setText("No selection");
            }
            return comp;
        }

      @Override
      public Dimension getPreferredSize() {
        // this doesn't work:
        // int minWidth = (new JLabel("No selection").getPreferredSize()).width;

        // this does work:
        String oldText = getText();
        setText("No selection");
        int minWidth = (super.getPreferredSize()).width;
        setText(oldText);


        Dimension d = super.getPreferredSize();
        if (d.width < minWidth) {
          return new Dimension(minWidth, d.height);
        } else {
          return d;
        }
      }
于 2012-07-20T17:29:38.953 に答える
0

これが私がこれまでに得たものですが、1つの大きな問題はクロスL&Fの問題です。別の方法は、ComboBoxモデルのすべての値と「選択なし」の値を調べて、どれが最も長いかを確認することです。次に、それをprototypeDisplayValueとして設定できます。問題は、各文字列の境界を測定するためのグラフィカルコンテキストが必要なことです。

@Enwiredと@Robinで見つけた2つのソリューションを次に示します。両方に感謝します。

編集:@Robinと話し合った後、このソリューションは実際にははるかに単純であり、すべてのプラットフォームとルックアンドフィールで機能することがわかりました。唯一の欠点は、追加のJComboBoxを作成する必要があることです。

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;

public class TestComboBox {

    protected void initUI() {
        JFrame frame = new JFrame(TestComboBox.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridBagLayout());
        JComboBox comboBox = new JComboBox(new Object[] { "Something", "Stuff", "Beep" }) {

            private JComboBox internal;

            private JComboBox getInternalComboBox() {
                if (internal == null) {
                    internal = new JComboBox(new Object[] { null });
                }
                return internal;
            }

            @Override
            public Dimension getPreferredSize() {
                Dimension preferredSize = super.getPreferredSize();
                if (getSelectedItem() == null) {
                    getInternalComboBox().setRenderer(getRenderer());
                    Dimension nullDimension = getInternalComboBox().getPreferredSize();
                    preferredSize.width = Math.max(preferredSize.width, nullDimension.width);
                    preferredSize.height = Math.max(preferredSize.height, nullDimension.height);
                }
                return preferredSize;
            }

            @Override
            public void updateUI() {
                internal = null;
                super.updateUI();
            }
        };
        comboBox.setRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value == null) {
                    setText("No selection");
                }
                return comp;
            }
        });
        comboBox.setSelectedItem(null);
        panel.add(comboBox);
        frame.add(panel);
        frame.setSize(200, 100);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestComboBox().initUI();
            }
        });
    }
}

編集2:@Enwiredと話し合った後、ListCellRenderergetPreferredSizeを直接オーバーライドするこの代替ソリューションが思い浮かびました。これでは、利用可能なさまざまなL&Fを試すことができます。

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;

public class TestComboBox {

    protected void initUI() {
        final JFrame frame = new JFrame(TestComboBox.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridBagLayout());
        JComboBox comboBox = new JComboBox(new Object[] { "Something", "Stuff", "Beep" });
        comboBox.setRenderer(new DefaultListCellRenderer() {

            private Dimension nullDimesion;

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                if (value != null && nullDimesion == null) {
                    nullDimesion = ((JComponent) getListCellRendererComponent(list, null, -1, false, false)).getPreferredSize();
                }
                Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value == null) {
                    setText("No selection");
                }
                return comp;
            }

            @Override
            public Dimension getPreferredSize() {
                Dimension preferredSize = super.getPreferredSize();
                if (nullDimesion != null) {
                    preferredSize.width = Math.max(preferredSize.width, nullDimesion.width);
                    preferredSize.height = Math.max(preferredSize.height, nullDimesion.height);
                }
                return preferredSize;
            }

            @Override
            public void updateUI() {
                nullDimesion = null;
                super.updateUI();
            }
        });
        comboBox.setSelectedItem(null);
        final JComboBox uiComboBox = new JComboBox(UIManager.getInstalledLookAndFeels());
        uiComboBox.setRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof LookAndFeelInfo) {
                    LookAndFeelInfo info = (LookAndFeelInfo) value;
                    setText(info.getName());
                }
                return comp;
            }
        });
        uiComboBox.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            UIManager.setLookAndFeel(((LookAndFeelInfo) uiComboBox.getSelectedItem()).getClassName());
                            SwingUtilities.updateComponentTreeUI(frame);
                        } catch (ClassNotFoundException e1) {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        } catch (InstantiationException e1) {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        } catch (IllegalAccessException e1) {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        } catch (UnsupportedLookAndFeelException e1) {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        }
                    }
                });
            }
        });
        panel.add(comboBox);
        panel.add(uiComboBox);
        frame.add(panel);
        frame.setSize(300, 100);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestComboBox().initUI();
            }
        });
    }
}
于 2012-07-20T18:45:58.247 に答える