我有一个水平排列的JList。列表中的所有项目都具有显着不同的大小,默认情况下,渲染器将每个项目缩放为列表中最大项目的大小。我试图按如下方式实现自定义渲染器,但列表中的每个项目仍保持相同的大小。有什么建议吗?
以下是ListCellRenderer:
{$IFDEF CONDITIONALEXPRESSIONS}
答案 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
的比例因子变量。
ListCellRenderer
。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);
}
}
和屏幕截图:
要单独运行它,只需选择至少一个索引,然后按一下按钮即可播放。