Java Swing:JList与ListCellRenderer所选项目的高度不同
我正在制作一个自定义 ListCellRenderer。 我知道每个单元格可以有不同的尺寸。 但现在我想为所选单元格设置不同的尺寸。 不知何故,JList 在第一次必须计算每个单元格的边界时会缓存每个单元格的维度。 这是我的代码:
public class Test {
static class Oh extends JPanel {
public Oh() {
setPreferredSize(new Dimension(100, 20));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
static class Yeah extends JPanel {
private boolean isSelected;
public Yeah(boolean isSelected) {
setPreferredSize(new Dimension(100, 100));
this.isSelected = isSelected;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//setSize(100, 100); // doesn't change the bounds of the component
//setBounds(0, 0, 100, 100); // this doesn't do any good either.
if (isSelected) g.setColor(Color.GREEN);
else g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setSize(800, 500);
Vector<Integer> ints = new Vector<Integer>();
for (int i = 0; i < 100; i++) {
ints.add(i);
}
JList list = new JList(ints);
list.setCellRenderer(new ListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (isSelected || ((Integer) value) == 42) return new Yeah(isSelected);
else return new Oh();
}
});
//list.setPrototypeCellValue(null);
//list.setFixedCellHeight(-1);
f.add(new JScrollPane(list));
f.setVisible(true);
}
}
在评论中您可以看到我已经尝试过的内容。
我已经搜索了很长一段时间,发现了很多无用的文章,其中一些涉及 ListCellRenderer/动态高度 的东西,但它们之所以有效,是因为各个单元格的高度保持不变。 我的身高正在发生变化,那么我该怎么办呢?
I'm making a custom ListCellRenderer. I know that you can have different dimensions for each individual cell. But now I want to have a different dimension for the selected cell. Somehow, the JList is caching the dimension for each individual cell the first time it has to calculate bounds for each cell.
This is my code:
public class Test {
static class Oh extends JPanel {
public Oh() {
setPreferredSize(new Dimension(100, 20));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
static class Yeah extends JPanel {
private boolean isSelected;
public Yeah(boolean isSelected) {
setPreferredSize(new Dimension(100, 100));
this.isSelected = isSelected;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//setSize(100, 100); // doesn't change the bounds of the component
//setBounds(0, 0, 100, 100); // this doesn't do any good either.
if (isSelected) g.setColor(Color.GREEN);
else g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setSize(800, 500);
Vector<Integer> ints = new Vector<Integer>();
for (int i = 0; i < 100; i++) {
ints.add(i);
}
JList list = new JList(ints);
list.setCellRenderer(new ListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (isSelected || ((Integer) value) == 42) return new Yeah(isSelected);
else return new Oh();
}
});
//list.setPrototypeCellValue(null);
//list.setFixedCellHeight(-1);
f.add(new JScrollPane(list));
f.setVisible(true);
}
}
In the comments you can see what I've already tried.
I've already searched quite long and found a lot of useless articles, some of them touch the ListCellRenderer/dynamic height thing, but they only work because the height stays the same for the individual cells. My heights are changing, so how do I do this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
基本上,问题有两个方面,都位于 ui 委托中,
解决第一个问题的补救措施确实是渲染器:实现忽略给定的选定标志并查询列表以获取真正的选择,如@Andy所述。 在代码中,使用 OP 的组件
要解决第二个问题,自定义 ui 委托(也如其他答案中所建议的那样)是一种可能的解决方案。 尽管在一般情况下有些工作,但如果需要支持多个 LAF。
强制 ui 自愿更新其缓存的一种侵入性较小但稍微肮脏的方法是在选择更改时发送一个假的 ListDataEvent:
BTW,SwingX 项目有一个自定义的 ui delegate - 主要用于支持排序/过滤 - 使用公共 api 重新计算缓存,然后上面的 ListSelectionListener 将被简化(并且干净:-) 为
Basically, there are two aspects of the problem, both located in the ui delegate
The remedy to fix the first is indeed the renderer: implement to ignore the given selected flag and query the list for the real selection, as outlined by @Andy. In code, using the OP's components
To fix the second, a custom ui delegate (as suggested in others answers as well) is a possible solution. Though some work in the general case, if supporting multiple LAFs is needed.
A less intrusive but slightly dirty method to force the ui into voluntarily update its cache is to send a fake ListDataEvent on selectionChange:
BTW, JXList of the SwingX project has a custom ui delegate - mainly for supporting sorting/filtering - with public api to re-calculate the cache, then the above ListSelectionListener would be simplified (and clean :-) to
我刚刚实现了这个功能。 问题是,单元格渲染器被请求两次来渲染单元格。 在第一轮中,所有列表条目均在不进行选择的情况下呈现,然后使用选择再次呈现选定的单元格。 因此,如果您在第一轮中提供首选大小,它将被缓存并也用于第二轮。
诀窍是忽略
getListCellRendererComponent
中的isSelected
布尔参数,并通过检查list.getSelectedIndices()
是否包含给定索引。但是,我仍然有问题,在列表可见后,渲染组件的高度有时太大/太小。 用鼠标调整列表大小后,一切都恢复正常了。 我尝试过验证/重新验证、重新绘制、重置缓存高度,但没有任何效果。 Swing有时有点奇怪......
I just implemented this feature. The problem is, that the cell renderer is asked twice for rendering a cell. In the first round all list entries are rendered without selection, then the selected cells are rendered again using selection. So if you provide a preferred size in the first round, it is cached and also used for the second round.
The trick is to ignore the
isSelected
boolean parameter in thegetListCellRendererComponent
and to figure out the selection state by checking iflist.getSelectedIndices()
contains the given index.But, I still have the problem, that after the list is made visible, the height of the rendered components are sometimes to large/small. After resizing the list by mouse everything is fine again. I played around with validate/revalidate, repaint, reset of cached heights, but nothing worked. Swing is sometimes a bit strange...
JList 无法根据选择或其他情况更改单元格的大小。 该列表使用“缓存”大小。 如果有新的 cellRenderer,则该大小将被重新计数并应用到列表中的所有单元格中。 我认为原因是包含大量条目的列表的性能。 可能的解决方案是编写自己的 ListUI 实现,它能够对选定和未选定的单元格使用不同的大小。 这还带来了通过对数或其他插值来调整选择周围的单元格大小的可能性。 我希望你有这样做的充分理由。 这是很多工作!
The JList has no ability to change size of cell depending on selection or whatever. The list use "cached" sizes. If there is new cellRenderer provided this sizes are recounted and applied within all cells in list. I think the reason is performance for list with a lot of entries. The possible solution is to write own ListUI implementation which is able to use different sizes for selected and unselected cells. This brings also possibility to adjust size of cells around selection by logarithm or other interpolation. I hope you have a big reason why to do this. It is a lot of work!
我一直在为这个愚蠢的 JList 行高问题抓狂。
我有一个单元格渲染器,它为每一行设置一个可变的行高 - 问题是 JList 保留高度的缓存。
使用其他答案,我想我已经实现了圣杯。 如下所示:
使用 Jaap 创建的 BasicListUI 的简化版本:
然后,当您创建 JList 时 - 像这样扩展它:
根据您的情况,您可能需要在创建过程中在 triggerUpdate 周围放置一个防护。
I've been tearing my hair out about this stupid JList row height problem.
I have a cell renderer which sets a variable row height for every row - problem is that JList keeps a cache of the heights.
Using the other answers, I think I've struck on the holy grail. Here it is:
Use a simplified version of the BasicListUI as created by Jaap:
Then when you create a JList - extend it like this :
You may need to put a guard around the triggerUpdate during creation depending on your circumstances.
感谢 Rastislav Komara,我已经能够很轻松地解决这个问题:
我创建了一个扩展 BasicListUI 的内部类,并创建了在 ListSelectionListener.valueChanged 上调用的公共方法:
updateLayoutState 方法通常在 JList 高度更改时触发。
我在这里做的唯一“疯狂”的事情是我的渲染器需要知道所选索引是什么。 这是因为 updateLayoutState 方法在其高度计算中不使用选定的索引。
不知何故,在 getListCellRendererComponent 中使用 list.getSelectedIndex() 效果不佳。
编辑:
另请检查内夫斯特和克利奥帕特拉的答案,他们看起来更聪明,先尝试一下......
Thanks to Rastislav Komara I've been able to solve this quite easily:
I've created an inner class that extends BasicListUI and created public method that is called on ListSelectionListener.valueChanged:
The updateLayoutState method is normally triggered when the JList height changes.
The only "insane" thing I'm doing here is that my renderer needs to know what the selected index is. This is because the updateLayoutState method doesn't use the selected index in it's height calculations.
Somehow using list.getSelectedIndex() inside getListCellRendererComponent doesn't work well.
Edit:
Check also the anser by nevster and kleopatra, they look way smarter, try them first...
JList 可能正在“缓存”您的单元格渲染器。 尝试附加一个 ListSelectionListener,并在选择更改时再次设置渲染器。
The JList is probably "caching" your cell renderer. Try to attach a ListSelectionListener, and set the renderer again when selection is changed.
这是一个简单的解决方案:
当然您需要编写自己的
ListCellRenderer
实现,并且根据列表元素的不同选择状态,您可以设置返回组件的不同首选高度。只需要继续的一个问题是:当您第一次选择列表的元素时,无法正确绘制。 但在那之后,一切都运转良好。
希望这可以帮到你。
this is a simple solution:
of course you need write your own implementation of
ListCellRenderer
, and according to different selection state of list element, you can set different prefer height of returned Component.Only one issue need to go on is : when you select an element of List FIRST time, not draw correctly. but after then, all work well.
hope this can help you.