I am working on Java the Application of Swing and which i am getting Data and i am using swing Jtable Render for render Image in that but when data is more its hanging all time so what can i do to prevent that?
example render that i am using.
public class DefaultTableCellRenderer extends javax.swing.table.DefaultTableCellRenderer {
JLabel jLabel;
public DefaultTableCellRenderer() {
jLabel = new JLabel();
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean selected, boolean focus, int row, int col) {
try {
if (row == 1) {
jLabel.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("blank.png"))));
jLabel.setText("Image Data");
jLabel.setBackground(Color.LIGHT_GRAY);
} else {
jLabel.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("blank.png"))));
jLabel.setText("Final");
}
//jLabel.setIcon(new ImageIcon(ImageIO.read(new File("blank"))));
return jLabel;
} catch (Exception e) {
e.printStackTrace();
}
return jLabel;
}
#Override
public boolean mouseEnter(Event evt, int x, int y) {
System.out.println(jLabel.getText());
return true;
}
}
This...
jLabel.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("blank.png"))));
Is an expensive call, each time it's called, a new ImageIcon class is created which is wrapping around the BufferedImage data been read. Unlike ImageIcon, ImageIO will not buffer images and re-use them, instead, it will read the resource a new.
This means, that each time the cell is rendered, the image is been fully reloaded.
Since, your loading the same image each time, simple load the image when you construct the class and make use of it when needed, for example...
public class DefaultTableCellRenderer extends javax.swing.table.DefaultTableCellRenderer {
private Icon icon;
public DefaultTableCellRenderer() throws IOException {
icon = new ImageIcon(ImageIO.read(getClass().getResource("blank.png");
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean selected, boolean focus, int row, int col) {
super.getTableCellRendererComponent(table, value, selected, focus, row, col);
setIcon(icon);
if (row == 1) {
setText("Image Data");
setBackground(Color.LIGHT_GRAY); // You may need to set the opaque state for this to work...
} else {
setText("Final");
}
return this;
}
}
Some suggestions for you:
You can load the images at the start of application using multithreading. Use the class Executors to do it. Important is: you must load all the images before you show your UI.
Another possibility is asynchronious loading of images. Use SwingWorker to load images. The loading must be implemented in the method doInBackground(). When some images are loaded you can use the methods publish()/process() to update your table model with new images.
You can combine both and use Executors in doInBackground() method.
I think, your problem is not the CPU load. Your problem is IO. Reading from hard disk is very slow and should be performed in background when it's possible.
Related
I am putting some icons in my jtable's cell but problem is it is changing it's background color to table's background color....i want it to change it to row's background color...and also when it is selected it does't show the selection...Here is my code how i am setting icons....
Users user;
ConsoleUsersListTbl.getColumnModel().getColumn(1).setCellRenderer(new ImageRender());
DefaultTableModel userTableModel = (DefaultTableModel) ConsoleUsersListTbl.getModel();
for (int i = 0; i < userList.size()-1; i++) {
user = userList.get(i);
javax.swing.ImageIcon image_icon = new javax.swing.ImageIcon(user.getUser_image());
if (image_icon.getIconWidth() > 32 || image_icon.getIconWidth() > 32) {
InputStream in = new ByteArrayInputStream(user.getUser_image());
BufferedImage buff_image;
try {
buff_image = ImageIO.read(in);
int type = buff_image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : buff_image.getType();
BufferedImage resizedImage = resizeImage(buff_image, type);
image_icon.setImage(resizedImage);
userTableModel.setValueAt(image_icon, i, 1);
} catch (IOException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
userTableModel.setValueAt(image_icon, i, 1);
}
and here is the out put......
Edit1
here is my Image Render class ;and making setOpaque true makes my icons white..
public class ImageRender extends DefaultTableCellRenderer {
JLabel lable = new JLabel();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
lable.setIcon((ImageIcon)value);
lable.setOpaque(true);
return lable;
}
}
I think you need to implement the custom cell renderer as discussed here and also described here. The renderer has access to information if the current cell is focused or selected, so you can adjust background, foreground or even content any way you want. Mind that components may be opaque (have they own background) or not (the parent background or other content is visible through it). This is controlled through setOpaque(boolean).
I need to add image in Jtable cell without using TableCellRenderer.If i Use the following code means it display the name (string) in that particular cell instead of image.how to do this?.
ImageIcon Icon= new ImageIcon("Blank.gif");
table.setValueAt(Icon,1,0);
using renderer
class FRM_FLXD_ICON_ASSGN extends DefaultTableCellRenderer {
ImageIcon Icon;
public Component getTableCellRendererComponent(
JTable table, Object value, boolean selected, boolean focus,
int row, int col) {
if(selected == true){
Icon=new ImageIcon(getClass().getResource("Pointer.gif"));
}
else{
Icon=new ImageIcon(getClass().getResource("Blank.gif"));
}
this.setIcon(Icon);
return this;
}
}
JTable know Icon/ImageIcon Object, then you can add Icon/ImageIcon directly to the JTable, example
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?
Basically, there are two aspects of the problem, both located in the ui delegate
it fails to configure the renderer to its real state when measuring, that is ignores the selection (and focus) completely
it is notoriously stubborn against being forced to re-calculate the cached cell sizes: it has no public api to do so and only does voluntarily on model changes.
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
ListCellRenderer renderer = new ListCellRenderer() {
Yeah yeah = new Yeah(false);
Oh oh = new Oh();
#Override
public Component getListCellRendererComponent(JList list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
// ignore the given selection index, query the list instead
if (list != null) {
isSelected = list.isSelectedIndex(index);
}
if (isSelected || ((Integer) value) == 42) {
yeah.isSelected = isSelected;
return yeah;
}
return oh;
}
};
list.setCellRenderer(renderer);
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:
ListSelectionListener l = new ListSelectionListener() {
ListDataEvent fake = new ListDataEvent(list, ListDataEvent.CONTENTS_CHANGED, -1, -1);
#Override
public void valueChanged(ListSelectionEvent e) {
JList list = (JList) e.getSource();
ListDataListener[] listeners = ((AbstractListModel) list.getModel())
.getListDataListeners();
for (ListDataListener l : listeners) {
if (l.getClass().getName().contains("ListUI")) {
l.contentsChanged(fake);
break;
}
}
}
};
list.addListSelectionListener(l);
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
ListSelectionListener l = new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
((JXList) e.getSource()).invalidateCellSizeCache();
}
};
list.addListSelectionListener(l);
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 the getListCellRendererComponent and to figure out the selection state by checking if list.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...
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!
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:
public class BetterListUI extends BasicListUI {
public void triggerUpdate() {
updateLayoutState();
}
}
Then when you create a JList - extend it like this :
betterListUI = new BetterListUI();
myJList = new JList() {
#Override
public void repaint(long tm, int x, int y, int width, int height) {
betterListUI.triggerUpdate();
super.repaint(tm, x, y, width, height);
}
};
myJList.setUI(betterListUI);
You may need to put a guard around the triggerUpdate during creation depending on your circumstances.
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:
private class MyRenderer implements ListCellRenderer {
public int listSelectedIndex = -1;
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
if (index == listSelectedIndex)
return new Yeah(isSelected);
else
return new Oh();
}
}
MyRenderer lcr = new MyRenderer();
private class MyListUI extends BasicListUI {
public void triggerUpdate() {
lcr.listSelectedIndex = list.getSelectedIndex();
updateLayoutState();
list.revalidate();
}
}
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...
The JList is probably "caching" your cell renderer. Try to attach a ListSelectionListener, and set the renderer again when selection is changed.
...
addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {
if(event.getValueIsAdjusting() == false) {
list.setCellRenderer(new MyRenderer());
}
}
)
...
this is a simple solution:
public class VariableHeightListUI extends BasicListUI {
#Override
public void paint(Graphics g, JComponent c) {
updateLayoutState();
super.paint(g, c);
}
}
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.
OK, I know how to make a simple custom JComponent. I know how to override a TableCellRenderer. I can't seem to combine the two.
Here's a sample JComponent I created:
public static class BarRenderer extends JComponent
{
final private double xmin;
final private double xmax;
private double xval;
public BarRenderer(double xmin, double xmax)
{
this.xmin=xmin;
this.xmax=xmax;
}
#Override protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Rectangle r = g.getClipBounds();
g.drawRect(r.x, r.y,
(int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
}
public void setXval(double x) {
this.xval = x;
repaint();
}
public double getXval() { return xval; }
}
It works fine as a standalone JComponent. I call setXval(something) and it updates just fine. (edit: I have a Swing Timer that updates the data periodically)
But if this component is something I return in TableCellRenderer.getTableCellRendererComponent(), then it only repaints when I click on the cell in question. What gives? I must be leaving out something really simple.
For performance reasons a JTable reuses renderer components to paint multiple cells - so when you see the component in the JTable it isn't actually there in the traditional sense of a Component in a Container which is present at a location. This means that calling repaint() on the renderer component does nothing.
The most effective option would be to store the Integer value of the bar in your TableModel. Your TableCellRenderer would then look something like this:
public class BarTableCellRenderer implements TableCellRenderer {
private final BarRenderer rendererComponent = new BarRenderer(0, 10);
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
rendererComponent.setXval((Integer)value);
return rendererComponent;
}
}
Then you could change the Integer in your TableModel and it would trigger a repaint of the bar (you may need a TableModel.fireTableCellUpdated dependent on the TableModel implementation you are using).
Both of you (Russ Hayward and Andrew) helped, the key was essentially to do the following:
store the state to be made visible in the TableModel itself, not in the renderer
make sure that when the TableModel's state changes, fireTableCellUpdated() is called
have only one TableCellRenderer object and one JComponent for my custom column (not one per cell)
within TableCellRenderer.getTableCellRendererComponent() store the cell's state for purposes of being rendering soon after (long-term storage is in the TableModel)
provide that state to the JComponent
return the JComponent
override JComponent.PaintComponent()
one convenient possibility is for a custom renderer to extend JComponent and implement TableCellRenderer, then in TableCellRenderer.getTableCellRendererComponent() you store the cell's state and return this;
Here's the relevant excerpt of my code that now works:
class TraceControlTableModel extends AbstractTableModel {
/* handle table state here */
// convenience method for setting bar value (table model's column 2)
public void setBarValue(int row, double x)
{
setValueAt(x, row, 2);
}
}
// one instance of BarRenderer will be set as the
// TableCellRenderer for table column 2
public static class BarRenderer extends JComponent
implements TableCellRenderer
{
final private double xmin;
final private double xmax;
private double xval;
public BarRenderer(double xmin, double xmax)
{
super();
this.xmin=xmin;
this.xmax=xmax;
}
#Override protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Rectangle r = g.getClipBounds();
g.drawRect(r.x, r.y,
(int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
}
#Override
public Component getTableCellRendererComponent(JTable arg0,
Object value,
boolean isSelected, boolean hasFocus,
int row, int col)
{
// save state here prior to returning this object as a component
// to be painted
this.xval = (Double)value;
return this;
}
}
If you make a table with say 3 rows, each having a different Xval, then does it initially renderer correctly, meaning each cell has a different looking bar?
When you say it does not repaint unless you click it, has something happened to your underlying data that should have caused the visual display of the data (the rendered bar) to change?
If the data changed, but the table does not immediatley re-render, then I would say that your TableModel is not working properly.
underlying data changes -> TableModel changes -> fires TableModelEvent -> JTable re-renders
Look at the TableModel tuturial: http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#data
to make sure you are doing everything correct.
Hi I have a class called ColorChooser (in the net.java.dev.colorchooser.ColorChooser package)
This is a custom component used to select colors. What I want is to display a JTable with ColorChoosers in the second column. So I created my own TableCellRenderer and it works:
#SuppressWarnings("serial")
class ColorChooserTableRenderer extends DefaultTableCellRenderer {
public static List<ColorChooser> colors;
public ColorChooserTableRenderer(int rows) {
colors = new ArrayList<ColorChooser>(rows);
for (int i = 0; i<rows ; i ++) {
colors.add(new ColorChooser());
}
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
return colors.get(row);
}
}
I register this in my table :
JTable t = new JTable(5,3);
t.getColumn(t.getColumnName(1)).setCellRenderer(new ColorChooserTableRenderer(5));
The display is good. It even displays the tool tip of the ColorChoosers when i hover my mouse over one of them. The problem is that the ColorChoosers do not receive MouseEvents.
Normally when you press and hold the mouse on a ColorChooser, you get a pop up window that you can use to select a color. When in the JTable the ColorChooser component does not receive the mouse event.
Any solutions?
Edit: The question can be easily modified to this:
Can you please give me a small example of a table containing JButtons in the second column that actually work? You know, buttons that can be pressed?
This sounds vaguely familiar as I have been using table cell renderers for other purposes.
My understanding is that TableCellRenderer is only used to render the component; a component does not actually exist at each of the cells.
So you'd probably have to somehow forward mouse events from the JTable itself to the ColorChooser.
edit: p.s., see my question -- also for custom table cell rendering, you only need 1 instance of the component itself for the entire column, if the column is rendered with the same logic. Don't store persistent state in the TableCellRenderer, store it in the TableModel instead, and use that state immediately prior to rendering when you handle the call to getTableCellRendererComponent().
A renderer only paints the component on the screen and does not allow for interaction. What you need is to also implement a TableCellEditor. It is recommend that you inherit the AbstractCellEditor and you'll save some work. Check out the java tutorial for tables.
Example:
public class MyTableCellRenderer implements TableCellRenderer
{
private JButton button = new JButton("Press Me");
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return button;
}
}
public class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
private JButton button;
public MyTableCellEditor()
{
button = new JButton("Press Me");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("buttonPressed");
}
});
}
public Object getCellEditorValue() {
return null;
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return button;
}
}