4. Editando celdas de Viewers

Imagen de elgabo

Ya hemos aprendido como mostrar una lista de elementos dentro de una tabla, pero probablemente tambien se requiera modificar los atributos de dichos elementos. Para esto se utiliza CellModifiers (modificadores de celda) para obtener los valores de cada celda y modificar dichos valores.

Pero primero se tendran que mejorar el diseño de algunas clases que tenemos. En primer lugar nuestras clases que se encargan de representar al modelo seguiran el patro de Java Beans. Los que conocen el patron tambien sabran que ahora podremos registrar clases que implementen la interface PropertyChangeListener y saber automaticamente cuando se produsca algun cambio en los elementos del modelo (seguiremos el patron de Observador visto en el capitulo anterior). No veremos el codigo del modelo que permite realizar esto, porque abarcaria mas alla de este tutorial pero es necesario tener claro como trabajan estos patrones de diseño para entender el codigo adjuntado.

Como ya tenemos un publicador de eventos, tambien debemos codificar la parte del subscriptor o receptor de eventos. Cambiaremos la clase TestContentProvider para que se adapte a estos requerimientos


public class TestContentProvider extends ArrayContentProvider implements PropertyChangeListener {
//el indice del atributo nombre
public static final int NOMBRE_INDEX=0;
//el indice del atributo casado
public static final int CASADO_INDEX=1;
//viewer al cual esta asociado
private Viewer viewer;
//metodo llamada cuando se cambia el input del viewer
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
//viewer
this.viewer=viewer;
//antiguo input (elementos del modelo)
List oldPadres=(List) oldInput;
//nuevo input (elementos del modelo)
List newPadres=(List) newInput;
//nos desregistramos de los antiguos javabeans
if(oldPadres!=null)
{
for (Padre padre : oldPadres) {
padre.removePropertyChangeListener(this);
}
}
//nos registramos contra los nuevos javabeans
if(newPadres!=null)
{
for (Padre padre : newPadres) {
padre.addPropertyChangeListener(this);
}
}
}
/**
* Maneja los eventos de cambio de propiedad en un clase del modelo
*/
public void propertyChange(PropertyChangeEvent evt) {
//refresca los elementos del viewer
this.viewer.refresh();
}
}

Hay 3 cambios importantes que se han realizado, primero ya no implementamos directamente la interface IStructuredContentProvider, sino que extendemos de la clase ArrayContentProvider, lo cual maneja el hecho de que el input del viewer sea un arreglo o lista de elementos, al implementar el metodo getElements(). El segundo cambio y tal vez el mas importante es que ahora nuestro content provider implementara la interface PropertyChangeListener, esto nos permite tomar las medidas respectivas cuando el modelo cambia, en nuestro caso refrescar el viewer. El implementar una interface no es suficiente para recibir las notificaciones que nos interesa, tambien tendremos que registrarnos contra los elementos, este es nuestro tercer cambio y se maneja mediante el metodo inputChanged el cual es llamado cada vez que el input del viewer cambia. Asi registramos y desregistramos al content provider de los elementos del modelo para poder recibir los eventos de los elementos que realmente estamos interesados.

De esta forma podremos saber cuando los elementos de nuestro modelo cambian y sobre que atributo se realizo el cambio, asi podremos actualizar nuestras vistas, el encargado de realizar este trabajo sera el controlador. Como pueden ver estamos siguiendo el patron MVC. Ahora nuestro siguiente trabajo sera generar el codigo que realiza la modificacion del modelo.

Para modificar el modelo implementaremos la interface ICellModifier, la cual declara los siguientes metodos:

boolean canModify(Object element, String property) Regresara true si la propiedad del elemento del modelo puede ser modificada
Object getValue(Object element, String property) Regresara el valor del atributo del elemento pasado como paremetro. Mas adelante veremos que el tipo de dato que regresa este metodo debe de estar de acuerdo al editor de celdas asociado a la propiedad
void modify(Object element, String property, Object value) Se encarga de modificar el atributo del elemento del modelo con un nuevo valor. Mas adelante veremos que el tipo de dato que se pasa como nuevo valor depende el editor de celdas asociado la propiedad


public class TestCellModifier implements ICellModifier {
//los nombres de los atributos de Padre
public static final String[] PROPIEDADES=new String[]{"nombre","casado"};
public boolean canModify(Object element, String property) {
/*
* si es un padres se
* podran modificar
* todos sus atributos
*/
if (element instanceof Padre) {
return true;
}
else
return false;
}
/**
* Se obtiene el valor que mostrara el CellEditor segun el atributo que seleccione
*/
public Object getValue(Object element, String property) {
Object value=null;
if (element instanceof Padre) {
Padre padre = (Padre) element;
if (PROPIEDADES[TestContentProvider.NOMBRE_INDEX].equals(property))
{
value=padre.getNombre();
}
else if (PROPIEDADES[TestContentProvider.CASADO_INDEX].equals(property))
{
value=new Boolean(padre.isCasado());
}
}
return value;
}
/**
* Modificar el valor del atributo que corresponda en el modelo
*/
public void modify(Object element, String property, Object value) {
if (element instanceof Item) {
element = ((Item)element).getData();
}
if (element instanceof Padre) {
Padre padre = (Padre) element;
if (PROPIEDADES[TestContentProvider.NOMBRE_INDEX].equals(property))
{
padre.setNombre((String) value);
}
else if (PROPIEDADES[TestContentProvider.CASADO_INDEX].equals(property))
{
padre.setCasado((Boolean)value);
}
}
}
}

Como se puede ver es muy parecido a un content provider, primero se realiza un casting seguro hacia el objeto del modelo y despues se obtiene o modifica el valor del atributo que corresponda. El TestContentProvider sabe que tiene que refrescar el viewer porque se ha registrado como listener los elementos del modelo.

La clase TestView tambien cambiara, ahora deberemos asociar al viewer los cell editors, cell modifier y los viewer comparators, estos ultimos nos ayudaran a ordenar los elementos que muestra el viewer.


public class TestView extends ViewPart {
public static final String ID="com.testing.tutorial.rcp.views.TestView"; //el mismo id definido en el plugin
private TableViewer viewer;
//los nombre de columnas que representaran los atributos
public static final String[] COLUMNAS=new String[]{"NOMBRE","CASADO"};
public TestView() {
}
@Override
public void createPartControl(Composite parent) {
//obtenemos los datos
List input=Padre.getModel();
//creamos el viewer
this.viewer=new TableViewer(parent,SWT.NONE);
//definimos atributos de las tablas
this.viewer.getTable().setHeaderVisible(true);
this.viewer.getTable().setLinesVisible(true);
//creamos las columnas de la tabla
for(int i=0; i TableColumn column=new TableColumn(this.viewer.getTable(),SWT.NONE);
column.setText(TestView.COLUMNAS[i]);
column.pack();
//el indice de la columna
final int index=i;
column.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
//calculamos el orden (ascendente o descendente)
boolean asc=true;
if(viewer.getTable().getSortDirection()==SWT.UP)
asc=false;
//seteamos el comparador
viewer.setComparator(new TestViewerComparator(index,asc));
//modificamos la columna ordenada y el orden
viewer.getTable().setSortColumn(viewer.getTable().getColumn(index));
if(asc)
viewer.getTable().setSortDirection(SWT.UP);
else
viewer.getTable().setSortDirection(SWT.DOWN);
}
});
}
//asociamos nombres de las propiedades para cada columna
this.viewer.setColumnProperties(TestCellModifier.PROPIEDADES);
//creamos los celleditors
CellEditor[] editors=new CellEditor[TestCellModifier.PROPIEDADES.length];
/*
* Como se va a editar una propiedad
* tipo String, se utilizara un
* TextCellEditor
*/
editors[TestContentProvider.NOMBRE_INDEX]=new TextCellEditor(this.viewer.getTable());
/*
* Como se va a editar una propiedad
* tipo Boolean, se utilizara un
* CheckboxCellEditor
*/
editors[TestContentProvider.CASADO_INDEX]=new CheckboxCellEditor(this.viewer.getTable());
//asiganmos los celleditors al viewer
this.viewer.setCellEditors(editors);
//asignamos el cellmodifier al viewer
this.viewer.setCellModifier(new TestCellModifier());
//definimos el content provider
this.viewer.setContentProvider(new TestContentProvider());
//definimos el label provider
this.viewer.setLabelProvider(new TestLabelProvider());
//definimos los datos
this.viewer.setInput(input);
//defimos el provedor de seleccion
this.getSite().setSelectionProvider(this.viewer);
}
@Override
public void setFocus() {
}
}

Con respecto a la modificacion de celdas, antes habiamos dicho que el valor que se pasa como parametro a los metodos getValue() y modify() dependia de un “editor de celdas”, este editor deber ser una clase que hereda de la clase abstracta CellEditor. Segun que clase sea, el valor pasado puede ser:

TextCellEditor Pasara un String, que representara el String ingresado en el Text.
CheckboxCellEditor
Pasara un Boolean, que representara si el Checkbox esta seleccionado o no. Hay que hacer notar que no se presentara un widget Checkbox, si se desea, se tendra que representar graficamente, problemente utilizando una imagen.
ComboBoxCellEditor Pasara un Integer, que representara el indice escogido del Combo.
DialogCellEditor Pasara un Object, este cell editor utiliza un Dialog para obtener dicho objeto. Podremos heredar de este editor para poder pasar cualquier tipo de objeto.
ColorCellEditor Pasara una instancia de RGB que representa un color. Se debe hacer notar que este cell editor es un hijo de DialogCellEditor.

Por ultimo hay que notar el codigo que se encarga de ordenar los elementos por sus columnas. Basicamente cada vez que seleccionamos una columna creamos un nuevo ViewerComparator, que ordenara los datos basado en el orden anterior (si antes era ascendente, ahora se ordenara descendentemente) y en la columna seleccionada. El metodo compare() del ViewerComparator se encarga de realizar la comparacion.


public class TestViewerComparator extends ViewerComparator {
//columna que ordenada
private int columnIndex;
//si el orden sera ascendento o descentente
private boolean asc;
public TestViewerComparator(int columnIndex, boolean asc) {
this.columnIndex=columnIndex;
this.asc=asc;
}
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
//obtiene la respuesta por defecto
int r=super.compare(viewer, e1, e2);
if (e1 instanceof Padre && e2 instanceof Padre) {
Padre padre1=(Padre) e1;
Padre padre2=(Padre) e2;
//escoge el criterio indicado segun el indice
switch (this.columnIndex) {
case TestContentProvider.NOMBRE_INDEX:
r=padre1.getNombre().compareTo(padre2.getNombre());
break;
case TestContentProvider.CASADO_INDEX:
Boolean b1=new Boolean(padre1.isCasado());
Boolean b2=new Boolean(padre2.isCasado());
r=b1.compareTo(b2);
break;
default:
break;
}
}
if(this.asc)
return r;
else
return (r*-1);
}
}

De esta forma hemos generado un editor que permita modificar los atributos de una lista de objetos. Dejo un link hacia el codigo de ejemplo:

com.testing.tutorial.rcp_modifiers
com.testing.tutori...
Hosted by eSnips