在JTable中设置类型为Combobox的渲染器/编辑器

时间:2016-11-06 20:51:15

标签: jtable tablecellrenderer

我得到一个JTable,其内容是类“item”的Vector。类项包含带有正确getter / setter的数字和字母数字字段,但字段alt是字符串的ArrayList除外。对于本专栏,我打算使用comboBox样式的渲染器和编辑器,以便选择一个可用的选项并显示选择。 alt字段因行而异,这就是我的问题所在。我在这里看到了很多这样的渲染器的例子,但是在所有的例子中,每行的可用选择是相同的。这是代码:

public class item extends Vector {

private int no, code;
private String ....;
private List<String> alt;

@SuppressWarnings("unchecked")
public item(int no, int code, ...., List alt) {
    super();
    this.add(no);
    this.add(code);
    .... 
    this.add(alt);
}

public int getNo() {
    return (int) this.get(0);
}

public void setNo(int no) {
    this.no = no;
}

// other setter/getters...

public void setAlt(List<String> alt) {
    this.alt = alt;
}

@SuppressWarnings("unchecked")
public List<String> getAlt() {
    return (List<String>) this.get(6);
}

@SuppressWarnings("all")
public Vector getAsVector() {
    // ArrayList<Object> a= new ArrayList<Object>();
    Vector a = new Vector(7);
    a.add(no);
    a.add(sifra);
    a.add(red1);
    a.add(red2);
    a.add(sastojci);
    a.add(eanKod);
    a.add(alt);
    return a;
}

}

JTable获得必要的功能:

public Class getColumnClass(int ci){
    if (ci==6) return JComboBox.class;
    else return String.class;       
}


@Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
    item it = data.get(rowIndex);

    switch (columnIndex) {
    case 0:
        it.setNo((int) value);
        break;
    case 1:
        it.setCode((int) value);
        break;
    case 2:
        ....
    case 6:

        it.setAlt((List<String>) value);
        break;
    }
}   

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
    Object returnValue = null;
    item it = data.get(rowIndex);

    switch (columnIndex) {
    case 0:
        returnValue = it.getNo();
        break;
    case 1:
        returnValue = it.getCode();
        break;
    case 2:
        ... 
    case 6:
        returnValue = it.getAlt();
        break;
    }

    return returnValue;
}       

public boolean isCellEditable(int rowIndex, int columnIndex) {
    return columnIndex > 0;
}  

最后是渲染器/编辑器类:

    String[] sval = {"alfa", "Beta", "Gamma"};   //dummy array

protected class ComboRenderer extends JComboBox implements TableCellRenderer {

    public ComboRenderer(){
        super(sval);            //works, but this is not I want
//      here should be used a similar super(list of options for a certain row), but how to get it?
//      setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
        setOpaque(true);
    }


    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {

    if (isSelected) {
              setForeground(table.getSelectionForeground());
              setBackground(table.getSelectionBackground());
        } else {
              setForeground(table.getForeground());
              setBackground(table.getBackground());
        }           
        this.setSelectedItem(items); 
        return this;
    }

}


protected class ComboEditor extends AbstractCellEditor
implements TableCellEditor {

    private List<String> alt;

    protected ComboEditor() {
        super();
    }

    @Override
    public Object getCellEditorValue() {
        return this.alt;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        if (value instanceof List) {
            this.alt = (List<String>) value;
        }

        JComboBox<String> combo = new JComboBox<String>();
    // combo.setEditable(true);   //does not work for editable combobox!
        for (String a : alt) {
            combo.addItem(a);
        }

        combo.setSelectedItem(alt);

        if (isSelected) {
          setForeground(table.getSelectionForeground());
          setBackground(table.getSelectionBackground());
        } else {
          setForeground(table.getForeground());
          setBackground(table.getBackground());
        }           

        return combo;
    }

} 

以下是问题:

1)渲染器显然不适用于构造函数的错误部分(super(something))。但是在这部分我无法获得渲染的项目,所以我无法获得初始化的选择列表。如何解决这个问题?

2)编辑器似乎运行良好,但是当我从行中移动时,它再次显示旧值。我应该在这里保存一些以及如何保存?没有新数据,只需在此项目的可用选项之间进行选择。

3)如何从外部达到某一行的选定值,比方说打印?

4)如何才能获得可编辑的Combobox而不是固定?这条线

// combo.setEditable(true);   //does not work for editable combobox!

在这种情况下不起作用

1 个答案:

答案 0 :(得分:0)

与此同时,我解决了问题(第4号除外),但我认为这必须是一个更好的解决方案,所以我还在等待替代解决方案。

首先,我注意到,因为我有一个且只有渲染器的实例,

的返回值
setSelectedItems(<available options>)
无论在那里选择什么,

也将返回所有其他行的FIRST行中的选择。所以我决定为每一行创建一个渲染器实例。这意味着JTable构造函数将包含以下代码:

rendererList = new ArrayList<ComboRenderer>();
.... 
// create the Array of renderers, one for each row 
    for (int cnt=0; cnt<data.size(); cnt++){
        ComboRenderer a = new ComboRenderer(); 
        a.setOptions(cnt);
        rendererList.add(a);
    }
....
// telling to JTable which renderer to use for every cell    
@Override 
public TableCellRenderer getCellRenderer(int row, int coll) {
    if (coll==6) return rendererList.get(row);
    else return this.getDefaultRenderer(this.getColumnClass(coll));     

}   

渲染器和编辑器类现在看起来如下:

   protected class ComboRenderer extends JComboBox implements TableCellRenderer {

    List<String> items;
    int selected;
    //selected will contains the index of selected option, changed through the cell editor as listener

    public ComboRenderer(){
        super();
        setOpaque(true);
        selected=0;   //initial value
    }

    public void setSel(int k) {
        selected=k;
    }

    public int getSel() {
        return selected;
    }
    // filling each renderer with available options; 
    @SuppressWarnings("unchecked")
    public void setOptions(int row) {
        items = data.get(row).getAlt();

        for (String a : items) {
            this.addItem(a);
        }
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {

        if (isSelected) {
              setForeground(table.getSelectionForeground());
              setBackground(table.getSelectionBackground());
        } else {
              setForeground(table.getForeground());
              setBackground(table.getBackground());
        }    
        this.setSelectedIndex(selected);
        return this;
    }

}


class ComboEditor extends AbstractCellEditor implements TableCellEditor{

    private List<String> alt ;


    protected ComboEditor() {
        super();
    }

    @Override
    public Object getCellEditorValue() {
        return this.alt;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        if (value instanceof List) {
            this.alt = (List<String>) value;
        }

        JComboBox<String> combo = new JComboBox<String>();
        for (String a : alt) {
            combo.addItem(a);
        }
        final ComboRenderer  rend= rendererList.get(row);
        // adding actionlistener to letting know the renderer the selection 
        combo.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) { 
               int i=((JComboBox<?>) arg0.getSource()).getSelectedIndex();
               // telling the renderer the selection    
               rend.setSel(i);
            }
        });

        combo.setSelectedIndex(rend.getSel());

        if (isSelected) {
          setForeground(table.getSelectionForeground());
          setBackground(table.getSelectionBackground());
        } else {
          setForeground(table.getForeground());
          setBackground(table.getBackground());
        }           

        return combo;
    }
}

它现在正在运行,但代码对我来说非常笨拙。必须有办法以更优雅的方式解决这个问题。我简直无法相信每一行必须有一个独特的渲染器,这是浪费内存。有什么想法吗?