Ограничьте ширину JScrollpane, содержащей JList

Я создал JList внутри JScrollPane, который использует пользовательский ListCellRenderer. ListCellRenderer содержит Jlabel, который оборачивает свое содержимое с помощью HTML, поэтому текст автоматически переносится. Без использования JScrollPane перенос работает, но когда я использую JScrollPane, текст не переносится и появляется горизонтальная полоса прокрутки.

Выдержка из моего ListCellRenderer:

public class WorkableCellRenderer implements ListCellRenderer<Workable> {

    @Override
    public final Component getListCellRendererComponent(final JList<? extends Workable> list, final Workable value,
            final int index, final boolean isSelected, final boolean isFocused) {
        JPanel cell = new JPanel();
        cell.setLayout(new BoxLayout(cell, BoxLayout.Y_AXIS));

        // ...

        JPanel pnlDescription = new JPanel();
        cell.add(pnlDescription);
        pnlDescription.setLayout(new BorderLayout());

        JLabel lblDescription =
                new JLabel("<html><p style=\"margin: 0; padding: 0;\">" + value.getDescription() + "</p></html>");
        lblDescription.setFont(lblDescription.getFont().deriveFont(Font.PLAIN));
        pnlDescription.add(lblDescription, BorderLayout.CENTER);

        // ...

        return cell;
    }

}

Метод, который добавляет мой список в JFrame:

private void addLoadedModulesPanel() {
    pnlLoadedModulesWrapper = new JPanel();
    pnlBottom.add(pnlLoadedModulesWrapper, BorderLayout.CENTER);
    pnlLoadedModulesWrapper.setLayout(new MigLayout("insets 0 5 5 5", "[grow]", "[grow]"));

    pnlLoadedModulesBox = new JPanel();
    pnlLoadedModulesWrapper.add(pnlLoadedModulesBox, "cell 0 0,grow");
    pnlLoadedModulesBox.setLayout(new MigLayout("insets 3", "[grow]", "[grow]"));
    pnlLoadedModulesBox.setBorder(BorderFactory.createTitledBorder("Geladene Module"));

    lstLoadedModules = new JList<Workable>(DefaultWorkableManager.getInstance());
    lstLoadedModules.setCellRenderer(new WorkableCellRenderer());

    final JScrollPane listScroller = new JScrollPane(lstLoadedModules);

    pnlLoadedModulesBox.add(listScroller, "cell 0 0,grow");
}

Я думаю, что проблема в окне просмотра JScrollPane, потому что оно больше, чем сам JScrollPane. Как я могу настроить ширину области просмотра так, чтобы она всегда была такой же ширины, как JScrollPane?

Вот MCVE, который показывает мою проблему:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.border.EmptyBorder;

public class TestFrame extends JFrame {

    private static final long serialVersionUID = -5281126214046039839L;

    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    final TestFrame frame = new TestFrame();
                    frame.setVisible(true);
                } catch (final Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public TestFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200, 500);
        getContentPane().setLayout(new BorderLayout());

        final JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BorderLayout());
        mainPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
        getContentPane().add(mainPanel);

        final DefaultListModel<String> lm = new DefaultListModel<>();
        final JList<String> list = new JList<>(lm);
        list.setCellRenderer(new MyListCellRenderer());

        final JScrollPane scrollPane = new JScrollPane(list);
        mainPanel.add(scrollPane);

        for (int i = 1; i < 21; i++) {
            lm.addElement(String.valueOf(i));
        }
    }

    private class MyListCellRenderer implements ListCellRenderer<String> {

        private final JPanel panel = new JPanel();
        private final JLabel label = new JLabel();

        public MyListCellRenderer() {
            panel.setLayout(new BorderLayout());
            panel.setBackground(new Color(16777215));
            panel.add(label);
        }

        @Override
        public Component getListCellRendererComponent(final JList<? extends String> list, final String val,
                final int index, final boolean isSelected, final boolean hasFocus) {
            label.setText("<html><p>Item number " + val
                    + " contains some very long text, that ideally should wrap.</p></html>");

            if (isSelected) {
                panel.setBackground(new Color(5323));
            } else {
                panel.setBackground(new Color(16777215));
            }

            return panel;
        }
    }

}

person stevecross    schedule 30.04.2014    source источник
comment
Чтобы быстрее получить помощь, опубликуйте MCVE (минимальный полный и проверяемый пример). Вам нужно будет исключить MigLayout (использовать стандартные макеты JSE), чтобы сделать его MCVE, который большинство людей сможет быстро опробовать.   -  person Andrew Thompson    schedule 30.04.2014
comment
несвязанный: никогда не создавайте новый компонент при каждом вызове getXXRenderer, вместо этого создайте его один раз и настройте его по мере необходимости (в этом весь смысл механизма рендеринга)   -  person kleopatra    schedule 30.04.2014


Ответы (2)


все зависит ..., тогда JList в JScrollPane может вернуть правильный getPreferredSize (или напрямую с помощью JList.setPrototypeCellValue) для используемых LayoutManager

  1. какое значение для getPreferredSize возвращает ListCellRenderer, это PreferredSize одинаково для всех элементов или нет

  2. как устанавливается для JList.setLayoutOrientation ()

  3. используются ли ограничения для видимой строки или нет JList.setVisibleRowCount()

person mKorbel    schedule 30.04.2014
comment
Я не звоню JList.setVisibleRowCount() или JList.setLayoutOrientation(). У ListCellRenderer нет метода getPreferredSize(). - person stevecross; 30.04.2014
comment
1. getPreferredSize (который по умолчанию возвращает все JComponents в LayoutManager) является концентрацией моего 3-х точечного ярлыка setPrototypeCellValue, 2. не воссоздавать JComponents в ListCellRenderer (в комментарии kleopatra) 3. почему есть JPanel с JLabes, 4. ничего не указывает на вашу цель, все (возможный ответ) касается угадывания, 5. помимо того, что ваша модель может быть неправильно спроектирована (угадывание, увидев woodoo в Renderer) - person mKorbel; 30.04.2014
comment
Я добавил MCVE, который делает то же самое, что и моя исходная программа. - person stevecross; 30.04.2014
comment
проблема в используемом LayoutManager, чтобы попытаться перейти на другой LayoutManager, опять же, есть ли какие-либо причины для рисования JPanel с Jlabel в Renderer - person mKorbel; 30.04.2014
comment
Исходная панель содержит некоторые другие метки, но они не являются обтекаемыми. - person stevecross; 30.04.2014
comment
что происходит с GridBagLayout/BoxLayout, см. (в отличие от) связанный код в одном из моих комментариев здесь - person mKorbel; 30.04.2014

думаю, что проблема заключается в области просмотра JScrollPane, потому что она больше, чем сама JScrollPane. Как настроить ширину области просмотра так, чтобы она всегда имела ту же ширину, что и JScrollPane?

Вам необходимо реализовать интерфейс Scrollable на вашей панели, чтобы ограничить ширину панели шириной панели прокрутки.

Или вы можете использовать прокручиваемую панель, которая предоставляет несколько методов, позволяют контролировать это поведение.

person camickr    schedule 30.04.2014