Revalidating JList - custom elements
Asked Answered
D

3

7

I'm using a JList to hold chat data for my chat program.
It uses a custom list renderer to render a custom JPanel object as the element type.
This JPanel contains two JLabels (anchored to the top, for name and time), and a JTextArea (anchored to the bottom, for chat message).

It looks like this:

a pic1

Everything works great, but I want to add a hide/show feature.
Using a previously programmed PopupMenu handler, I have a popup appear when you right click on an element.

a pic2

When you click hide (or show, it's a toggle) then it should minimize the element like so...

a pic3

The only problem is... it doesn't update the JList cell size as you can see the large empty region where the text used to be.
However, when I type another message...

a pic4

The JList fixes the cell size completing the 'hide' operation.
My question is how do you get the JList to revalidate/repaint/etc programmatically.
And don't think I haven't tried all the obvious solutions...

public void setHidden(boolean hidden) {
    // this is in the custom JPanel class
    System.out.println("Initial: " + this.getPreferredSize());

    // TextArea is the JTextArea which we set invisible when we want to hide it.
    TextArea.setVisible(!hidden); // TextArea is a variable btw
    this.invalidate();
    this.validate();
    this.repaint();

    System.out.println("After: " + this.getPreferredSize());
    container.revalidate();
}

/*
 * This is what the above printlns show when you hide, then show the element.
 *
 * Initial: java.awt.Dimension[width=176,height=38]
 * After: java.awt.Dimension[width=176,height=20]
 * Initial: java.awt.Dimension[width=176,height=20]
 * After: java.awt.Dimension[width=176,height=38]
 */

public void revalidate() {
    // container.revalidate() ^^^
    // list is the list containing the chat elements
    list.invalidate();
    list.validate();
    list.repaint();
}

The custom JPanel class uses a GroupLayout to render the components.
Do you guys have any knowledge on how to programmically cause a JList to revalidate its cell sizes?
... besides the methods that I've posted? :)

Solution:
After searching method after method and testing if they would solve my problem, I found that executing this code after a hide/show operation would cause the cell height (and width) to be recalculated and without any unwanted visual 'flicker' of the JList.

list.setFixedCellHeight(0);
list.setFixedCellWidth(0);
list.setFixedCellHeight(-1);
list.setFixedCellWidth(-1);
Deandeana answered 23/2, 2012 at 6:50 Comment(2)
please learn java naming conventions and stick to themDespotic
I know the java naming convention, I just like to have my swing variables upper case. But I don't always follow that either...Deandeana
D
3

Without seeing any code, I can only guess: the most probable reason is that you're doing the hide under the feet of the list, that is without its model notifying its listeners. The list's ui delegate caches the cell size deep inside, which is cleared on receiving ListEvents

Despotic answered 23/2, 2012 at 9:58 Comment(1)
You think I'm right-clicking and hiding the element with my cursor physically below the JList? All that happens when I 'hide' the element is the JTextArea (which holds the chat message) being set invisible. So when it's visibility is changed, I need to JList to recalculate its cell size for that element.Deandeana
W
2

This is job for JTable with two Columns (Chat and Boolean) in the TableModel and with visible Chat Column only, the trick is by using by implement RowFilter where you set as parameter to the second column only String "false" (Object in the JTable with Boolean is possible filtering with returns value in the String "true" / "false")

Whelp answered 23/2, 2012 at 7:48 Comment(2)
Your grammer makes your answer hardly understandable. How will this JTable fix the cell resizing? And you have to remember I'm using a custom cell renderer to render this JList. It wouldn't display what it does without it.Deandeana
@Bradley Odell please read tutorial JList about Model (there are data stored) and View and its renderering, not never don't use invalidate()Whelp
F
0

This is a very peculiar shortcoming of the JList class. I ran into the issue myself in the course of cleaning up some of my code in unrelated areas.

For what it's worth, removing the element from the ListModel and then adding it again will produce the appropriate dimensions for the associated rendered component in the JList. It's an odd way to go about it, and seems to cause the list to behave the same way as the accepted (and preferred) solution:

list.setFixedCellHeight(0);
list.setFixedCellWidth(0);
list.setFixedCellHeight(-1);
list.setFixedCellWidth(-1);

I stumbled upon this problem because the code for my project was originally written to invoke the removeAllElements() method of the ListModel and then add all of the elements again one by one using addElement(). Everything was working great until I decided that I should rewrite the program so that it would simply leave the model alone whenever changes to the dimensions of the displayed components in the JList were requested by the user. In other words, it was unnecessary to involve the model because elements were not being added or removed from the list. Unfortunately, after changing the preferred size of the renderer, no amount of repaint() or revalidate() method calls on the JList would cause it to layout its elements correctly. In my case, only resizing the parent component (a JFrame) produced the intended behavior.

Fasano answered 19/7, 2014 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.