Java Swing 中具有设置像素高度的项目的可滚动列表

Scrollable List with items of set pixel height in Java Swing

我正在尝试在 swing 中创建 GUI。 GUI 将保持可变数量的 JPanel 个固定高度堆叠在一起。当堆栈中 JPanel 的总高度大于封闭框架的高度时,用户应该能够在面板中上下滚动。

         ____________________
        |                    |
        |   panel 1          |
        |                    |
        |____________________|
        |                    |
        |   panel 2          |
        |                    |
        |____________________|
     ___|____________________|___
    |   |   panel 3          |   |
    |   |                    |   |
    |   |____________________|   |     ^
    |   |                    |   |     |
    |   |   panel 4          |   |     |
    |   |                    |   |     |
    |   |____________________|   |     v
    |   |                    |   |
    |   |   panel 5          |   |
    |___|____________________|___|
        |____________________|
        |                    |
        |   panel 6          |
        |                    |
        |____________________|

我试过将 JScrollPaneJPanelGridLayoutGridBagLayout 组合使用,并尝试调用 setPreferedSizesetMinimumSize 没有成功。我觉得设置这种 gui 应该相当简单,但似乎无法找到解决方案。

如果您使用 JPanel,那么视口持有的 JPanel 应该覆盖 JPanel class 并且应该实现 Scrollable 接口,为覆盖的方法返回正确的值,尤其是 getPreferredScrollableViewportSize()方法。不过,为了我的钱,我更愿意使用 JList 并使用适当的渲染器而不是 JPanel,然后通过其 setVisibleRowCount(...) 方法设置我的 JLists visibleRowCount。

例如:

示例编辑为现在显示 JList 和 Scrollable JPanel

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.*;

@SuppressWarnings("serial")
public class VisibleRows extends JPanel {
    private DefaultListModel<Datum> dataModel = new DefaultListModel<>();
    private JList<Datum> datumList = new JList<>(dataModel);

    public VisibleRows() {
        DataPanel dataPanel = new DataPanel(8);
        for (int i = 0; i < 200; i++) {
            String name = "John Smith " + i;
            int value = i;
            Datum datum = new Datum(name, value);
            dataPanel.addDatum(datum);
            dataModel.addElement(datum);
        }

        JScrollPane scrollPane1 = new JScrollPane(dataPanel);
        scrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        add(scrollPane1);

        datumList.setVisibleRowCount(8);
        datumList.setCellRenderer(new DatumRenderer());
        datumList.setPrototypeCellValue(new Datum("XXXXXXXXXXX", 200));
        JScrollPane scrollPane2 = new JScrollPane(datumList);
        scrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        add(scrollPane2);

    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("VisibleRows");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new VisibleRows());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

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

@SuppressWarnings("serial")
class DatumRenderer extends DatumPanel implements ListCellRenderer<Datum> {

    @Override
    public Component getListCellRendererComponent(JList<? extends Datum> list, Datum value, int index,
            boolean isSelected, boolean cellHasFocus) {
        setDatum(value);
        return this;
    }

}

@SuppressWarnings("serial")
class DataPanel extends JPanel implements Scrollable {
    private int visibleRowCount = 1;

    public DataPanel(int visibleRowCount) {
        this.visibleRowCount = visibleRowCount;
        setLayout(new GridLayout(0, 1));
    }

    public void addDatum(Datum datum) {
        add(new DatumPanel(datum));
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        if (getComponentCount() > 0) {
            JComponent comp = (JComponent) getComponents()[0];
            int width = getPreferredSize().width;
            int height = visibleRowCount * comp.getPreferredSize().height;
            Dimension d = new Dimension(width, height);
            System.out.println(d);
            return d;
        } else {
            return new Dimension(0, 0);
        }
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        if (getComponentCount() > 0) {
            JComponent comp = (JComponent) getComponents()[0];
            Dimension d = comp.getPreferredSize();
            if (orientation == SwingConstants.VERTICAL) {
                return d.height;
            } else {
                return d.width;
            }
        }
        return 0;
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        if (getComponentCount() > 0) {
            JComponent comp = (JComponent) getComponents()[0];
            Dimension d = comp.getPreferredSize();
            if (orientation == SwingConstants.VERTICAL) {
                return visibleRowCount * d.height;
            } else {
                return d.width;
            }
        }
        return 0;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return true;
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }

}

@SuppressWarnings("serial")
class DatumPanel extends JPanel {
    private static final int GBC_I = 3;
    private Datum datum;
    private JLabel nameLabel = new JLabel();
    private JLabel valueLabel = new JLabel();

    public DatumPanel() {
        setLayout(new GridBagLayout());
        add(new JLabel("Name:"), createGbc(0, 0));
        add(nameLabel, createGbc(1, 0));
        add(new JLabel("Value:"), createGbc(0, 1));
        add(valueLabel, createGbc(1, 1));
    }

    public DatumPanel(Datum datum) {
        this();
        setDatum(datum);
    }

    public final void setDatum(Datum datum) {
        this.datum = datum;
        nameLabel.setText(datum.getName());
        valueLabel.setText("" + datum.getValue());
    }

    public Datum getDatum() {
        return datum;
    }

    private GridBagConstraints createGbc(int x, int y) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = x;
        gbc.gridy = y;
        gbc.insets = new Insets(GBC_I, GBC_I, GBC_I, GBC_I);
        gbc.insets.left = x != 0 ? 3 * GBC_I : GBC_I;
        gbc.weightx = 0.0;
        gbc.weighty = 0.0;
        return gbc;
    }
}

class Datum {
    private String name;
    private int value;

    public Datum(String name, int value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public int getValue() {
        return value;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Name: " + name + "\n");
        sb.append("Value: " + value);
        return super.toString();
    }

}
import javax.swing.*;

public class ButtonsInScrollPane{

    public static void main(String[] args){
        JFrame frame = new JFrame();
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
        p.add(getJButton(p));
        JScrollPane scroll = new JScrollPane(p);
        frame.setContentPane(scroll);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(200, 200);
        frame.setVisible(true);
    }

    static public JButton getJButton(JPanel p){
        JButton b = new JButton("more");
        b.addActionListener(evt->{
            p.add(getJButton(p));
            p.revalidate();
            p.repaint();
            });
        return b;
    }
}

点击按钮几次,按钮太多,然后会激活滚动。它应该几乎正是你所问的。