I am stuck with implementing ListCellRenderer. This is my code.
I am getting the data from a DB in the form of Domain class that looks like this:
public class Domain {
private Integer id;
private String naziv;
private Integer status;
public Domain(){}
public Integer getId() {return id;}
public void setId(int i){id = i;}
public String getNaziv(){return naziv;}
public void setNaziv(String n){naziv = n;}
public Integer getStatus(){return status;}
public void setStatus(int s){status = s;}
}
Set up of JList:
DefaultListSelectionModel m = new DefaultListSelectionModel();
m.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
m.setLeadAnchorNotificationEnabled(false);
DefaultListModel<String> modelRN = new DefaultListModel<String>();
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 86, 390, 199);
contentPane.add(scrollPane);
JList<String> listRN = new JList<String>(modelRN);
scrollPane.setViewportView(listRN);
listRN.setBorder(new TitledBorder(null, "", TitledBorder.LEADING, TitledBorder.TOP, null, null));
listRN.setSelectionModel(m);
and this is how I populate the list:
dRN = new DBdomain(s,a,b).Conn();
for(int i=0;i<dRN.size();i++){
modelRN.addElement(dRN.get(i).getNaziv());
where dRN = ArrayList<Domain>
So the problem is this. I am populating the list with strings that are Domain.getNaziv() but i want to change the background in the list where Domain.getStatus() has certain value. I know I need to implement something like this:
public class MyListCellRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
// do something
return c;
}
}
The problem is that I am not populating Jlist with Domain but with a Domain filed which is a string, so the value argument in getListCellRendererComponent doesnt see the filed status so I don't know how I would mark the fields whose background I want to change.
hope I provided all the info and that somebody can point me in the right direction.
The problem is that I am not populating Jlist with Domain
Well DO populate JList with Domain. Perhaps something like this:
public class MyListCellRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component cell = null;
if (value instanceof Domain) {
Domain domain = (Domain)value;
int status = domain.getStatus();
String naziv = domain.getNaziv();
cell = super.getListCellRendererComponent(list,
naziv, // note this...
index,
isSelected,
cellHasFocus);
if (status > 0) { // or whatever...
cell.setBackground(STATUS_ON_COLOR);
} else {
cell.setBackground(STATUS_OFF_COLOR);
}
}
}
return cell;
}
Related
To put this short:
What is this about
I have a JTable with Model which displays data fetched from an SAP system.
My Goal is in a specific column to display only a part of the data which is in the model. For example the row of the model has Object["a","b"] but the user is only supposed to see a.
So I read a lot of threads here on StackOverflow and a lot of tutorials on how to use custom tablecellrenderers and editors etc. but I am not able to fix my problem, which is that the cell where i registered the renderer will not be highlighted when selected.
A possible solution is described HERE but this does not work for me.
Here is my custom Renderer:
public class SapChangeNumberCellRenderer extends DefaultTableCellRenderer{
private static final long serialVersionUID = -2649719064483586819L;
private SapChangeNumberTable table;
private int valuesSize;
public final static String ASM_AMOUNT = LanguageServer.getString("71", "Baugruppen");
public SapChangeNumberCellRenderer(SapChangeNumberTable table) {
super();
this.table = table;
}
#Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected,
final boolean hasFocus,
final int row, final int column) {
// components & Layout
JPanel panel = new JPanel(new BorderLayout());
JButton buttonDots = new JButton("...");
JLabel text = new JLabel();
List<String> values = (List<String>) value;
valuesSize = values.size();
if (valuesSize > 0) {
// set values
buttonDots.setPreferredSize(new Dimension(14, 14));
text.setText(values.get(0));
} else {
text.setText("");
}
if (valuesSize > 1) {
// button to open dialog only if we have more than 1 item
panel.add(buttonDots, BorderLayout.EAST);
}
panel.add(text, BorderLayout.WEST);
panel.setOpaque(true);
return panel;
}
#Override
public String getToolTipText(MouseEvent e) {
String toolTip = String.valueOf(valuesSize) + Initializer.SPACE + ASM_AMOUNT;
return toolTip;
}
public SapChangeNumberTable getTable() {
return table;
}
}
So as you can see depending on the list size of the values I manipulate the component which will be given back from the method. The setOpaque(true) method does somehow not achieve my goal.
Here is the according JTabel (note: BaseTable is just a wrapper for JTable with some goodies I need...nothing fancy here)
public class SapChangeNumberTable extends BaseTable {
/** the model */
private SapChangeNumberTableModel model = new SapChangeNumberTableModel();
/** parent frame */
private SapPanel parent = null;
public SapChangeNumberTable(SapPanel parent) {
this.parent = parent;
this.init();
}
/**
* init the table
*/
private void init() {
// set model (not filled yet)
this.setModel(model);
// set renderer
setRendererAndEditor();
// add search filter row, enabling sorting is included
this.addFilterSearchRowPanel();
// single selection
this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// hide
this.hideColumns();
this.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
}
/**
* sets the default table cell renderer
*/
private void setRendererAndEditor() {
getColumnModel().getColumn(convertColumnIndexToView(SapChangeNumberTableModel.COL_ID_SPM_ASM_NUMBER))
.setCellRenderer(new SapChangeNumberCellRenderer(this));
getColumnModel().getColumn(convertColumnIndexToView(SapChangeNumberTableModel.COL_ID_SPM_ASM_NUMBER))
.setCellEditor(new SapChangeNumberAsmRefTableCellEditor(this));
}
#Override
public void setStatusBarDataCount(boolean value) {
}
#Override
public void hideColumns() {
}
#Override
public int getColModelSortIndex() {
return 0;
}
#Override
public void load() {
}
#Override
public SapChangeNumberTableModel getModel() {
return model;
}
public boolean isChanging() {
return model.isFilling();
}
public SapFactoryChange getRow(int row) {
return model.getSapFactoryChange(row);
}
#Override
public void clear() {
model.clear();
}
#Override
public Component prepareRenderer(TableCellRenderer renderer, int rowIndex, int vColIndex) {
Component comp = super.prepareRenderer(renderer, rowIndex, vColIndex);
if (vColIndex == SapChangeNumberTableModel.COL_ID_SPM_ASM_NUMBER) {
//what the hack to do here to manipulate the comp ? I can't add a JPanel to a plain Component
}
return comp;
}
}
In the table I tried some stuff with prepareRenderer but here I can't manipulate the data (values) and all other stuff I am doing in the custom renderer. Maybe I have a basic understanding problem of how to approach this. I am thankful for any hints !
I just found a very simple solution which I thought would overwrite my wanted behavior, but it doesn't.
Just implemented this into the Table:
#Override
public Component prepareRenderer(TableCellRenderer renderer, int rowIndex, int vColIndex) {
Component comp = super.prepareRenderer(renderer, rowIndex, vColIndex);
if (isRowSelected(rowIndex)) {
comp.setBackground(Color.ORANGE);
}
return comp;
}
works like a charme!
I have taken a jtable, and added a model to it, and added text area rendere and editor to it.
Table.setModel(
JUTableBindingFactory.createAttributeListBinding(
Binding, Table name, View name, null, null, attributes));
Table.getColumnModel()
.getColumn(i)
.setCellEditor(
new TextAreaEditor(
true, true, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
Table.getColumnModel()
.getColumn(i)
.setCellRenderer(
new TextAreaCellRenderer(
true, true, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
_Table.setRowHeight(100);
Also I have set the table row hieght.
Text area renderer class
public class TextAreaCellRenderer extends JTextArea implements TableCellRenderer
{
private JScrollPane pane;
private boolean _isEditable = false;
private boolean _isEnabled = false;
private int _verticalBarProperyValue = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED;
private int _horizontalBarProperyValue = JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED;
public TextAreaCellRenderer()
{
super();
init();
}
public TextAreaCellRenderer(boolean isEnabled, boolean isEditable, int
verticalBarProperyValue, int horizontalBarProperyValue)
{
super();
_isEditable = isEditable;
_isEnabled = isEnabled;
_verticalBarProperyValue = verticalBarProperyValue;
_horizontalBarProperyValue = horizontalBarProperyValue;
init();
}
private void init()
{
pane = new JScrollPane(this, _verticalBarProperyValue, _horizontalBarProperyValue);
setLineWrap(true);
setWrapStyleWord(true);
setEditable(_isEditable);
setEnabled(_isEnabled);
}
/**
* Returns the cell renderer component.
*/
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
setLineWrap(true);
setWrapStyleWord(true);
setText(value != null ? value.toString() : "");
setBackground((isSelected) ? NWSTheme.BLUE_DARK : NWSTheme.WHITE);
setForeground((isSelected) ? NWSTheme.WHITE : NWSTheme.BLACK);
pane.setEnabled(true);
return pane;
}
}
the editor class is also quite similar, but the problem doesn't seem to be relating to this, what else should i try?
Try moving the horizontal scroll more up as sometimes we don't and are given the impression of what you experienced. Try atleast
Does someone know a good way to display the sorting icons in the header of a JTable, without using the build in sort functionality?
The sorting is done by the table model (actually a database) and not by the JTable itself. Thats why the automatic display of the icons doesn't work. Maybe one can insert a dummy RowSorter that does nothing, but makes the sort icons appear?
I found a better Solution
I just wrote my own RowSorter, so that the sorting does not have any effect, but redirects the sorting request to the model instead. That way the sort order is displayed by the look and feel itself. Some Pseudocode:
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.RowSorter;
import xyz.SortableTableModel;
public class MyRowSorter<M extends SortableTableModel> extends RowSorter<M> {
private M tableModel;
private List<? extends SortKey> sortKeys = new LinkedList<>();
public MyRowSorter(M tableModel) {
this.tableModel = tableModel;
}
#Override
public M getModel() {
return tableModel;
}
#Override
public void toggleSortOrder(int column) {
// redirecting sort request to model and modification of sortKeys
List<? extends SortKey> newSortKeys = ...;
setSortKeys(newSortKeys);
}
#Override
public int convertRowIndexToModel(int index) {
return index; // will always be the same
}
#Override
public int convertRowIndexToView(int index) {
return index; // will always be the same
}
#Override
public void setSortKeys(List<? extends SortKey> keys) {
if (keys == null) {
sortKeys = Collections.EMPTY_LIST;
} else {
sortKeys = Collections.unmodifiableList(keys);
}
fireSortOrderChanged();
}
#Override
public List<? extends SortKey> getSortKeys() {
return sortKeys;
}
#Override
public int getViewRowCount() {
return tableModel.getRowCount();
}
#Override
public int getModelRowCount() {
return tableModel.getRowCount();
}
// no need for any implementation
#Override public void modelStructureChanged() { }
#Override public void allRowsChanged() { }
#Override public void rowsInserted(int firstRow, int endRow) { }
#Override public void rowsDeleted(int firstRow, int endRow) { }
#Override public void rowsUpdated(int firstRow, int endRow) { }
#Override public void rowsUpdated(int firstRow, int endRow, int column) { }
}
In that case you can try to write a custom TableCellRenderer for JTableHeader.
Here is simple example of renderer:
private static class MyRenderer implements TableCellRenderer {
private ImageIcon icon1;
private ImageIcon icon2;
private TableCellRenderer defaultRenderer;
MyRenderer(JTable t){
defaultRenderer = t.getTableHeader().getDefaultRenderer();
icon1 = new ImageIcon(getClass().getResource("1.png"));
icon2 = new ImageIcon(getClass().getResource("2.png"));
}
#Override
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
Component c = defaultRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, col);
if(col%2 == 0){
((JLabel)c).setIcon(icon1);
} else {
((JLabel)c).setIcon(icon2);
}
return c;
}
}
Here icon1 and icon2 is your sorting icons.
And you can set that renderer for your JTableHeader like next:
table.getTableHeader().setDefaultRenderer(new MyRenderer(table));
table - is your JTable.
The sorting is done by the table model (actually a database) and not by the JTable itself.
Check out the DefaultRowSorter class. Maybe you use the setSortsOnUpdates(...) and setSortKeys(...) so the sorting icons match the sort from the database. You could try:
Creating an empty model
Set the sort keys
use setSortsOnUpdates(false);
Update the model using the setDataVector() (or some equivalent method if using a custom model)
Note this approach assumes you have created the TableModel with column names and no data and added the model to the JTable. I think you will then also need to use:
table.setAutoCreateColumnsFromModel(false);
to prevent the TableColumnModel from being recreated when you load the data into the model.
Solution is tricky when you want your code to work with other existing Swing layouts (I am talking about com.formdev .... flatlaf ). These L&Fs create a special Header renderer.
Here is a simple solution that will work with all main L&Fs on the market (tatoo, formdev, jgoodies). The trick is to subclass from DefaultTableCellHeaderRenderer but also to pass the table look and feel current header renderer as parameter.
// this custom renderer will display the sorting icon for all afftected columns.
class CustomTableHeaderRenderer extends DefaultTableCellHeaderRenderer implements TableCellRenderer{
final private Icon ascIcon = UIManager.getIcon("Table.ascendingSortIcon");
final private Icon descIcon = UIManager.getIcon("Table.descendingSortIcon");
TableCellRenderer iTableCellRenderer = null;
public CustomTableHeaderRenderer(TableCellRenderer tableCellRenderer)
{
iTableCellRenderer = tableCellRenderer;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) iTableCellRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column) ;
List<? extends SortKey> sortKeys = table.getRowSorter().getSortKeys();
label.setIcon(null);
for (SortKey sortKey : sortKeys) {
if (sortKey.getColumn() == table.convertColumnIndexToModel(column)){
SortOrder o = sortKey.getSortOrder();
label.setIcon(o == SortOrder.ASCENDING ? ascIcon : descIcon);
break;
}
}
return label;
}
}
yourTable.getTableHeader().setDefaultRenderer( new CustomTableHeaderRenderer( yourTable.getTableHeader().getDefaultRenderer() ));
I have a JButton inside a JTable in the last column, and when client click on that, It will show a JFrame. But I don't know how I can get the row so I can get the object in the row and send it to JFrame constructor?
it is my table:
table = new JTable(model);
JTableHeader tableHeader = table.getTableHeader();
tableHeaderRenderer = table.getTableHeader().getDefaultRenderer();
tableHeader.setDefaultRenderer(new TableCellRenderer() {
private JLabel label;
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (selectedColumn == value) {
label = (JLabel) tableHeaderRenderer.getTableCellRendererComponent(table,
value, true, true, row, column);
label.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
label.setBorder(BorderFactory.createCompoundBorder(label.getBorder(),
BorderFactory.createEmptyBorder(0, 5, 0, 0)));
label.setHorizontalAlignment(SwingConstants.LEFT);
} else {
label = (JLabel) tableHeaderRenderer.getTableCellRendererComponent(table,
value, false, false, row, column);
label.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
label.setBorder(BorderFactory.createCompoundBorder(label.getBorder(),
BorderFactory.createEmptyBorder(0, 5, 0, 0)));
label.setHorizontalAlignment(SwingConstants.CENTER);
}
return label;
}
});
table.getColumnModel().getColumn(3).setCellRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBackground(Color.white);
JButton button = Shorter.button("Details", true);
buttonPanel.add(button);
return buttonPanel;
}
});
table.getColumnModel().getColumn(3).setCellEditor(new TableCellEditor() {
public boolean stopCellEditing() {return false;}
public boolean shouldSelectCell(EventObject arg0) {return false;}
public void removeCellEditorListener(CellEditorListener arg0) {}
public boolean isCellEditable(EventObject arg0) {return true;}
public Object getCellEditorValue() {return null;}
public void cancelCellEditing() {}
public void addCellEditorListener(CellEditorListener arg0) {}
public Component getTableCellEditorComponent(JTable arg0, Object arg1,
boolean arg2, int arg3, int arg4) {
JFrame frame = new JFrame();
frame.setVisible(true);
return null;}
});
when frame loaded I want to know which row selected to add to frame constructor...
But I don't know how I can get the row so I can get the object in the
row and send it to JFrame constructor?
there are three choices
event from JButton inside a JTable in the last column must to returs proper coordinates from JTables view, you have to convert view to model in the case that JTables view is
sorted
filtered
column(s) is/are reordered
column(s) is/are hidden (removed from JTables view)
(and/or with) add ListSelectionListener to JTable
Mouse Events can returns that too, read official Oracle tutorial How to use Tables - Specifying Tool Tips for Cells for working code example
I wrote a simple ButtonColumn some time ago, I think it can help you a little, but as #mKorbel mentioned, you must to solve problems with sorting,filtering etc.
.
public class ButtonColumn extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor{
private JButton button;
private String value;
public ButtonColumn(){
button = new JButton();
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("pressed"+value);
}
});
}
#Override
public Component getTableCellRendererComponent(JTable arg0, Object arg1,
boolean arg2, boolean arg3, int arg4, int arg5) {
button.setText(arg4+"");
value = " "+arg4;
return button;
}
#Override
public Object getCellEditorValue() {
return null;
}
#Override
public Component getTableCellEditorComponent(JTable arg0, Object arg1,
boolean arg2, int arg3, int arg4) {
button.setText(arg3+"");
value = " "+arg3;
return button;
}
}
Set it to your column in next way:
table.getColumnModel().getColumn(YOUR_COLUMN_NUMBER).setCellEditor(new ButtonColumn());
table.getColumnModel().getColumn(YOUR_COLUMN_NUMBER).setCellRenderer(new ButtonColumn());
See Table Button Column for a solution that provides you with the row. All you need to do is provide it with an Action.
I've got a JTree with icons on some of the nodes within the tree. They appear and work fine but when I select a node with a icon, the renderer does not render the entire node selected but appears to have an offset applied to it, as if it thinks the icon is still to the left of the node as below:
The code for the renderer (which extends DefaultTreeCellRenderer) is below:
public ProfileTreeRenderer() {
super.setLeafIcon(null);
super.setClosedIcon(null);
super.setOpenIcon(null);
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (c instanceof JLabel) {
JLabel label = (JLabel) c;
label.setHorizontalTextPosition(SwingConstants.LEADING);
}
if(sel && !hasFocus) {
setBackgroundSelectionColor(UIManager.getColor("Panel.background"));
setTextSelectionColor(UIManager.getColor("Panel.foreground"));
} else {
setTextSelectionColor(UIManager.getColor("Tree.selectionForeground"));
setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
}
if (value instanceof ProfileNode) {
ProfileNode node = (ProfileNode) value;
if (node.isUsed() && !sel) {
c.setForeground(Color.GRAY);
}
if (node.getIcon() != null) {
setIcon(node.getIcon());
}
}
}
I cannot see why the renderer would apply this offset, so can anyone offer a way to get the node fully selected with the icon? The SSCCE code for the tree itself is below.
public class Example extends JDialog {
public Example() {
JTree tree = new JTree(createModel());
tree.setCellRenderer(new ProfileTreeRenderer());
setLayout(new BorderLayout());
add(tree, BorderLayout.CENTER);
}
private TreeModel createModel() {
ProfileNode root = new ProfileNode("Profiles");
ProfileNode userA = new ProfileNode("Example User A");
ProfileNode userB = new ProfileNode("Example User B");
// You'll need to subsitute your own 16x16 icons here
userA.setIcon(ImageSet.USER_ICON);
userB.setIcon(ImageSet.USER_ICON);
root.add(userA);
root.add(userB);
return new DefaultTreeModel(root);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Example().setVisible(true);
}
});
}
}
The ProfileNode class:
public class ProfileNode extends DefaultMutableTreeNode {
#Getter private String labelDisplay;
#Getter #Setter private ImageIcon icon;
#Getter #Setter private boolean isUsed = false;
public ProfileNode(String labelDisplay) {
this.labelDisplay = labelDisplay;
}
#Override
public String toString() {
return labelDisplay;
}
}
Thanks in advance.
The problem is that the DefaultTreeCellRenderer uses its icon property exclusively for the open/leaf/close icons: it assumes that - if the icon != null - it's at the start of the component (even if it isn't) and adjusts the selection accordingly. You need to re-adjust ... or use SwingX renderers :-)
Something like:
JXTree tree = new JXTree();
tree.expandAll();
IconValue iv = new IconValue() {
Icon icon = XTestUtils.loadDefaultIcon("green-orb.png");
#Override
public Icon getIcon(Object value) {
return value.toString().contains("s") ? icon : null;
}
};
StringValue converter = new MappedValue(StringValues.TO_STRING, iv);
WrappingProvider provider = new WrappingProvider(IconValues.NONE, converter);
// hacking around missing api ...
LabelProvider wrappee = (LabelProvider) provider.getWrappee();
wrappee.getRendererComponent(null).setHorizontalTextPosition(JLabel.LEADING);
TreeCellRenderer r = new DefaultTreeRenderer(provider);
tree.setCellRenderer(r);