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:
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.
When you click hide (or show, it's a toggle) then it should minimize the element like so...
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...
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);
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
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")
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.
Related
I have a Swing application that shows a list of complex objects to the user. These are nicely rendered using a ListCellRender, which fills a JPanel with more UI controls. Obviously editing does not work and the components are not enabled to accept input.
Now I want the user to be able to edit the entries. Basically you could think of in-place editing. I tried to simply enable the panel that renders the list entries - but it does not work. What else could/should I do to have an editable list?
So basically the answer is to favour JTable over JList. For anyone questioning that, the JTable can be configured to show one column only, and the difference would not be visible to the user.
Programming wise JTable is more complex and thus justifies that JList is used in simple cases (only one columne, no editing required).
I will try to describe the problem as accurately as possible, since this is a legacy project from someone else that I am trying to fix (to the best of my abilities).I cannot build an accurate working example due to the way this project is done. The way this program was built is wrong in many places so just pinpointing the problem would help me immensely.
There is a Jframe with a menu bar and table. The frame switches between Tables based on which option you click within the menu bar. Here's the problem: When updating the table, the first table seems to be fine, but the other ones fails to update its view properly if something is changed (specifically, it doesn't update upon deletion, but insertion seems fine). This is how the main table is made:
public void start(){
mainTable=new Jtable(model){
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
/* tons of styling here */
};
}
/* a bit more styling? */
renderSecondTable();
renderThirdTable();
}
I apologize for this ugly code, the codebase itself is even worse. Does rendering other tables by calling a method within this "start()" function lead to concurrency problems? Which is why updating the view is problematic? Extra information that might help:
each table has a seperate Frame, so there is a main frame and then a smaller frame that displays the tables
buttons for each table (delete, update etc.) are also added in by calling in extra methods like RenderButtonsForTable1()
They are all AbstractTableModel
calling FireTableDataChanged() or other similar functions do not seem to do work for non-main-tables
revalidate and repaint also doesn't work
The table does NOT use removeRow or fireTableRowsDeleted to remove the rows, there are seperate functions that do this with object streams (I don't quite understand this yet). The view seems to update automatically for the main table just fine.
Again I apologize if this is extremely confusing and ugly, the original creator didn't seem to understand what he was doing either :/
Edit: More clarification!
It seems that it does in fact only have one MAIN frame, and this
frame swaps out multiple Jpanels (each with a different Table). Within each Panel there is also a scrollpane
Deleting from the main Panel causes no problems, but deleting entries from other Panels require the Frame to be reopened or else the entry just stays there (also breaks the frame/panel completely if you empty the entire table)
The entry is properly disposed of in the backend (as far as I can tell)
any insert/delete is done through an action listener for the corresponding button
Each table has a seperate Tablemodel (all extends AbstractTableModel) with its seperate functions that all do the same thing
Table is filled with data read from a database
all tables are made in the same fashion as the main one above with prepareRenderer
I also drew up a vague diagram of how the Jframe looks:
As the title says I'm trying to make it so my JTextArea have some strings so that I can call other functions, like a hyperlink of sorts, can that be done?
For reference, I would like to know how the left side of any Tutorialspoint tutorial is made but in Java.
PS: I'm using a CardLayout.
EDIT: Something like this
Forget about using a JTextArea. Take a look at JList instead. This is Swing's basic list class. It supports the selection of one or more items from a list. Although often the list consists of strings, it is possible to create a list of just about any object that can be displayed.
Although a JList will work properly by itself, most of the time you will wrap a JList inside a JScrollPane, so long lists will automatically be scrollable.
A JList generates a ListSelectionEvent when the user makes or changes a selection. This event is also generated when the user deselects a n item. It is handled by implementing ListSelectionListener. This listener specifies only one method, which is called:
void valueChanged(ListSelectionEvent le)
Here, le is a reference to the object that generated the event. Although ListSelectionEvent does provide some methods of its own, often you will interrogate the JList object itself to determine what has occurred.
By default, a JList allows the user to select multiple ranges of items within the list, but you can change this behavior by calling setSelectionMode(int), which is defined by JList. The integer passed to this method must be one of the values defined by the ListSelectionModel interface:
SINGLE_SELECTION
SINGLE_INTERVAL_SELECTION
MULTIPLE_INTERVAL_SELECTION
You can obtain the selected values by calling getSelectedValuesList(), or, if you are using single selection, you can call getSelectedValue(). Once you have the selected the value(s), you can invoke the method(s) dedicated to that/those objects accordingly.
One last tip: In Java, they are called methods, not functions ;)
Happy programming!
All Swing components in my app (except labels) have tooltips that can be annoying once the user knows what's going on, so I have a Preferences menu that allows turning them off. I could name every component and set its tooltip text to "" [e.g., txtPattern.setToolTipText("");] (and 10 others), but I decided (with SO aid that started awhile back) to write code that would be more elegant (a learning experience):
private void tipsOff(Container container){
Component [] c = container.getComponents();
for (Component cc : c)
((JComponent)cc).setToolTipText("");
}
private void mniPrefTooltipsActionPerformed(java.awt.event.ActionEvent evt) {
if(! mniPrefTooltips.isSelected()){
tipsOff(gui.getContentPane());
tipsOff(gui.pnlLetters);
tipsOff(gui.mbrMenuBar);
}
else{
gui.dispose();
gui = new IO();
gui.setVisible(true);
}
}
I have a problem, which is that the tooltips are NOT turned off for the two large text areas at the bottom of the gui (highlighted in the Navigator pane). The two buttons (marked with green in Nav. pane) ARE processed correctly. These items are supposed to be processed via the first call to tipsOff, which processes gui.getContentPane()).
(I added the two lines bellow to try to rectify the problem. Nope.)
tipsOff(gui.scrOutput);
tipsOff(gui.scrScratch);
(Also tried this. Nope.)
tipsOff(gui.txaOutput);
tipsOff(gui.txaScratchwork);
How can I elegantly (i.e., assume I have many text areas, not just 2) turn off the text area tooltips?
P.S. I get the message Access of private field of another object for all but the first call to tipsOff. I don't care, owing to the nature of the task at hand.
Use ToolTipManager.sharedInstance().setEnabled( false ) to disable all tool tips in your Swing application.
Benefits compared to your approach
It works :-)
You do not clear the tooltips, so it is easy to re-enable them again. For example if you want to offer UI to your user to activate/de-activate the tooltips this approach will work. In your approach, you would have to restore all the tooltips you previously cleared, which would be difficult to do in a generic way.
I've enabled the Drag'n'Drop facilities of a JTable by setting a TransferHandler to it. Now I can add items and reorder them inside this table. For easier adding items from another location (let's say windows desktop) I also set a DropTarget to the JPanel which holds the table and some other components. This DropTarget simply forwards the Transferable to the table's TransferHandler which adds the item at the end of the list:
panel.setDropTarget(new DropTarget(table, new DropTargetAdapter(){
#Override
public void drop(DropTargetDropEvent dtde){
TransferSupport transferSupport = new TransferHandler.TransferSupport(table, dtde.getTransferable());
table.getTransferHandler().importData(transferSupport);
}
}));
I've instantiated 4 of these panels so I can both add items from outside and move them from one to another panel.
That works for me, except the fact, that it performs always a COPY, not a MOVE. If I ask the DropTargetDropEvent for the DropAction with getDropAction() an exception with "Not a Drop" is thrown. (strange: Why exists a DropTargetDropEvent which isn't a drop?)
An exportDone() is also implemented and works correct when I drop directly into the body of the table. When dropping on the underlaying panel, exportDone() is invoked too, but with SourceAction==NONE (int 0).
What am I doing wrong? What is the right way to perform the right (COPY or MOVE) action when dropping on one of my panels?