JList单独大小的单元格

时间:2015-09-02 22:05:43

标签: java swing

我有一个水平排列的JList。列表中的所有项目都具有显着不同的大小,默认情况下,渲染器将每个项目缩放为列表中最大项目的大小。我试图按如下方式实现自定义渲染器,但列表中的每个项目仍保持相同的大小。有什么建议吗?

以下是ListCellRenderer:

{$IFDEF CONDITIONALEXPRESSIONS}

3 个答案:

答案 0 :(得分:0)

尝试使用-1作为参数调用JList方法setFixedCellHeight。这会导致JList在绘制列表项时使用任何自定义渲染器返回的首选高度。

答案 1 :(得分:0)

我认为您可以尝试继承javax.swing.plaf.basic.BasicListUI并覆盖protected void paintCell(Graphics g, int row, Rectangle rowBounds, ListCellRenderer cellRenderer, ListModel dataModel, ListSelectionModel selModel, int leadIndex),也可以尝试其他一些方法来执行单元格大小计算。

然后使用JList.setUI(ListUI)应用您的自定义用户界面。 UI负责使用ListCellRenders绘制JList。请原谅我没有提供完整的例子,因为它可能是很多工作,调整了自定义绘图过程的所有方面。

答案 2 :(得分:0)

您将需要三件事:

    每个单独单元格的比例因子。例如,您可以在列表的每个条目中添加类型double的比例因子变量。
  1. 自定义ListCellRenderer
  2. 将方法BasicListUI#updateLayoutState()公开。

遵循独立代码(有关详细信息,请阅读注释):

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.Icon;
import javax.swing.JButton;
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.plaf.basic.BasicListUI;

public class JListIndipendentCellSizes {

    //Configuration constants:
    private static final int ICON_SIZE = 20;
    private static final double SCALE_STEP_SIZE = 0.125; //Smaller values of this makes zooming slower. Greater values makes zooming faster.

    //Simple Icon implementation for demonstration purposes.
    public static class TJIcon implements Icon {
        private final Color rectColor, ovalColor;

        public TJIcon(final Color rectColor, final Color ovalColor) {
            this.rectColor = rectColor;
            this.ovalColor = ovalColor;
        }

        @Override
        public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
            g.setColor(rectColor);
            g.fillRect(x, y, getIconWidth(), getIconHeight());
            g.setColor(ovalColor);
            g.fillOval(x, y, getIconWidth(), getIconHeight());
        }

        @Override public int getIconWidth() { return ICON_SIZE; }
        @Override public int getIconHeight() { return ICON_SIZE; }
    }

    //A simple list entry. Contains a text, an icon and (on top of them all) the scale factor:
    public static class TJListEntry {
        private final String text;
        private final Icon icon;
        private double scaleFactor;

        public TJListEntry(final String text,
                           final Icon icon) {
            this.text = text;
            this.icon = icon;
            scaleFactor = 1;
        }

        public String getText() {
            return text;
        }

        public Icon getIcon() {
            return icon;
        }

        public double getScaleFactor() {
            return scaleFactor;
        }

        public void zoomIn() {
            scaleFactor = scaleFactor + SCALE_STEP_SIZE;
        }

        public void zoomOut() {
            scaleFactor = Math.max(scaleFactor - SCALE_STEP_SIZE, SCALE_STEP_SIZE); //Do not allow underflow.
        }

        public void resetZoom() {
            scaleFactor = 1;
        }
    }

    public static class TJListCellRenderer extends JLabel implements ListCellRenderer<TJListEntry> {
        private double currentScaleFactor;

        public TJListCellRenderer() {
            //Ensure every pixel is painted starting from the top-left corner of the label:
            super.setVerticalAlignment(JLabel.TOP);
            super.setHorizontalAlignment(JLabel.LEFT);
            //We need to do this, because the scaling in paintComponent() is also relative to the top-left corner.
        }

        @Override
        public void paintComponent(final Graphics g) {
            //setRenderingHints here? Probably for ANTIALIAS...
            ((Graphics2D)g).scale(currentScaleFactor, currentScaleFactor); //Let's scale everything that is painted afterwards:
            super.paintComponent(g); //Let's paint the (scaled) JLabel!
        }

        @Override
        public Dimension getPreferredSize() {
            final Dimension superPrefDim = super.getPreferredSize(); //Handles automatically insets, icon size, text font, etc.
            final double w = superPrefDim.width * currentScaleFactor, //And we just scale the preferred size.
                         h = superPrefDim.height * currentScaleFactor; //And we just scale the preferred size.
            return new Dimension((int)w + 1, (int)h + 1); //Add one extra pixel to spare.
        }

        @Override
        public Component getListCellRendererComponent(final JList<? extends TJListEntry> list, final TJListEntry value, final int index, final boolean isSelected, final boolean cellHasFocus) {

            currentScaleFactor = value.getScaleFactor(); //Probably the most important step.

            setIcon(value.getIcon()); //Could be a loaded ImageIcon here (i.e. image)!
            setText(value.getText());

            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            setOpaque(true);

            return this;
        }
    }

    public static class TJListUI extends BasicListUI {
        @Override
        public void updateLayoutState() {
            super.updateLayoutState(); //Just make the following method public.
            /*Note: this is not really needed here:
            The method could remain protected, but in the case you want this
            code to be a bit more reusable, then you shall make it public.*/
        }
    }

    public static void main(final String[] args) {
        final TJListEntry[] entries = new TJListEntry[]{new TJListEntry("This is a sample text.", new TJIcon(Color.BLACK, Color.WHITE)),
                                                        new TJListEntry("This is a longer sample text.", new TJIcon(Color.GREEN, Color.RED)),
                                                        new TJListEntry("Small text", new TJIcon(Color.LIGHT_GRAY, Color.BLUE))};

        final TJListUI ui = new TJListUI();

        final JList<TJListEntry> list = new JList<>(entries);
        list.setUI(ui); //Important step! Without setting our UI, it won't work (at least as I have looked for).
        list.setCellRenderer(new TJListCellRenderer()); //Important step! Without setting our custom ListCellRenderer you will have to implement your own ListUI probably.

        final JScrollPane scroll = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        final JButton buttonZoomIn = new JButton("Zoom in selected cells"),
                      buttonZoomOut = new JButton("Zoom out selected cells"),
                      buttonResetZoom = new JButton("Reset zoom of selected cells");

        buttonZoomIn.addActionListener(e -> {
            for (final int i: list.getSelectedIndices())
                list.getModel().getElementAt(i).zoomIn();
            ui.updateLayoutState(); //Read the preferred sizes from the cell renderer.
            list.revalidate(); //Update the JScrollPane.
            list.repaint(); //Repaint the list.
        });

        buttonZoomOut.addActionListener(e -> {
            for (final int i: list.getSelectedIndices())
                list.getModel().getElementAt(i).zoomOut();
            ui.updateLayoutState(); //Read the preferred sizes from the cell renderer.
            list.revalidate(); //Update the JScrollPane.
            list.repaint(); //Repaint the list.
        });

        buttonResetZoom.addActionListener(e -> {
            for (final int i: list.getSelectedIndices())
                list.getModel().getElementAt(i).resetZoom();
            ui.updateLayoutState(); //Read the preferred sizes from the cell renderer.
            list.revalidate(); //Update the JScrollPane.
            list.repaint(); //Repaint the list.
        });

        final JPanel buttons = new JPanel(); //FlowLayout.
        buttons.add(buttonZoomIn);
        buttons.add(buttonZoomOut);
        buttons.add(buttonResetZoom);

        final JPanel panel = new JPanel(new BorderLayout());
        panel.add(buttons, BorderLayout.PAGE_START);
        panel.add(scroll, BorderLayout.CENTER);

        final JFrame frame = new JFrame("Independent JList cell sizes demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

和屏幕截图:

Sample screenshot

要单独运行它,只需选择至少一个索引,然后按一下按钮即可播放。