I am currently working on a JComboBox component where I want to have a JTable inside of a ComboBox for a drop down selection. I have extended the ListCellRenderer and I have the table inside of the popup.
I want to present it two different ways. The first as a label of a bound column of the selected row when the popup is not visible. The second is to show the table with a JScrollPane when the popup is visible.
Unfortunately, When I do this the popup is being shrunk to the row height of the list which only leaves room for the columns of the table.
If I just use the scrollpane I can see the full table but then when the popup is not visible I see the table inside the combobox which is not what I want.
How can I set the height such that the table can fit while still being able to show the label only when the popup is not visible?
Below is a minimal example that compiles and runs:
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
public class TableComboBoxMain {
public static void main(String[] args) {
JTableComboBox<Employee> combo = new JTableComboBox<>();
combo.addItem(new Employee("April",3));
//combo.setMaximumRowCount(10);
combo.setRenderer(new TableListCellRenderer(combo));
JFrame frame = new JFrame("Test Table Combo Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(combo);
frame.pack();
frame.setVisible(true);
}
public static class Employee{
String name;
int nr;
public Employee(String name, int number ){
this.name =name;
this.nr = number;
}
}
public static class JTableComboBox<E> extends JComboBox<E> implements ListSelectionListener{
#Override
public void valueChanged(ListSelectionEvent e) {System.out.println("Row selected"+e.getFirstIndex());
this.setSelectedIndex(e.getFirstIndex());
}
}
public static class TableListCellRenderer extends JScrollPane implements ListCellRenderer{
JTable table;
JTableComboBox combo;
boolean mouseListenerAdded;
public TableListCellRenderer(JTableComboBox combo){
this.combo=combo;
DefaultTableModel model = new DefaultTableModel();
String[] cols1 = new String[]{"1","2","3","4"};
String[] cols2 = new String[]{"Mark","John","April","Mary"};
model.addColumn("Nr", cols1);model.addColumn("Name",cols2);
table = new JTable(model);table.setShowGrid(true);table.setGridColor(Color.gray);
this.getViewport().add(table);
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if(!mouseListenerAdded){
list.addMouseListener(this.getListener());
mouseListenerAdded = true;
}//If this is uncommented then the BasicComboPopup size is always no more than 1 row?!!
if(!combo.isPopupVisible()){
Employee emp = (Employee) value;
return new JLabel(emp.name);
}else
return this;
}
public MouseAdapter getListener(){
MouseAdapter adapter = new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
if(combo.isPopupVisible()){
System.out.println("MouseClicked over list");
int row = table.rowAtPoint(e.getPoint());
if(row>0){
table.setRowSelectionInterval(row-1, row-1);
ListDataEvent event = new ListDataEvent(combo,ListDataEvent.CONTENTS_CHANGED,row-1,row-1);
combo.contentsChanged(event);
}
}
}
};
return adapter;
}
}
}
So I found a solution to the problem. I am not finished yet because there are a few interactions I still want to have:
I want to be able to move columns
I want to be able have popup menu for columns
I want to be able to scroll vertically with the mouse
I wanted to post the solution in case anyone else wants a starting point example. I will update my answer as I solve these additional issues.
Below is the test class:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.table.DefaultTableModel;
public class TableComboBoxMain {
public static void main(String[] args) {
JTableComboBox<String> combo = new JTableComboBox<>();
combo.addItem("");
BasicComboBoxUI ui = new BasicComboBoxUI(){
#Override
protected ComboPopup createPopup() {
return new BasicComboPopup( comboBox ){
#Override
protected int getPopupHeightForRowCount(int maxRowCount) {
return 100;
}
#Override
protected JScrollPane createScroller() {
JScrollPane sp = new JScrollPane( list,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED );
sp.getHorizontalScrollBar().setFocusable( false );
return sp;
}
};
}
};
combo.setUI(ui);
combo.setRenderer(new TableListCellRenderer(combo));
JFrame frame = new JFrame("Test Table Combo Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(combo);
frame.pack();
frame.setVisible(true);
}
public static class Employee{
String name;
int nr;
public Employee(String name, int number ){
this.name =name;
this.nr = number;
}
}
public static class JTableComboBox<E> extends JComboBox<E> implements ListSelectionListener{
#Override
public void valueChanged(ListSelectionEvent e) {
System.out.println("Row selected"+e.getFirstIndex());
this.setSelectedIndex(e.getFirstIndex());
}
}
public static class TableListCellRenderer extends DefaultListCellRenderer{
JTable table;
JTableComboBox combo;
JPanel renderer = new JPanel();
JPanel colHeader = new JPanel();
JScrollPane scroll = new JScrollPane();
boolean mouseListenerAdded;
public TableListCellRenderer(JTableComboBox combo){
this.combo=combo;
DefaultTableModel model = new DefaultTableModel();
String[] cols1 = new String[]{"1","2","3","4","5","6"};
String[] cols2 = new String[]{"Mark","John","April","Mary","Joe","Jack"};
model.addColumn("Nr", cols1);model.addColumn("Name",cols2);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
colHeader.add(table.getTableHeader());
renderer.setLayout(new BorderLayout());
renderer.add(colHeader, BorderLayout.NORTH);
renderer.add(table,BorderLayout.CENTER);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
scroll.getViewport().add(table);
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
list.setFixedCellHeight(table.getRowHeight()*(table.getRowCount()+1)+10);
list.setFixedCellWidth(table.getPreferredSize().width);
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(!mouseListenerAdded){
list.addMouseListener(this.getListener());
mouseListenerAdded = true;
}
if(!combo.isPopupVisible()){
label.setText("Select...");
if(table.getSelectedRowCount()>0)
label.setText(""+table.getModel().getValueAt(table.getSelectedRow(),1));
return label;
}
return scroll;
}
public MouseAdapter getListener(){
MouseAdapter adapter = new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
if(combo.isPopupVisible()){
System.out.println("MouseClicked over list");
int row = table.rowAtPoint(e.getPoint());
if(row>0){
table.setRowSelectionInterval(row-1, row-1);
ListDataEvent event = new ListDataEvent(combo,ListDataEvent.CONTENTS_CHANGED,row-1,row-1);
combo.contentsChanged(event);
}
}
}
};
return adapter;
}
}
}
The key parts of the solution is that in order to have the table and the features of a table that you would want in a popup is you need to override the UI. Specifically you need to override the createPopup on BasicComboBoxUI, getPopupHeightForRowCount and createScroller on BasicComboPopup. Lastly, when implementing getListCellRenderingComponent you have set a fixed height and width that matches the tables preferred height and width. This will allow the main scroller of the popup to act as the scrollpane for the table.
Related
Good day.
I'm pretty new to CellRenderer and stuff and I am still reading a lot about it. Right now, I'm stuck with something I'm not sure how to work. I was able to put a JPanel inside my JList by setting it's CellRenderer to a custom one. But here is the million dollar question: How can I interact with the controls?
I mean, I want to be able to right click into the cell that contains my jPanel,
Right click to show some actions, and Highlight the selected row.
How can I achieve this?
heres is my code:
import java.awt.CardLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class cellTest extends JPanel{
JLabel label;
public cellTest(){
buildInterface();
}
private void buildInterface(){
label = new JLabel();
setLayout(new CardLayout());
add(label);
}
public void setText(String text){
String pre = "<html><body style='width: 200px;'>";
label.setText(pre+text);
}
}
Second Part:
import Read.Medicine_Cell;
import java.awt.CardLayout;
import java.awt.Component;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
public class testjava {
public static void main(String args[]){
JFrame jf = new JFrame();
jf.setSize(500, 500);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLayout(new CardLayout());
JList list = new JList();
DefaultListModel dlm = new DefaultListModel();
dlm.addElement("This is a test");
dlm.addElement("I wanna know if it works");
dlm.addElement("Pardon if im not that good yet");
dlm.addElement("or my problem is basic");
list.setModel(dlm);
list.setCellRenderer(new MyCellRenderer());
jf.add(new JScrollPane(list));
jf.show();
}
static class MyCellRenderer extends DefaultListCellRenderer{
final cellTest cellx = new cellTest();
#Override
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus){
final String text = (String) value;
cellx.setText(text);
return cellx;
}
}
}
I'm writing a GUI using Swing. I have a custom written JComboBox using a ListCellRenderer and a BasicComboBoxEditor.
In my getListCellRendererComponent() method I change the color of the the list based on whether the item is "selected" (mouse is hovering above), which is nice and all, but I don't want the selection to change background color once a choice is made, which it currently does.
The first picture shows how the interface looks before a selection is made, and the second one shows how it looks after.
QUESTION
How do I change the background of the "selection" to the "stockColor"?
MCVE
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.util.Vector;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.plaf.basic.BasicComboBoxEditor;
public class TFComboBox extends JComboBox{
public static void main(String[] args){
createAndShowGUI();
}
public static void createAndShowGUI(){
JFrame frame = new JFrame("MCVE");
JPanel pane = new JPanel(new BorderLayout());
TFComboBox cb = new TFComboBox();
boolean[] tf = {true, false};
cb.addItems(tf);
JButton b = new JButton("Click me!");
pane.add(cb, BorderLayout.CENTER);
pane.add(b, BorderLayout.LINE_END);
frame.add(pane);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private DefaultComboBoxModel model;
private Vector<Boolean> comboBoxItems;
private JComboBox comboBox;
public TFComboBox(){
comboBoxItems = new Vector<Boolean>();
comboBoxItems.add(Boolean.TRUE);
comboBoxItems.add(Boolean.FALSE);
comboBox = new JComboBox(comboBoxItems);
model = new DefaultComboBoxModel();
setModel(model);
setRenderer(new TrueFalseComboRenderer());
setEditor(new TrueFalseComboEditor());
}
public void addItems(boolean[] items){
for(boolean anItem : items){
model.addElement(anItem);
}
}
}
class TrueFalseComboRenderer extends JPanel implements ListCellRenderer {
private JLabel labelItem = new JLabel();
private Color stockColor = labelItem.getBackground();
public TrueFalseComboRenderer(){
setLayout(new BorderLayout());
labelItem.setOpaque(true);
labelItem.setHorizontalAlignment(JLabel.CENTER);
add(labelItem);
setBackground(Color.LIGHT_GRAY);
}
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
boolean tempValue = (boolean) value;
labelItem.setText(Boolean.toString(tempValue));
if(isSelected){
labelItem.setBackground(stockColor.darker());
labelItem.setForeground(Color.WHITE);
} else {
labelItem.setBackground(stockColor);
labelItem.setForeground(Color.BLACK);
}
return this;
}
}
class TrueFalseComboEditor extends BasicComboBoxEditor {
private JLabel labelItem = new JLabel();
private JPanel panel = new JPanel();
private Object selectedItem;
public TrueFalseComboEditor() {
labelItem.setOpaque(false);
labelItem.setHorizontalAlignment(JLabel.CENTER);
labelItem.setForeground(Color.WHITE);
panel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2));
panel.setBackground(Color.BLUE);
panel.add(labelItem);
}
public Component getEditorComponent(){
return this.panel;
}
public Object getItem(){
return this.selectedItem;
}
public void setItem(Object item){
if(item == null){
return;
}
this.selectedItem = item;
labelItem.setText(item.toString());
}
}
EDIT
I've added a MCVE and as you can see it is the "problem" that the JComboBox is focused that has to do with my issue. I've placed a button next to the ComboBox to help with removing the focus from the ComboBox.
Simply doing a setFocusable(false) would fix it, but also take away some of the functionality of the rest of the program, so this is not desired.
for better help sooner post an SSCCE / MCVE, short, runnable, compilable, with hardcoded value for JComboBox / XxxComboBoxModel in local variable
JList has Boolean array implemented as default in API (no idea whats hidden in
String trueFalseItem = Boolean.toString(tempValue); and with value stored JComboBox model)
this is just code minimum to test isSelected and to change JList.setSelectionXxx inside DefaultListCellRenderer
for example (code in SSCCE / MCVE form)
.
.
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.SwingUtilities;
public class ComboBoxBooleanModel {
private javax.swing.Timer timer = null;
private Vector<Boolean> comboBoxItems;
private JComboBox box;
public ComboBoxBooleanModel() {
comboBoxItems = new Vector<Boolean>();
comboBoxItems.add(Boolean.TRUE);
comboBoxItems.add(Boolean.FALSE);
box = new JComboBox(comboBoxItems);
box.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (c instanceof JLabel) {
JLabel l = (JLabel) c;
if (Boolean.TRUE.equals(value)) {
l.setBackground(Color.RED);
if (isSelected) {
list.setSelectionForeground(Color.RED);
list.setSelectionBackground(Color.BLUE);
}
} else if (Boolean.FALSE.equals(value)) {
l.setBackground(Color.BLUE);
if (isSelected) {
list.setSelectionForeground(Color.BLUE);
list.setSelectionBackground(Color.RED);
}
}
return l;
}
return c;
}
});
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(box);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
box.setSelectedIndex(1);
}
});
start();
}
private void start() {
timer = new javax.swing.Timer(2250, updateCol());
timer.start();
}
public Action updateCol() {
return new AbstractAction("text load action") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
if (box.getSelectedItem() == (Boolean) false) {
box.setSelectedItem((Boolean) true);
} else {
box.setSelectedItem((Boolean) false);
}
}
};
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComboBoxBooleanModel comboBoxModel = new ComboBoxBooleanModel();
}
});
}
}
Here is a short demo of 2 JCombos, one of which will not change its background color when selected :
public static void main(String[] args){
JFrame frame = new JFrame("Combos BG Color test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.getContentPane().setPreferredSize(new Dimension(400, 40));
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(1,2));
frame.add(mainPanel);
JComboBox<String> aCombo = new JComboBox<>(new String[]{"A","B","C"});
mainPanel.add(aCombo);
JComboBox<String> bCombo = new JComboBox<>(new String[]{"1","2","3"});
Color bgColor = bCombo.getBackground();
bCombo.setRenderer(new DefaultListCellRenderer() {
#Override
public void paint(Graphics g) {
setBackground(bgColor);
super.paint(g);
}
});
mainPanel.add(bCombo);
frame.pack();
frame.setVisible(true);
}
(Most of the credit goes to this answer)
I would like to have a jtable with 4 columns.
One column must be a combobox.
Other columns are strings.
When i click one time on a cell, i would like the cell become editable with blinking caret/cursor.Also, if i click on the combobox i would like the combox set popup visble.
I have read and tested tutorial "How to Use Tables", and if i make just one click on combobox in the cell, it opens. My first problem is that i don't understand why the code in the tutorial works when we implement abstract table model and not working DefaultTableModel.
My code is :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class JtabletestOK {
public static void main(String[] args) {
JtabletestOK test = new JtabletestOK();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create a table and add it to a scroll pane in a new tab
JTable jTable1 = new JTable()
{
// Place cell in edit mode when it 'gains focus'
public void changeSelection(
int row, int column, boolean toggle, boolean extend)
{
super.changeSelection(row, column, toggle, extend);
if (editCellAt(row, column))
{
Component editor = getEditorComponent();
editor.requestFocusInWindow();
// System.out.println("ffin focus gagne");
if (editor instanceof JTextField) {
JTextField jf = (JTextField) editor;
jf.select(0, jf.toString().length());
}}
}
};
jTable1.setPreferredScrollableViewportSize(new Dimension(800,100));
jTable1.setFillsViewportHeight(true);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(jTable1);
Object columnNames[] = { "DataCombo", "Data 2", "Data 3", "Data 4" };
DefaultTableModel model = new DefaultTableModel(columnNames, 0);
Object rowData[] = { "", "Row1-Column2", "Row1-Column3", "Row1-Column3" };
model.addRow(rowData);
jTable1.setModel(model);
String[] comboBoxArray = {"proem1","veitem2","atem3"};
JComboBox jcb = new JComboBox(comboBoxArray);
jcb.setEditable(true);
TableColumn colCombo = jTable1.getColumnModel().getColumn(0);
colCombo.setCellEditor(new DefaultCellEditor(jcb));
jcb.setEditable(true);
frame.getContentPane().add(scrollPane);
frame.pack();
frame.setVisible(true);
}
The source with abstract model is here :
http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/uiswing/examples/components/TableRenderDemoProject/src/components/TableRenderDemo.java
Just find the problem : When commenting the statement jcb.setEditable(true); , if i do a single click on the comboxcell it opens the cell.
But i don't know why it works better. Also, i would like the combox editable.
How i can have the same behavior for others cells.
Hello again, i have updated the code in order to have
- cells become editable if i click one time on the cell by overriding the method
- combobox become editable if i click one time on the cell by overriding the method
I put my new code here, it may help other :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.AbstractCellEditor;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import javax.swing.text.JTextComponent;
public class JtabletestOK {
public static void main(String[] args) {
JtabletestOK test = new JtabletestOK();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create a table and add it to a scroll pane in a new tab
JTable jTable1 = new JTable()
{
// Place cell in edit mode when it 'gains focus'
public void changeSelection(
int row, int column, boolean toggle, boolean extend)
{
super.changeSelection(row, column, toggle, extend);
if (column > 0)
{
if (editCellAt(row, column))
{
Component editor = getEditorComponent();
editor.requestFocusInWindow();
// System.out.println("ffin focus gagne");
if (editor instanceof JTextField) {
JTextField jf = (JTextField) editor;
jf.select(0, jf.toString().length());
}}
}
}
};
jTable1.setPreferredScrollableViewportSize(new Dimension(800,100));
jTable1.setFillsViewportHeight(true);
replaceTabByEnter(jTable1);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(jTable1);
Object columnNames[] = { "DataCombo", "Data 2", "Data 3", "Data 4" };
DefaultTableModel model = new DefaultTableModel(columnNames, 0);
Object rowData[] = { "", "Row1-Column2", "Row1-Column3", "Row1-Column3" };
model.addRow(rowData);
jTable1.setModel(model);
String[] comboBoxArray = {"proem1","veitem2","atem3"};
JComboBox jca = new JComboBox(comboBoxArray);
jca.setSelectedItem("");
JTextComponent editor = (JTextComponent) jca.getEditor().getEditorComponent();
jca.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
JComponent ja = (JComponent) e.getSource();
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
JComponent ja = (JComponent) e.getSource();
JTable jtb = (JTable) ja.getParent();
jtb.changeSelection(0,1,false,false);
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
editor.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Not mouseClicked yet.");
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Not mousePressed yet.");
}
#Override
public void mouseReleased(MouseEvent e) {
JComponent ja = (JComponent) e.getSource();
JComponent jcbloc = (JComponent) ja.getParent();
JComboBox jcb = (JComboBox) jcbloc;
jcb.setPopupVisible(true);
System.out.println("Not mouseReleased yet.");
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Not mouseEntered yet.");
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Not mouseExited yet.");
}
});
autocompletecombo jcb =new autocompletecombo(jca);
TableColumn colCombo = jTable1.getColumnModel().getColumn(0);
jca.setEditable(true);
comboboxEditor cbe = new comboboxEditor(jca);
colCombo.setCellEditor(cbe);
frame.getContentPane().add(scrollPane);
frame.pack();
frame.setVisible(true);
}
public void replaceTabByEnter(JTable jtane) {
KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
InputMap im = jtane.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(enter, im.get(tab));
}
class comboboxEditor extends AbstractCellEditor implements TableCellEditor{
JComboBox comboBox;
JTextField jtf;
S11InitialSelection sjcb;
#Override
public Object getCellEditorValue() {
return comboBox.getSelectedItem();
}
public comboboxEditor(JComboBox jcb) {
comboBox = jcb;
jtf.selectAll();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
comboBox.setSelectedItem(value);
return comboBox;
}
public boolean stopCellEditing() {
fireEditingStopped();
// jt.EditNextCell();
return true;
}
}
}
When i click on the combobox i would like to highlight the text by selecting it.
I don't remember how to get the textfield from the jcombobox in order to use selectAll() method or something like that.
I have find a solution for that :
public void mouseReleased(MouseEvent e) {
JComponent ja = (JComponent) e.getSource();
JComponent jcbloc = (JComponent) ja.getParent();
JComboBox jcb = (JComboBox) jcbloc;
jcb.setPopupVisible(true);
JTextComponent editor = (JTextComponent) jcb.getEditor().getEditorComponent();
editor.setSelectionStart(0);
editor.setSelectionEnd(editor.getText().length());
System.out.println("Not mouseReleased yet.");
}
Thanks for your help.
I have finally find how to solve all my problems. I post all the code.
I hope it will help others. If you find better way to adress the problem, i'm open.
It remains two strange thing but it work as i want, so if somenone as an idea :
If i move the code
if(e.getKeyChar() == KeyEvent.VK_ENTER){
JComponent ja = (JComponent) e.getSource();
JComboBox jcbloc = (JComboBox) ja.getParent();
JTable jtb = (JTable) jcbloc.getParent();
jtb.changeSelection(0,1,false,false);
}
in keyReleased instead of keyPressed, it doesn't work.
When i press enter on basic cell text, the program go to next cell alone. I don't understand how it goes alone to next cell on enter key.
All the code here :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.AbstractCellEditor;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicTextUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import javax.swing.text.JTextComponent;
public class JtabletestOKStackOver {
public static void main(String[] args) {
JtabletestOKStackOver test = new JtabletestOKStackOver();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create a table and add it to a scroll pane in a new tab
JTable jTable1 = new JTable()
{
// Place cell in edit mode when it 'gains focus'
public void changeSelection(
int row, int column, boolean toggle, boolean extend)
{
super.changeSelection(row, column, toggle, extend);
if (column > -1)
{
if (editCellAt(row, column))
{
Component editor = getEditorComponent();
editor.requestFocusInWindow();
if (editor instanceof JTextField) {
JTextField jf = (JTextField) editor;
jf.select(0, jf.toString().length());
}
if (editor instanceof JComboBox) {
JComboBox jcb = (JComboBox) editor;
jcb.setPopupVisible(true);
JTextComponent editorCombo = (JTextComponent) jcb.getEditor().getEditorComponent();
editorCombo.setSelectionStart(0);
editorCombo.setSelectionEnd(editorCombo.getText().length());
}
}
}
}
};
jTable1.setPreferredScrollableViewportSize(new Dimension(800,100));
jTable1.setFillsViewportHeight(true);
jTable1.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(jTable1);
Object columnNames[] = { "DataCombo", "Data 2", "Data 3", "Data 4" };
DefaultTableModel model = new DefaultTableModel(columnNames, 0);
Object rowData[] = { "", "Row1-Column2", "Row1-Column3", "Row1-Column3" };
model.addRow(rowData);
jTable1.setModel(model);
String[] comboBoxArray = {"proem1","veitem2","atem3"};
JComboBox jca = new JComboBox(comboBoxArray);
jca.setSelectedItem("");
JTextComponent editor = (JTextComponent) jca.getEditor().getEditorComponent();
editor.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER){
JComponent ja = (JComponent) e.getSource();
JComboBox jcbloc = (JComboBox) ja.getParent();
JTable jtb = (JTable) jcbloc.getParent();
jtb.changeSelection(0,1,false,false);
}
}
#Override
public void keyReleased(KeyEvent e) {
}
});
editor.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
JComponent ja = (JComponent) e.getSource();
JComponent jcbloc = (JComponent) ja.getParent();
JComboBox jcb = (JComboBox) jcbloc;
jcb.setPopupVisible(true);
JTextComponent editor = (JTextComponent) jcb.getEditor().getEditorComponent();
editor.setSelectionStart(0);
editor.setSelectionEnd(editor.getText().length());
}
#Override
public void focusLost(FocusEvent e) {
}
});
TableColumn colCombo = jTable1.getColumnModel().getColumn(0);
jca.setEditable(true);
comboboxEditor cbe = new comboboxEditor(jca);
colCombo.setCellEditor(cbe);
TableColumn colAutre = jTable1.getColumnModel().getColumn(1);
TableColumn colAutre2 = jTable1.getColumnModel().getColumn(2);
TableColumn colAutre3 = jTable1.getColumnModel().getColumn(3);
textCellEditor dce = new textCellEditor(new JTextField());
colAutre.setCellEditor(dce);
colAutre2.setCellEditor(dce);
colAutre3.setCellEditor(dce);
frame.getContentPane().add(scrollPane);
frame.pack();
frame.setVisible(true);
}
class comboboxEditor extends AbstractCellEditor implements TableCellEditor{
JComboBox comboBox;
public comboboxEditor(JComboBox jcb) {
comboBox = jcb;
}
public Object getCellEditorValue() {
return comboBox.getSelectedItem();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
comboBox.setSelectedItem(value);
return comboBox;
}
public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
}
class textCellEditor extends AbstractCellEditor implements TableCellEditor{
JTextField jtextfield;
public textCellEditor(JTextField jtf) {
jtextfield = jtf;
}
public Object getCellEditorValue() {
return jtextfield.getText();
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if (isSelected) {
// cell (and perhaps other cells) are selected
}
if (value== null)
{
value="";
}
value = value.toString();
if (value instanceof Integer) {
value = value.toString();
}
jtextfield.setText((String) value);
// Return the configured component
return jtextfield;
}
public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
}
}
Thanks for your help.
Don't use an ActionListener on the combo box. The "popup" will be displayed by the editor when you click on the cell.
My first problem is that i don't understand why the code in the tutorial works when we implement abstract table model and not working DefaultTableModel.
Editing is controlled by the isCellEditable(...) method of the TableModel. When you click on a cell that is editable the JTable will use the appropriate editor. If the editor is a combo box, then the popup will be displayed when the cell is clicked.
Just find the problem : When commenting the statement jcb.setEditable(true); , if i do a single click on the comboxcell it opens the cell. But i don't know why it works better
When you click on a cell I believe the MouseEvent is forwarded to the editor (ie the combo box). Since the combo box is editable the MouseEvent goes to the text field, so focus remains of the text field and the popup is not displayed. Try this with a normal combo box, not displayed in a JTable so see the same behaviour.
I don't know why your code with AbstractTableModel works (because I cannot see it). But I can help you to make your example working.
jcb.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
System.out.println("test");
final JComboBox j= (JComboBox)evt.getSource();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (j.isDisplayable()) j.setPopupVisible(true);
}
});
}});
Designing a custom JTable I already know that DefaultCellEditor admits a JComboBox in his constructor. This JComboBox, when clicked to display the list items, shows above the other cells of the JTable. The problem is that I need a more sophisticated behavior as what JComboBox offers, so that I've implemented a JTextField with a JList and a JButton, when the JButton gets clicked (or the user enters text in the JTextField) the elements in the JList become visible. This 3 elements are in a JPanel. When I try to use this panel as a cell editor (extending AbtractCellEditor and implementing TableCellEditor) the elements in the list show inside the editing cell but I cannot mimic the behavior of the DefaultCellEditor with the combo so that the list elements show above the JTable.
Here I define the custom cell editor: (very short version);
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableCellEditor;
public class CustomCellEditor extends AbstractCellEditor implements TableCellEditor {
private JList list;
private JButton button;
private JTextField editor;
private JPanel mainPanel;
public CustomCellEditor() {
list = new JList(new String[] { "One", "Two", "Three" });
editor = new JTextField();
button = new JButton("Click me ");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
list.setVisible(!list.isVisible());
}
});
JPanel auxPanel = new JPanel(new BorderLayout());
auxPanel.add(editor, BorderLayout.CENTER);
auxPanel.add(button, BorderLayout.EAST);
mainPanel = new JPanel(new BorderLayout());
mainPanel.add(auxPanel, BorderLayout.NORTH);
mainPanel.add(list, BorderLayout.CENTER);
}
#Override
public Object getCellEditorValue() {
return editor.getText();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return mainPanel;
}
}
And this a main program with a jtable with this panel as a cell editor:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class CustomTable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
CustomTable instance = new CustomTable();
instance.createAndShowUI();
}
});
}
private void createAndShowUI() {
JTable table = new JTable(new CustomTableModel());
//So that I can see the contents of the list when edited
table.setRowHeight(60);
TableColumn editableColumn = table.getColumnModel().getColumn(0);
editableColumn.setPreferredWidth(250);
editableColumn.setCellEditor(new CustomCellEditor());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(table, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private class CustomTableModel extends AbstractTableModel {
private final String[] columnNames = {"Editable column", "Other column"};
private final Object[][] data = {
{"Ricardo", "Mr."},
{"Josefina", "Ms."}
};
#Override
public int getRowCount() {
return data.length;
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data[rowIndex][columnIndex];
}
#Override
public boolean isCellEditable(int row, int col) {
if (0 == col)
return true;
return false;
}
#Override
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
}
Can someone help me?
Thanks!!
Using a JComboBox as a table editor relies on it being a button that displays a popup component when pressed. As you have several components in a cell, consider these alternatives:
Add the components to a panel, as shown here for a group of JRadioButton instances.
Add the components to a modal dialog, as shown here using JDialog.
I would like to create JComboBox control similar with the URL textbox of Firefox. Does anyone know how to customize the textfield of the JComboBox. I want to add some icons on the ALIGN.HORIZONTAL_RIGHT near the arrow button of the JComboBox
Thanks for your very detail explanation. Actually I will combine DefaultListCellRenderer and add the icon to the combo box like following code
import java.awt.Dimension;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main extends JFrame {
public Main() {
// Create icon "C"
JButton jb = new JButton("C");
jb.setMargin(new Insets(0, 0, 0, 0));
jb.setBounds(245, 2, 18, 18);
// Create combo box
String[] languages = new String[]{"Java", "C#", "PHP"};
JComboBox jc = new JComboBox(languages);
// jc.setEditable(true);
jc.add(jb);
getContentPane().add(jc);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(300, 58));
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
final Main main = new Main();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
main.setVisible(true);
}
});
}
}
But when I put jc.setEditable(true); the combo editor hided my icon. I'm thinking another way to simulate Firefox awesome bar. Do you have any idea for this?
To change how a component renders, you generally work with what are called Renderers.
For combobox, look at how to create a custom combobox renderer. Just a quick glance, but for your case, a simple configuration of DefaultListCellRenderer may be enough, since you can set the JLabel properties to position the text to the image. If not, just extend from it.
Remember also that you have to set a model that includes the icon so that the combobox renderer can get it - might want to do a custom ComboBoxModel too, depending on your data object.
Here is completed example that demonstrate it:
package com.demo.combo.icon;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ShowConboWithIcons extends JFrame {
private static final long serialVersionUID = 1L;
private static final ImageIcon INFO_ICON = new ImageIcon("info.png");
private static final ImageIcon NONE_ICON = new ImageIcon("none.png");
public final String NONE_STR = "None";
private final String INFO_STR = "Info";
private JComboBox comboBox;
private JPanel topPanel;
private String[] str_arr = null;
public ShowConboWithIcons(String[] str_arr) {
this.str_arr = str_arr;
}
public void createGUI(){
setMinimumSize(new Dimension(100,100));
setTitle("Demo");
setLocation(200, 200);
topPanel = new JPanel();
getContentPane().add(topPanel, BorderLayout.CENTER);
Map<Object, Icon> icons = new HashMap<Object, Icon>();
icons.put(NONE_STR, NONE_ICON);
icons.put(INFO_STR, INFO_ICON);
comboBox = new JComboBox();
comboBox.setRenderer(new IconListRenderer(icons));
comboBox.addItem("None");
for(String val : str_arr){
comboBox.addItem(val);
}
topPanel.add(comboBox);
super.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
}
});
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
String[] str_arr = {"A", "B", "C", "D", "E"};
ShowConboWithIcons T = new ShowConboWithIcons(str_arr);
T.createGUI();
T.setVisible(true);
}
class IconListRenderer extends DefaultListCellRenderer{
private static final long serialVersionUID = 1L;
private Map<Object, Icon> icons = null;
public IconListRenderer(Map<Object, Icon> icons){
this.icons = icons;
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index,boolean isSelected, boolean cellHasFocus)
{
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
// Get icon to use for the list item value
Icon icon = icons.get(value);
if(!value.toString().equals(NONE_STR)){
icon = icons.get(INFO_STR);
}
// Set icon to display for value
label.setIcon(icon);
return label;
}
}
}
Preview: