I am working on making the Arduino IDE fully accessible for people using access software. This means adding keyboard accessibility to two of the dialogs that currently do not work with access software under windows. The two dialogs are the Board Manager and the LibraryManager. If you want a real example of what I am posting you can get teh Arduino IDE on the Windows Store or download it on line. Just search for Arduino IDE and download the windows version. .
doing all that might be to much for most of you so I have created a more simple table that shows the same problem. If someone can help me understand how to add keyboard and focus access to the below Custom JTable and its custom column which is a JPanel I can work on fixing the Arduino IDE tables.
I understand that the Arduino IDE should probably be made into a
better table but I have yet to figure out a cleaner way to do what they are doing with these tables so I am trying to make what they have accessible.
So below is my problem table. It has two rows and two columsn. The
second column of each row is a panel of controls. I have borrowed
this code from some other stack posts and modified it to fit my
question. So if you think you have seen some of this code before that
is why. The problem is the code works for a mouse. At least I think
it does because I am blind and can not select the controls on the
second column because they can not get keyboard focus.
So Can someone out there help me figure out how to get keyboard focus
to move through this entire table. I have tried using focus
listeners, keyboard listeners, Focus Traversal policies, property
changes, and combinations between all of the above. I have taken out
my terrible attempts. Swing really should work without having to do
all this extra work but Java is what java is.
So if you are great at Java with swing please put away your mouse and run this
code and see if you can make the buttons activate without the mouse.
If you can and you can explain to me how to do it many blind users will
be in your debt.
Note that you should be able to Shift+Control+tab tot he table if the table is not the only control. On my sample it is. Then you should be able to arrow through the rows and columns and tab and shift+tab through the controls on the JPanel.
You can get the zip group of these files if you don't want to
put this all together yourself at this dropbox link:
https://www.dropbox.com/s/h4bdfu1mlo0jsvr/TableAccessTest.zip?dl=0
Here is the files. Each class has a comment with the file name before it. Again get the zip if you don't want to play block and copy.
//TableAccessTest.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
public class TableAccessTest extends JPanel
{
TableModel customRendererEditorTableModel = new TableModel();
JTable table;
public TableAccessTest()
{
setLayout(new BorderLayout());
table = new JTable(customRendererEditorTableModel);
table.setPreferredScrollableViewportSize(new Dimension(500, 600));
table.setRowSelectionAllowed(false);
table.setRowHeight(300);
table.getColumn("Custom JPanel").setCellRenderer(new CustomJPanelRenderer());
table.getColumn("Custom JPanel").setCellEditor(new CustomJPanelEditor());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Custom JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new TableAccessTest();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
//CustomJPanel.java
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;
public class CustomJPanel extends JPanel
{
JPanel colorPanel = new JPanel(new BorderLayout());
JButton jButton = new JButton("Change Color");
JSlider jSlider = new JSlider(JSlider.VERTICAL);
JTextField jTextField = new JTextField("");
public CustomJPanel()
{
jButton.setOpaque(true);
jButton.setFocusable(true);
jTextField.setEditable(false);
jTextField.setFocusable(true);
setFocusable(true);
jButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
Random randomGenerator = new Random();
int randomInt = randomGenerator.nextInt(4);
switch (randomInt)
{
case (0):
colorPanel.setBackground(Color.BLUE);
jTextField.setText("blue");
break;
case (1):
colorPanel.setBackground(Color.BLACK);
jTextField.setText("black");
break;
case (2):
colorPanel.setBackground(Color.CYAN);
jTextField.setText("cyan");
break;
default:
colorPanel.setBackground(Color.GREEN);
jTextField.setText("green");
break;
}
}
});
jSlider.setOpaque(true);
setLayout(new BorderLayout());
add(colorPanel, BorderLayout.CENTER);
add(jButton, BorderLayout.SOUTH);
add(jTextField, BorderLayout.NORTH);
add(jSlider, BorderLayout.EAST);
}
}
//TableModel.java
import javax.swing.table.AbstractTableModel;
public class TableModel extends AbstractTableModel
{
private String[] columnNames =
{
"First Column",
"Custom JPanel",
};
private Object[][] data =
{
{"Foo", new CustomJPanel()},
{"Bar", new CustomJPanel()}
};
public int getColumnCount()
{
return columnNames.length;
}
public int getRowCount()
{
return data.length;
}
public String getColumnName(int col)
{
return columnNames[col];
}
public Object getValueAt(int row, int col)
{
return data[row][col];
}
public Class getColumnClass(int c)
{
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col)
{
return col >= 1;
}
public void setValueAt(Object value, int row, int col)
{
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
//CustomJPanelEditor.java
import javax.swing.AbstractCellEditor;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
public class CustomJPanelEditor extends AbstractCellEditor implements TableCellEditor
{
CustomJPanel customJPanel;
public CustomJPanelEditor()
{
customJPanel = new CustomJPanel();
}
#Override
public java.awt.Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
customJPanel = (CustomJPanel) value;
return customJPanel;
}
#Override public Object getCellEditorValue()
{
return customJPanel;
}
}
//CustomJPanelRenderer.java
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class CustomJPanelRenderer implements TableCellRenderer
{
CustomJPanel rendererJPanel;
public CustomJPanelRenderer()
{
rendererJPanel = new CustomJPanel();
}
#Override
public java.awt.Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
rendererJPanel = (CustomJPanel) value;
return rendererJPanel;
}
}
Related
I have tried this part of code to get values from a JComboBox that is inside of a JTable, but it doesn't work!
I want to get the value of selected cell in order to insert into DB.
package fx;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class ComboInTable extends JFrame {
private static JFrame jFrame;
public ComboInTable() throws HeadlessException {
jFrame=this;
JTable table=new JTable();
DefaultTableModel model= (DefaultTableModel) table.getModel();
model.addColumn("A",new Object[]{"item1"});
model.addColumn("B",new Object[]{"item2"});
JScrollPane scrollPane=new JScrollPane(table);
String[] value1=new String[]{"1","2","3"};
String[] value2=new String[]{"a","b","c"};
TableColumn col0=table.getColumnModel().getColumn(0);
TableColumn col1=table.getColumnModel().getColumn(1);
col0.setCellEditor(new MyComboBoxEditor(value1));
col0.setCellRenderer(new MyComboBoxRenderer(value1));
col1.setCellEditor(new MyComboBoxEditor(value2));
col1.setCellRenderer(new MyComboBoxRenderer(value2));
JComboBox comboBox=new JComboBox(value1);
comboBox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() == ItemEvent.SELECTED)
{
System.out.println(e.getItem());
}
}
});
jFrame.setLayout(new FlowLayout());
jFrame.add(scrollPane);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setSize(400, 400);
jFrame.setVisible(true);
}
public static void main(String[] args) {
ComboInTable comboInTable=new ComboInTable();
}
}
I have MyComboBoxEditor and MyComboBoxRenderer classes.
package fx;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
public class MyComboBoxRenderer extends JComboBox implements TableCellRenderer {
public MyComboBoxRenderer(String[] items) {
super( items);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(isSelected){
setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
}
else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelectedItem(value);
return this;
}
}
package fx;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
public class MyComboBoxEditor extends DefaultCellEditor {
public MyComboBoxEditor(String[] items) {
super(new JComboBox(items));
}
}
I have tried this part of code to get values from a JComboBox that is inside of a JTable,
You don't get the value from the combo box.
You get the value from the JTable using the getValueAt(...) method.
I also have no idea why you are creating a custom renderer and editor. Just use the default renderer/editor provided by the table.
Start by reading the section from the Swing tutorial on How to Use Tables. You will find an example that shows how to use a combo box as an editor.
I am trying to create a JTable which has clickable buttons in one column. I have been following a tutorial found here: https://www.youtube.com/watch?v=3LiSHPqbuic. Essentially, when you click one of the buttons a message box pops up. My code almost works but I have to double click the buttons rather than single click. I have been playing around with it for quite sometime and not made much progress. Can anyone see where I'm going wrong?
package buttoncolumn;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableCellRenderer;
public class ButtonColumn extends JFrame {
public ButtonColumn() {
super("Button Column Example");
//Data for table
Object[][] data=
{
{"1","Man Utd",new Integer(2013),"21"},
{"2","Man City",new Integer(2014),"3"},
{"3","Chelsea",new Integer(2015),"7"},
{"4","Arsenal",new Integer(1999),"10"},
{"5","Liverpool",new Integer(1990),"19"},
{"6","Everton",new Integer(1974),"1"},
};
//Column header
String columnHeaders[]={"Position","Team","Last Year Won","Trophies"};
//Create table object
JTable table=new JTable(data,columnHeaders);
//Set custom renderer to teams column
table.getColumnModel().getColumn(1).setCellRenderer(new ButtonRenderer());;
//Set custom editor to team column
table.getColumnModel().getColumn(1).setCellEditor(new ButtonEditor(new JTextField()));
//Scroll pane
JScrollPane pane=new JScrollPane(table);
getContentPane().add(pane);
setSize(450,100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
ButtonColumn bc = new ButtonColumn();
bc.setVisible(true);
}
}
//Button renderer class
class ButtonRenderer extends JButton implements TableCellRenderer
{
//Constructir
public ButtonRenderer(){
setOpaque(true);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object obj,
boolean selected, boolean focused, int row, int col) {
setText((obj==null) ? "":obj.toString());
return this;
}
}
//Button editor class
class ButtonEditor extends DefaultCellEditor
{
protected JButton btn;
private String lbl;
private Boolean clicked;
public ButtonEditor(JTextField txt){
super(txt);
btn=new JButton();
btn.setOpaque(true);
//When button is clicked
btn.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
fireEditingStopped();
}
});
}
//Overide a couple of methods
public Component getTableCellEditorComponent(JTable table, Object obj,
boolean selected, int row, int col) {
//Set button text
lbl=(obj==null) ? "":obj.toString();
btn.setText(lbl);
clicked=true;
return btn;
}
//If button cell value changes (clicked)
#Override
public Object getCellEditorValue(){
if(clicked)
{
JOptionPane.showMessageDialog(btn, lbl+" Clicked");
}
clicked = false;
return new String(lbl);
}
#Override
public boolean stopCellEditing(){
clicked=false;
return super.stopCellEditing();
}
#Override
protected void fireEditingStopped(){
super.fireEditingStopped();
}
}
Make sure you consult the documentation for more information about the classes you are creating.
Have a look at the JavaDocs for DefaultCellEditor and Using Other Editors for more details.
Essentially, you need to modify the isCellEditable method of the DefaultCellEditor
class ButtonEditor extends DefaultCellEditor {
//...
#Override
public boolean isCellEditable(EventObject anEvent) {
return anEvent instanceof MouseEvent && ((MouseEvent) anEvent).getClickCount() == 1 && SwingUtilities.isLeftMouseButton((MouseEvent) anEvent);
}
}
Or, if you really want it simple, just use setClickCountToStart
class ButtonEditor extends DefaultCellEditor {
//...
public ButtonEditor(JTextField txt) {
super(txt);
setClickCountToStart(1);
//...
}
}
Well, I'm trying to render some components in the tables mentioned above. I know thats done with implementing a custom TableCellRenderer, and that works just fine in normal JTable, e.g. doing something like this:
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
public class Test
{
public static void main(String[] argv)
{
String[] columnNames = {"First Name","Last Name","nr"};
Object[][] data = {{"Homer","Simpson","1"},{"Madge","Simpson","2"},{"Bart","Simpson","3"},
{"Lisa","Simpson","4"},};
DefaultTableModel model = new DefaultTableModel(data,columnNames);
JTable table = new JTable(model);
table.getColumnModel().getColumn(2).setCellRenderer(new TableCellRenderer(){
final JButton button = new JButton();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
button.setText(value.toString());
return button;
}
});
JFrame f = new JFrame();
f.setSize(300,300);
f.add(new JScrollPane(table));
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
It also works with JIDE's Sortable- and AggregateTable, but as soon as I replace those tables with Xdev's, the component isn't rendered anymore, so it seems the CellRenderer isn't set properly, even though TableColumn.getCellRenderer() returns the correct class.
So does anyone know how to render a component in Xdev's tables?
Thanks in advance!
I have in my application two ComboBoxes for rows and columns for each JTable. Shown below:
I want to add and remove rows and columns in each table dynamically. I know I would do something like table.add() but what about remove? For example if user selects 3 from Row combobox, it should add another row to it but what if user selects 2 back? It should remove that row inserted. So how do I do that?
I know it can be a stupid question but I am a newbie to Swing so people can't really expect much from me :(
It seems that you want to control the dimension of the table using combo boxes. JTable renderers its underlying data model. Read more about tables in How to Use Tables. Adding and removing rows is actually manipulation of the data model. For example DefaultTableModel has many methods useful for your task: addRow(), removeRow(), getRowCount() etc.
It all depends on the data and task required. Check out this simple example that uses a custom table model that wraps Apache's RealMatrix. You can choose whatever data structure you need. DefaultTableModel is also good an may be sufficient. The example has two combo boxes that adjust table size.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;
public class TestTableDims extends JPanel{
private MyTableModel model;
private JTable table;
private JComboBox rowsCombo;
private JComboBox columnsCombo;
public TestTableDims() {
setLayout(new BorderLayout(5, 5));
model = new MyTableModel(2, 2);
JPanel buttonsPanel = new JPanel();
Integer[] test = {1, 2, 3, 4, 5};
rowsCombo = new JComboBox(test);
rowsCombo.setSelectedItem(2);
rowsCombo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.setRows(((Integer)rowsCombo.getSelectedItem()));
}
});
buttonsPanel.add(new JLabel("rows"));
buttonsPanel.add(rowsCombo);
columnsCombo = new JComboBox(test);
columnsCombo.setSelectedItem(2);
columnsCombo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.setColumns(((Integer)columnsCombo.getSelectedItem()));
}
});
buttonsPanel.add(new JLabel("columns"));
buttonsPanel.add(columnsCombo);
add(buttonsPanel, BorderLayout.NORTH);
JTable table = new JTable();
table.setModel(model);
add(new JScrollPane(table));
}
class MyTableModel extends AbstractTableModel {
private RealMatrix matrix;
public MyTableModel(int rows, int columns) {
matrix = new Array2DRowRealMatrix(rows, columns);
}
public void setRows(int rows) {
matrix = new Array2DRowRealMatrix(rows, matrix.getColumnDimension());
fireTableStructureChanged();
}
public void setColumns(int columns) {
matrix = new Array2DRowRealMatrix(matrix.getRowDimension(), columns);
fireTableStructureChanged();
}
#Override
public int getColumnCount() {
return matrix.getColumnDimension();
}
#Override
public int getRowCount() {
return matrix.getRowDimension();
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return Double.class;
}
#Override
public void setValueAt(Object value, int row, int column) {
matrix.setEntry(row, column, (double)value);
}
#Override
public Object getValueAt(int row, int column) {
return matrix.getEntry(row, column);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestTableDims panel = new TestTableDims();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
}
I am trying to nest a JTable inside of another JTable's column (using a CellRenderer).
Example (erroneous) output:
Why does the following example not output a table within a table?
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
public class Test {
public static void main(String[] args) {
JTable table = new JTable(new CustomTableModel());
table.setDefaultRenderer(Person.class, new CustomRenderer());
JPanel panel = new JPanel();
panel.add(new JScrollPane(table));
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
}
#SuppressWarnings("serial")
class CustomTableModel extends AbstractTableModel {
#Override
public int getColumnCount() {
return 2;
}
#Override
public int getRowCount() {
return 1;
}
#Override
public Object getValueAt(int row, int col) {
if (col == 0) {
return new Person("Bob");
} else {
return "is awesome!";
}
}
}
#SuppressWarnings("serial")
class CustomRenderer extends JPanel implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int col) {
JTable t = new JTable(new CustomTableModel());
add(new JScrollPane(t));
return this;
}
}
class Person {
public String name = "";
public Person(String name) {
this.name = name;
}
}
Don't add the sub table to a scroll pane. Instead, try adding the table to the center position of a JPanel with a BorderLayout, then add the tables header to the NORTH position
The main reason for this is the scroll pane will not be interactive and may hide data
Updated
I'm on phone, so it's hard to read the code :P
In your main table model, you need to override the getColumnClass method and make sure you are retuning Person.class for the required column (and the correct class types for the other columns to)