File APIs for Java Developers
Manipulate DOC, XLS, PPT, PDF and many others from your application.
http://aspose.com/file-tools
The moose likes Swing / AWT / SWT and the fly likes How to color JTable cell without a renderer? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "How to color JTable cell without a renderer? " Watch "How to color JTable cell without a renderer? " New topic
Author

How to color JTable cell without a renderer?

Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
Good Evening!I have a question, i think is a hard one(i am new in Java, but i am more than a month trying to make that work...):
I need to color cells in a JTable after pressing a JButton and doing some comparison.In another words:
the user write a number in some cells and press the button Analize and paint the cells after some value comparison.They can be red or blue.I tried using renderers like that:

And doing some comparison in a method like that:


disparing an event in JButton:


My problems:
1)-It colors everytime i change a value in every cell(instead after i press JButton).
2)-After i press JButton only the last line of my JTable is updated correctly.
I know that behaviour is expected when using renderers... there is other way to do what i want?
I googled and didn t find anything helpful...a snippet of code will be apreciated.


Best Wishes for all,
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3610
    
  60

Welcome to the Ranch, Paul!

The correct way is to use cell renderers. I see two very general directions:

1) Use a single renderer and implement the display logic in full there. That is, the renderer will have a look at which cell (row, column) is being drawn and also whether the Analyze button was pressed. It will use the correct drawing style for each situation.

2) Create smaller renderers designed for representing a single state, and override JTable.getCellRenderer(). In this method, you'll return proper renderer depending on row, column and a button state.

A combination of the two approaches is also possible.

Be careful when inspecting column and row indexes, if your table uses row filter/sorter, or allows column reordering, you might need to translate them to table coordinates.

Also, in my opinion, the information about the Analyze button (whether it was pressed) should be part of the table model. That way all the data used to draw the table are kept firmly together the model will be able to fire proper table update event and make sure everything is redrawn when data changes.
Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
Martin Vajsar , Thanks for the welcome!

1)Do you mean mix MyRenderer with method updateColors?How can i avoid to paint each time i edit a cell(writing the number)?

2)Basically, i paint of two colors:RED and BLUE(negative and positive), but using renderer, always change when i change a number in a cell.I want that behavior only when i press my JButton.

Can you post a snippet of your thoughts?

Best Wishes,
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18541
    
    8

Paul Hayabusa wrote:How can i avoid to paint each time i edit a cell(writing the number)?


Why is this a requirement? I don't see any need for dragging that into the code. Just write an ordinary cell renderer which renders a cell on demand and let the Swing code do its work.

Basically what should happen is that when you press the JButton, that changes something in your table model. Then you should call the model's method which notifies its listeners that a cell (or cells) have been changed. At this point the JTable will call the renderer for any cells which have changed. The key point is, everything the renderer needs to know should be available from the table model.
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3610
    
  60

If you're looking for an example, I'd suggest Oracle's JTable tutorial, there are lots of them.

Paul is right - you don't have to care about repainting, if you implement the table model correctly. The correct implementation means that the model advertises all changes to the underlying data. If you're extending AbstractTableModel, you should use the fireTableXxx methods for this purpose.

The renderer must be able to paint the cell correctly at any time it is asked to. If your requirement is to change the colors only after a button is pressed, then you need to store the colors in your table model, recompute the colors after each press of that button and use the fireTableXxx methods to let the table know the data in your model have changed. You cannot derive the coloring from the actual values in cells, because the table might need to repaint itself before the button was pressed, at which time the colors you want to see are not in accord with the cell value (which, I believe, is the difficulty you're encountering just now).
Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
You cannot derive the coloring from the actual values in cells, because the table might need to repaint itself before the button was pressed, at which time the colors you want to see are not in accord with the cell value (which, I believe, is the difficulty you're encountering just now).

Martin, sorry to bother you, but can you explain a little better?

I made some changes, put all the code changing colors in a JButton, but it changes the colors without pressing JButton....
I was thinking if there is a way to disable/enable the renderer?
Something like:


Best Regards,
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3610
    
  60

No, it is not possible to achieve your goals by disabling renderers.

The problem is that you can't control when exactly the table (or any other control) will be painted. It is the Swing framework which takes care of it. And if, for example, a form gets resized, some or even all components on the form might need partial or full repaint, including your table. Therefore, if you "disable the renderer" and immediately after this Swing decides that the table must be painted (and you really cannot ever get full control over that), the table wouldn't have means to paint itself. At worst it would crash, at best it would not update itself, which might or might not be obvious right away.

You must design your table to conform to this, which generally means you must be able to fully repaint the table in the state it was last painted. So if you want some change to appear only after a button is pressed, your renderers must paint the table "in the old way" until that button is actually pressed. After it is pressed, you can "tell" the the renderers to draw the table the new way and call repaint on the table (or use the table model mechanism I've mentioned earlier to let the table know it should repaint itself).

Let me know whether this explanation is better than the last one

Edit: it probably would be possible to alter the way table is drawn by setting a different renderer, and perhaps you could achieve your goal this way. But if you want to provide different renderers at different time, perhaps a better approach would be to override JTable.getCellRenderer(), as I've also already mentioned.
Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
I changed my first approach and was using all code within JButton and doing the comparison that way(in the same block):

I will try to separate them again using JTable.getCellRenderer()

Martin, Thanks for your help!
Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
Hey, i think i found a bit of my mistakes...i put before my analyze JButton a renderer to center all cells and editors for each editable cell:

Am i having some type of renderer conflict?

Best Regards,
Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
Martin,
i made a Class (AnalyzeColor) separating each renderer and doing a comparison in a proper method.The comparison/analyzing works flawless,but i still have the problem when i edit the cell.I tried a workaround in my CellEditorClass, doing:


But dont works fine.I think i am almost there, but i cant find where is my mistake.A thing i dont understand in your statement:
must paint the table "in the old way" until that button is actually pressed

How is that?I should disable repaint method?

Best Regards,
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3610
    
  60

Paul, I'm afraid I got a bit lost in this thread. What problem(s) are you experiencing right now?

Please note that renderers are not used when the cell is being edited. There are cell editors which are responsible for handling and displaying the cell contents while the cell is being edited. If you want to alter the color of the cell while it is being edited, you need to do it by customizing its cell editor. The JTable tutorial mentioned above shows how cell editors work.
Paul Hayabusa
Ranch Hand

Joined: Jan 17, 2013
Posts: 45
Martin,
I will change my CellEditor.
i have a weird behavior right now when i tested my JTable with more than one row:
Only the LAST row in my JTable is showing comparison results(being painted)!It should show ALL rows(i mean the 3 cells in each row) painted!
A recap of my work:
//My Button:

My changeColors method:


Note:The comparison works fine, but i dont get it the why only my last row is updated and not all of them.

Best Wishes,
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3610
    
  60

Paul, the problem is this: a renderer set for certain column is valid for all rows, if you set 7th column's renderer to, say, ColorRed, if will be used to paint 7th column's cell in all rows in the table.

I think you should not be setting renderers like this. This is what I'd do:

1) Create a table model that will contain information of colors in individual cells, say ColorTableModel (actually try to think of a better name that this ). You can extend your current table model to add this information, though if you're using DefaultTableModel now, it will be a little bit messy. A much better way would be to:
  • Create a class representing one row of your table, say ColorTableRow (again, a better name would be, uh, better). Use descriptive names of its attributes and declare them using the correct data type (as opposed to DefaultTableModel, where all cells are ultimately stored as instances of the Object class). Include the color information in the class (eg. Color minimumColor, Color maximumColor etc.)
  • Create a table model by extending AbstractTableModel. Use a list of ColorTableRows to store the data in the model. It is really not that hard, you need to override just a few methods of AbstractTableModel to get a functioning table model.

  • 2) Create a renderer.
  • The renderer will need access to the model you've created. You can either pass the instance of the model to the renderer's constructor, or get the table's model in the getTableCellRendererComponent method and cast it to ColorTableModel.
  • Implement the getTableCellRendererComponent method: set the cell's text and color. You can set the cell's value to the value parameter, but you need to obtain the color of the current cell from the ColorTableModel instance.

  • 3) Manage coloring of the table.
  • When user presses the Analyze button, go through all ColorTableRow instances and assign colors to cells. Don't forget to "unset" color of the cells that no longer meet the coloring criteria!
  • Let the table know which cells or rows need repainting. All cells that have changed color must be repainted. You can either use fireTableCellUpdated on individual cells you change, or fireTableRowsUpdated for all changed rows, or fireTableDataChanged - given today's computers speed, repainting the whole table instead of just a few cells is not going to be noticeable.

  • If you do it this way, I believe you'll have no problems with cell colors changing unexpectedly.
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin Vajsar,
    My table model extends AbstractTableModel!(The name is ...ClientTableModel!How creative...) In my table, only columns 7,8 and 9 are editable.Example:

    In 1) i dont understand that:
    Create a class representing one row of your table, say ColorTableRow (again, a better name would be, uh, better). Use descriptive names of its attributes and declare them using the correct data type (as opposed to DefaultTableModel,where all cells are ultimately stored as instances of the Object class). Include the color information in the class (eg. Color minimumColor, Color maximumColor etc.)
    When you say to create a ColorTableRow i must create a default with UI defauts of the table?Like table.getBackground and getForeground to confront my RED and BLUE colors?
    Where i will change in my table model and STORE the colors? Can you give me a small example?

    Best Regards,


    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Paul, I seem unable to explain myself properly. I've put together a quick sample of how would I implement table with colored cells that change on a button press. It just illustrates the principle. This is why there is just one column and column indexes are ignored, but other than this, this is how I would actually implement it in a project.

    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin Vajsar,
    many thanks for your code!I am travelling for work and didn t see your post!
    I will probably change the constructor to receive my Clients and a Color..at first look, your code looks smooth!
    Tomorrow i will post some results.

    one more time:thanks!

    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin,
    i am adapting your code to do what i need(evaluate user input after press jbutton and color the cell).
    I made a minor modification(of course, i changed the table cell to be editable):

    But the color dont change when user inputs 40 in the cell!

    BestWishes,
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    You'll need to be much more careful, Paul!

    The code you've shown here doesn't compile. However, if you fix this bug, the renderer works exactly as expected - cells with the value of 40 are always displayed in blue. I therefore assume that the problem is somewhere else. Please post your full code, and make sure it compiles cleanly.

    I also think that you still haven't fully grokked the JTable concepts. At line 9, you're calling model.getValue(row, column), which contains value of the cell to be drawn, but inside this method, this value is available in the value parameter. It's not a mistake, not a big one, but it shows that you still don't "instinctively" know how the table, model and renderer cooperate to draw a cell. Perhaps you could try to go through the tutorial once more?
    Darryl Burke
    Bartender

    Joined: May 03, 2008
    Posts: 4529
        
        5

    Martin Vajsar wrote:At line 9, you're calling model.getValue(row, column), which contains value of the cell to be drawn, but inside this method, this value is available in the value parameter. It's not a mistake,


    Actually, at line 9 he's calling table.getValueAt(...) which is as you say redundant but not wrong. Obtaining any value from the model using the row, column parameters to the renderer method would return a wrong value if the table is sorted or its columns are reordered, as those parameters are the view coordinates. And that mistake is seen in line 14.


    luck, db
    There are no new questions, but there may be new answers.
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Darryl Burke wrote:Obtaining any value from the model using the row, column parameters to the renderer method would return a wrong value if the table is sorted or its columns are reordered, as those parameters are the view coordinates. And that mistake is seen in line 14.

    You're right.

    Unfortunately, that mistake comes from my own code. My bad.
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45

    I also think that you still haven't fully grokked the JTable concepts. At line 9, you're calling model.getValue(row, column), which contains value of the cell to be drawn, but inside this method, this value is available in the value parameter. It's not a mistake, not a big one, but it shows that you still don't "instinctively" know how the table,
    model and renderer cooperate to draw a cell. Perhaps you could try to go through the tutorial once more?

    Martin, Do you mean that:

    Compile fine, but still dont work...I am using your own code for tests(the only difference i changed MyColorRenderer and put isCellEditable true in the model) What that line should be:
    Color color = model.getColorAt(row, column);

    BestWishes,
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Paul, ItDoesntWorkIsUseless. Please be more specific when describing the problems.

    I can only guess that the thing that doesn't work is the editing. You enter a new value (40) and nothing happens - neither the value nor color in the cell changes.

    Have a closer look at the code and try to work out where does the value to be displayed in a cell come from. Which field keeps that value? Where (on which line), and most importantly, how is it declared? Can it ever change?

    When you find this out, we can proceed to make necessary changes to make the table editable.

    Edit: the compile problem I've mentioned is on this linevalue is an Object, it cannot be cast directly to int. You need to cast it to Integer. At least in my version of Java (JDK6), perhaps some other version behaves differently? What version of JDK are you using?
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin,
    my bad, i should tell you: My JDK is 7 that promotion(int to int) is legal in newer versions.
    Anpout my problems: i made some modifications, but the color changes to blue when i edit!I wanna change only when i press JButton!Full code(without imports):

    Best Wishes,

    Michael Dunn
    Ranch Hand

    Joined: Jun 09, 2003
    Posts: 4632
    apologies for butting in here, but this might be one way of doing what you want (from your description)

    run it, change (0,1) to 40 [enter], click the toggleButton for selection --> row should change to blue foreground,
    then click again to unselect it --> row reverts to normal color.

    (I've stripped everything unrelated to the specific problem)
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Yes, Michael, that would be a possibility.

    I was trying to nudge Paul into a different approach:

    1) keep the original version of the renderer (from my Feb 28th post),

    2) when the Analyze button is pressed, change the color information in the model to reflect the new colors. That is, the Analyze button would loop through all rows in the model and if it found a value equal to 40, it would set the color to blue, otherwise to black.

    Paul, do you understand my intentions now?

    (By the way, sorry for snubbing you about casting Object to int. That was my fault, I should have found out this is possible in Java 7.)
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Michael Dunn,
    your approach works only in the FIRST time.Once you pressed jbutton, after i change the cell value(putting others to 40), the color change to blue without pressing JButton.

    Martin Vajsar,
    care to explain how i change the model to do that?(a snippet of code would be great)

    Best Wishes,

    Michael Dunn
    Ranch Hand

    Joined: Jun 09, 2003
    Posts: 4632
    > Michael Dunn,
    > your approach works only in the FIRST time.Once you pressed jbutton, after i change the cell value(putting others to 40), the color change to blue without pressing JButton.

    rubbish.

    it's a JToggleButton, not a JButton and it's your code:
    private class ColorButton extends JToggleButton

    if you don't know the difference, read the docs to find out why a JToggleButton remains 'pressed'
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Pal, since you didn't fully specify the requirements, I'm going to provide some so that we don't speak about some abstract coloring function. So, the specification I'm targeting is:

    Create an editable JTable. The editable column contains integers. Numbers are initially displayed in black. When user presses the Analyze button, the numbers in the table are inspected; cells containing numbers equal to the minimum of all values are rendered blue and cells containing numbers equal to the maximum of all values are rendered red. When user edits the numbers, the coloring doesn't change (existing coloring is preserved), only after pressing the Analyze button are the colors recomputed.

    It this similar to what you want to achieve?

    This is what I'd do:

    1) Start with the code I've provided, only make changes needed for the table to be editable (you already have done this). Don't change the renderer!
    2) Remove the toggle buttons (ColorButton class) and add the Analyze JButton.
    3) Recompute colors in the action triggered by the Analyze button. The code itself might perhaps reside in the MyColorModel class. You'd loop over all rows to find minimum and maximum and then, in a second loop, go through the rows again and set the desired color of the cell using MyColorModel.setColorAt.

    As you can see, it really isn't that complicated. I don't want to provide more code, it is important that you understand the concept, so that you can maintain the final code yourself. You'll learn much more by coding yourself than by reading someone else's code, which can moreover be buggy (as my code was - see Darryl's post. We can return to this bug later, but need to get the basics working first).
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Michael, i get it now.thanks for your help.

    It this similar to what you want to achieve?

    Thats Exactly what i want!


    Martin, can you elaborate your step 3?I dont understand very well the need for second loop and the why the change color in MyColorModel class.


    Best Wishes,
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Paul Hayabusa wrote:Martin, can you elaborate your step 3?I dont understand very well the need for second loop and the why the change color in MyColorModel class.

    The code can be anywhere. I proposed putting it into the MyColorModel, because the model "owns" all the data that need to be manipulated in this process. But it can be anywhere.

    The coloring algorithm itself doesn't have anything to do with Swing. There are several possible approaches, but I proposed two loops as probably the simplest and easiest one. Generally, to find a minimum or maximum in an unordered collection, you need to inspect all items of the collection, as the minimal and maximal items can be found at any position, possibly even on the last one. Try to flesh out this algorithm yourself, it really isn't too complicated.
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin,
    to be truthful, is simpler than you described.The maximun and minimun values in comparison comes from Data Base(they arent comparing a JTable cell with each other, but with the value in DB).I can not understand why that aproach dont works fine:

    doing the Job in the renderer:

    40 means the value that comes from DB.
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    What does the "not working fine" means?

    The renderer you've posted doesn't reflect whether a button was pressed or not. It renders all values of 40 in blue, regardless of anything else.

    The ColorButton code sets the color in the table model when that button is pressed. Is there anything unclear in this code?

    We both need to be working on identical specs. The specification I've proposed doesn't speak about loading values from the database, but that can be easily changed - and abstracted away, so that we can still have small, functioning code we can discuss. I cannot meaningfully assist you when you start trying to solve different specification without letting me know what that specification is.
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    We both need to be working on identical specs. The specification I've proposed doesn't speak about loading values from the database, but that can be easily changed -
    and abstracted away, so that we can still have small, functioning code we can discuss. I cannot meaningfully assist you when you start trying to solve different specification without letting me know what that specification is.

    Martin,

    When i mean does not work fine, i mean just color, but not like i want(ONLY when jbutton is pressed).
    About specification: Sorry, i saw now i made a mess(I was trying to simplify/shorten just putting the 40 is BLUE thing).
    There is two values: minValue and maxValue.The rest is like you wrote.If the cell BETWEEN minValue and maxValue, BLUE, if not, RED.
    Lets say min and max are 10 and 50.If the cell is 40, color will be blue, if its 60, RED.
    I change some things, but i always fall in the same step...
    Like i said before, coloring is not the problem, my problem is color when it needs to be colored.


    Best wishes,
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Paul,

    in the sample code I've posted there are classes MyColorModel, MyColorRow and MyColorRenderer. The model and (unmodified) renderer can be used together to construct a JTable instance which completely draws itself based on information stored in the model. So to change the value or color of a cell, all you need to do is to update data in the model. The model will take care of signalling to the table which cells need to be repainted.

    If you want to change the coloring after a button is pressed, all you need to do is to update the colors in the model when the button is pressed (in the action listener of that button). If this means reading values from a database and comparing individual cells to these values, so be it. If you want to change or reset colors after any other action (eg., after the user edits a cell), again, just set the colors using the model's method at the appropriate places of your program.

    Let's see the example you've mentioned:
    Paul Hayabusa wrote:
    There is two values: minValue and maxValue.The rest is like you wrote.If the cell BETWEEN minValue and maxValue, BLUE, if not, RED.
    Lets say min and max are 10 and 50.If the cell is 40, color will be blue, if its 60, RED.

    So, when the button gets pressed, loop through all rows/values in the model. Obtain the cell value. If it lies between the min and max, use the model's setColorAt method to set that cell's color to blue, otherwise use the same method to set that color to red.
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin,
    i changed something:
    JButton:

    and the same renderer as yours:

    When i press JButton(after writing 40 in one cell) ALL rows got blue colored.
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    Paul Hayabusa wrote:When i press JButton(after writing 40 in one cell) ALL rows got blue colored.

    It is probably because of this line:

    You're assigning into obj the value corresponding to the selected row in every iteration of the loop. If you enter 40 into the cell manually and don't move the selection, obj will always be 40. If you select a different cell and press the button, all rows will be black.

    You should have there i instead of table.getSelectedRow(). After this modification it seems to work fine in my environment.
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Martin,
    i must be doing something wrong...that code:

    And with the renderer is aplied in two columns:


    is painting the line(two cells) with BLUE, not only the cell changed for 40!My Renderer is the same of yours...and whats the problem with Color in your renderer called by Darryl Burke?

    Best wishes,
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    I immediately don't see anything wrong with that. Can you post your full code? (Hopefully it's still short.... )
    Paul Hayabusa
    Ranch Hand

    Joined: Jan 17, 2013
    Posts: 45
    Remember: I am using JDK 7!
    The code is almost the same as yours:

    Its working almost fine, except it colors more than the cell it should be coloring!
    Martin Vajsar
    Sheriff

    Joined: Aug 22, 2010
    Posts: 3610
        
      60

    The problem is that my original sample didn't support multiple columns. When you've added support for multiple columns, you've added another field for the value, but forgot to add another field for color. So you need to modify MyColorRow and MyColorModel to support different colors of different columns in a row.
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: How to color JTable cell without a renderer?