2009-07-20 8 views
14

Tengo un JTable y necesito poder reordenar las columnas. Sin embargo, quiero que la primera columna no pueda ser reordenada. He utilizado el siguiente para permitir el reordenamiento:¿Cómo mantener una sola columna sin ser reordenada en una JTable?

table.getTableHeader().setReorderingAllowed(true); 

Las columnas ahora se pueden reordenar incluyendo la primera columna que no quiero. ¿Hay alguna forma de bloquear la primera columna?

He visto algunas soluciones que usan dos tablas con la primera columna en una tabla separada, pero tal vez hay una manera mejor/más simple.

+0

Recibí el 70% de su pregunta y comencé a escribir una respuesta diciéndole que use 2 tablas ... Luego terminé de leer su publicación. – jjnguy

+0

¿Entonces está diciendo que la primera columna debería ser independiente de las demás? –

+0

Habla de mover columnas enteras, izquierda y derecha. – jjnguy

Respuesta

3

Creo que debe anular el método columnMoved() en TableColumnModelListener. la clase TableColumnModelEvent tiene un método getFromIndex() que debería poder observar para determinar si se trata de su columna fija, y luego debería poder cancelar el evento.

Espero que ayude. A

+0

Esta parece ser la mejor manera de hacerlo. – jjnguy

+0

He estado jugando con eso, mi pregunta es (ya que soy un neófito de Java) ¿cómo puedo cancelar el evento? – Bearddo

+1

elige si pasar la llamada al método a la superclase. si no lo hace, no se aplicará a la mesa. – phatmanace

2

Primero debe definir una manera mejor y más simple. ¿Qué no te gusta del enfoque de 2 mesas?

No puede usar TableColumnModelListener porque el evento se desencadena "después" de que la columna ya se ha movido.

El código para arrastrar la columna se encuentra en BasicTableHeaderUI. De modo que podría tratar de sobrescribir el código allí, pero luego tendría que hacerlo para todas las LAF.

El código anterior invoca JTableHeader.getReorderingAllowed() en un evento presionado por el mouse para determinar si se permite el reordenamiento de columna. Supongo que podría anular ese método en JTableHeader y quizás usar la clase MouseInfo para obtener la ubicación actual del mouse y determinar si estaba sobre la primera columna y luego devolver falso. Pero ahora también necesitaría crear una JTable personalizada que use el encabezado de la tabla personalizada.

Por supuesto, con cualquiera de las sugerencias anteriores, es posible que pueda evitar que se mueva la primera columna. Pero no olvide que también debe evitar que la segunda columna se inserte antes de la primera columna. No creo que haya una respuesta corta y simple a la pregunta.

Fixed Column Table es mi versión de cómo esto se vería reflejado en dos tablas. ¿Es mejor? No lo sé, pero es simple ya que es solo una línea de código para usarlo.

+0

El "enfoque 2tables" que probablemente no le gusta porque eso hace un descanso del modelo de datos, supongo. Obligaría a tener datos separados en dos modelos, es seguro en el JTable, en el lado gráfico, pero es más molesto en el modelo. – Gnoupi

+0

No lo obliga a usar dos modelos. Solo el OP sabe cuáles son sus requisitos. No podemos leer su mente. – camickr

+0

No necesita dos modelos, puede hacer fácilmente el enfoque de dos tablas desde el mismo modelo de tabla. Este es el enfoque más simple, tanto desde el punto de vista conceptual como desde el punto de vista del mantenimiento. – codethulhu

1

Tuve el mismo problema, y ​​estaba buscando al respecto. Hasta ahora he encontrado dos formas de hacerlo.

  • El "si estaba reescribiendo yo mismo" método: Modificación de las clases base de Java.

TableColumn necesitaría una nueva propiedad, como el "resizingAllowed", necesitaría el "reorderingAllowed". partir de esto, las modificaciones se realizan en BasicTableHeaderUI:

Ya existe:

private static boolean canResize(TableColumn column, 
           JTableHeader header) { 
    return (column != null) && header.getResizingAllowed() 
          && column.getResizable(); 
} 

Se necesitaría demasiado:

private static boolean canMove(TableColumn column, 
           JTableHeader header) { 
    return (column != null) && header.getReorderingAllowed() 
           && column.getReorderable(); 
} 

(Tenga en cuenta que si usted no desea que la primera columna solo para no moverse, puede hacerlo sin cambiar las columnas de tabla:

private static boolean canMove(TableColumn column, 
           JTableHeader header) { 
    return (column != null) && header.getReorderingAllowed() 
          && header.getColumnModel().getColumnIndex(column.getIdentifier()) != 0; 
} 

)

Después, dos lugares para modificar en el MouseInputListener:

  • en el mousePressed, llamando a la canMove() en lugar de la header.getReorderingAllowed(). Esto asegura que no se moverá una columna que no se debe mover.
  • Pero esto no es suficiente, debemos evitar que las columnas inmóviles se muevan durante el arrastre de otra. Es necesario cambiar la mouseDragged, también, cuando se está haciendo el "newColumnIndex":

    si (0 < newColumnIndex & & newColumnIndex < cm.getColumnCount())

Es necesario añadir la condición si este nuevo índice se puede mover, por ejemplo, utilizando el método "canMove()". De esta manera, cuando arrastres una columna a esta inmóvil, aún la arrastrarás, pero no la intercambiará.

Tenga en cuenta que este método requerirá que configure explícitamente la interfaz de usuario para el JTableHeader utilizado para su JTable, que en realidad no es ideal. Pero este es el más adecuado, ya que trata el problema en el lugar donde se supone que debe hacerlo.


  • El "Vamos a tratar de bloquear el comportamiento normal con lo que en realidad tenemos" método: No modificar la interfaz de usuario, este método de enfoque en la JTableHeader para bloquear los comandos realizados por la interfaz de usuario.

En primer lugar, para bloquear arrastrando la primera columna, necesitamos una subclase de JTableHeader, con este método reemplazado:

@Override 
public void setDraggedColumn(TableColumn pAColumn) 
{ 
    int lIndex = -1; 
    if (pAColumn != null) 
     lIndex = getColumnModel().getColumnIndex(pAColumn.getIdentifier()); 
    if (lIndex != 0) 
     super.setDraggedColumn(pAColumn); 
} 

Esto evitará que un usuario de arrastrar la primera columna. Pero como se describió anteriormente, esta es solo una parte del problema, debemos evitar que otra columna arrastrada se intercambie con esta primera.

Hasta ahora, no tengo un método correcto para esto. Me trató como subclase la TableColumnModel, y reemplazando el método moveColumn():

@Override 
public void moveColumn(int pColumnIndex, int pNewIndex) 
{ 
    //Move only if the first column is not concerned 
    if (pColumnIndex =! 0 && pNewIndex != 0) 
     super.moveColumn(pColumnIndex, pNewIndex); 
} 

Pero esto no funcionará, ya que la interfaz de usuario se actualizará todos modos la posición del ratón en el método mouseDragged, tendrá un salto desde su columna arrastrado a otro lugar

Todavía estoy buscando, y me pregunto si alguien tiene proposiciones sobre esta parte.

+0

Buen trabajo. Por favor, hágame saber su opinión sobre mi sugerencia y muchas gracias por sus explicaciones detalladas. – gouessej

0

He utilizado el "El" Vamos a tratar de bloquear el comportamiento normal con lo que realmente tenemos enfoque de "método". Gnoupi dijo que no resolvió la segunda parte del problema. Aquí está la solución solo para Windows XP L & F:

  1. copia la clase XPStyle a ti mismo.
  2. extender WindowsTableHeaderUI. Eche un vistazo al source code.
  3. lo utilizan: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

Gracias a Gnoupi por los esfuerzos.

7

Esta es la solución que he usado para evitar que la primera columna de ser reordenado

private int columnValue = -1; 
private int columnNewValue = -1; 


tblResults.getColumnModel().addColumnModelListener(new TableColumnModelListener() 
{ 
    public void columnAdded(TableColumnModelEvent e) {} 

    public void columnMarginChanged(ChangeEvent e) {} 

    public void columnMoved(TableColumnModelEvent e) 
    { 
     if (columnValue == -1) 
      columnValue = e.getFromIndex(); 

     columnNewValue = e.getToIndex(); 
    } 

    public void columnRemoved(TableColumnModelEvent e) {} 

    public void columnSelectionChanged(ListSelectionEvent e) {} 
}); 

tblResults.getTableHeader().addMouseListener(new MouseAdapter() 
{ 
    @Override 
    public void mouseReleased(MouseEvent e) 
    { 
     if (columnValue != -1 && (columnValue == 0 || columnNewValue == 0)) 
     tblResults.moveColumn(columnNewValue, columnValue); 

     columnValue = -1; 
     columnNewValue = -1; 
    } 
}); 

Cheers,

5

Casi 4 años después, todavía no hay una solución óptima a la vista en cualquier lugar.

Otro enfoque óptimo para evitar arrastre de la primera columna (columnas y otros más de la primera) es interceptar los MouseEvents antes de la mouseInputListener instalado por el uidelegate puede manejarlos (similar to a recent QA).

Los colaboradores

  • un MouseMotionListener costumbre que los delegados todos los eventos a la instalada originalmente, excepto la arrastró si se llevaría a otra columna por encima de la primera
  • reemplazar el original con la costumbre
  • actualización el reemplazo siempre que se cambie el LAF (porque el original está controlado por el ui). Esto requiere de subclases de JTableHeader y hacer el cableado en updateUI

El MouseInputListener personalizado:

/** 
* A delegating MouseInputListener to be installed instead of 
* the one registered by the ui-delegate. 
* 
* It's implemented to prevent dragging the first column or any other 
* column over the first. 
*/ 
public static class DragHook implements MouseInputListener { 

    private JTableHeader header; 
    private MouseListener mouseDelegate; 
    private MouseMotionListener mouseMotionDelegate; 
    private int maxX; 

    public DragHook(JTableHeader header) { 
     this.header = header; 
     installHook(); 
    } 

    /** 
    * Implemented to do some tweaks/bookkeeping before/after 
    * passing the event to the original 
    * 
    * - temporarily disallow reordering if hit on first column 
    * - calculate the max mouseX that's allowable in dragging to the left 
    * 
    */ 
    @Override 
    public void mousePressed(MouseEvent e) { 
     int index = header.columnAtPoint(e.getPoint()); 
     boolean reorderingAllowed = header.getReorderingAllowed(); 
     if (index == 0) { 
      // temporarily disable re-ordering 
      header.setReorderingAllowed(false); 
     } 
     mouseDelegate.mousePressed(e); 
     header.setReorderingAllowed(reorderingAllowed); 
     if (header.getDraggedColumn() != null) { 
      Rectangle r = header.getHeaderRect(index); 
      maxX = header.getColumnModel().getColumn(0).getWidth() 
        + e.getX() - r.x -1; 
     } 
    } 

    /** 
    * Implemented to pass the event to the original only if the 
    * mouseX doesn't lead to dragging the column over the first. 
    */ 
    @Override 
    public void mouseDragged(MouseEvent e) { 
     TableColumn dragged = header.getDraggedColumn(); 
     int index = getViewIndexForColumn(header.getColumnModel(), dragged); 
     // dragged column is at second position, allow only drags to the right 
     if (index == 1) { 
      if (e.getX() < maxX) return; 
     } 
     mouseMotionDelegate.mouseDragged(e); 
    } 

    //-------- delegating-only methods 

    @Override 
    public void mouseReleased(MouseEvent e) { 
     mouseDelegate.mouseReleased(e); 
    } 

    @Override 
    public void mouseClicked(MouseEvent e) { 
     mouseDelegate.mouseClicked(e); 
    } 

    @Override 
    public void mouseEntered(MouseEvent e) { 
     mouseDelegate.mouseEntered(e); 
    } 

    @Override 
    public void mouseExited(MouseEvent e) { 
     mouseDelegate.mouseExited(e); 
    } 

    @Override 
    public void mouseMoved(MouseEvent e) { 
     mouseMotionDelegate.mouseMoved(e); 
    } 

    //------------ un-/install listeners 

    protected void installHook() { 
     installMouseHook(); 
     installMouseMotionHook(); 
    } 

    protected void installMouseMotionHook() { 
     MouseMotionListener[] listeners = header.getMouseMotionListeners(); 
     for (int i = 0; i < listeners.length; i++) { 
      MouseMotionListener l = listeners[i]; 
      if (l.getClass().getName().contains("TableHeaderUI")) { 
       this.mouseMotionDelegate = l; 
       listeners[i] = this; 
      } 
      header.removeMouseMotionListener(l); 
     } 
     for (MouseMotionListener l : listeners) { 
      header.addMouseMotionListener(l); 
     } 
    } 

    protected void installMouseHook() { 
     MouseListener[] listeners = header.getMouseListeners(); 
     for (int i = 0; i < listeners.length; i++) { 
      MouseListener l = listeners[i]; 
      if (l.getClass().getName().contains("TableHeaderUI")) { 
       this.mouseDelegate = l; 
       listeners[i] = this; 
      } 
      header.removeMouseListener(l); 
     } 
     for (MouseListener l : listeners) { 
      header.addMouseListener(l); 
     } 
    } 

    public void uninstallHook() { 
     uninstallMouseHook(); 
     uninstallMouseMotionHook(); 
    } 

    protected void uninstallMouseMotionHook() { 
     MouseMotionListener[] listeners = header.getMouseMotionListeners(); 
     for (int i = 0; i < listeners.length; i++) { 
      MouseMotionListener l = listeners[i]; 
      if (l == this) { 
       listeners[i] = mouseMotionDelegate; 
      } 
      header.removeMouseMotionListener(l); 
     } 
     for (MouseMotionListener l : listeners) { 
      header.addMouseMotionListener(l); 
     } 
    } 

    protected void uninstallMouseHook() { 
     MouseListener[] listeners = header.getMouseListeners(); 
     for (int i = 0; i < listeners.length; i++) { 
      MouseListener l = listeners[i]; 
      if (l == this) { 
       listeners[i] = mouseDelegate; 
      } 
      header.removeMouseListener(l); 
     } 
     for (MouseListener l : listeners) { 
      header.addMouseListener(l); 
     } 
    } 

} 

uso que sobrevive conmutación de LAF, fi:

JTable table = new JTable(new AncientSwingTeam()) { 

    @Override 
    protected JTableHeader createDefaultTableHeader() { 
     JTableHeader header = new JTableHeader(getColumnModel()) { 
      DragHook hook; 

      @Override 
      public void updateUI() { 
       if (hook != null) { 
        hook.uninstallHook(); 
        hook = null; 
       } 
       super.updateUI(); 
       hook = new DragHook(this); 
      } 

     }; 
     return header; 
    } 

}; 
+0

+1 .................................... – mKorbel

0

Al principio, he utilizado la La última sugerencia de Gnoupi consistía en subclasificar el modelo TableColumn y reemplazar moveColumn, pero todavía había algunos saltos molestos.

Esta es "mi" solución completamente funcional y probada sin ningún salto desagradable, se basa principalmente en las sugerencias de StanislavKo y Cleopatra. He añadido un mecanismo más complicado de revertir el movimiento no deseado al soltar el botón del ratón:

table.getTableHeader().setUI(new WindowsTableHeaderUI() { 
     @Override 
     protected MouseInputListener createMouseInputListener() { 
      return new BasicTableHeaderUI.MouseInputHandler() { 

       @Override 
       public void mouseDragged(MouseEvent e) { 
        if (header.isEnabled() && header.getReorderingAllowed() && header.getDraggedColumn() != null && header.getDraggedColumn().getModelIndex() == frozenColumnModelIndex) { 
         header.setDraggedDistance(0); 
         header.setDraggedColumn(null); 
         return; 
        } 
        super.mouseDragged(e); 
       } 

       @Override 
       public void mouseReleased(MouseEvent e) { 
        if (header.isEnabled() && header.getReorderingAllowed() && header.getDraggedColumn() != null && 
         0 <= illegalTableColumnMoveFromIndex && illegalTableColumnMoveFromIndex < header.getTable().getColumnModel().getColumnCount()) { 
         header.setDraggedDistance(0); 
         header.setDraggedColumn(null); 
         header.getTable().getColumnModel().moveColumn(illegalTableColumnMoveToIndex, illegalTableColumnMoveFromIndex); 
         illegalTableColumnMoveFromIndex = -1; 
         illegalTableColumnMoveToIndex = -1; 
         return; 
        } 
        super.mouseReleased(e); 
       } 
      }; 
     } 
    }); 
    table.getColumnModel().addColumnModelListener(new TableColumnModelListener() { 
     @Override 
     public void columnAdded(TableColumnModelEvent e) { 
     } 

     @Override 
     public void columnRemoved(TableColumnModelEvent e) { 
     } 

     @Override 
     public void columnMoved(TableColumnModelEvent e) { 
      if (e.getFromIndex() != e.getToIndex() && table.getColumnModel().getColumn(e.getFromIndex()).getModelIndex() == frozenColumnModelIndex) { 
       illegalTableColumnMoveFromIndex = e.getFromIndex(); 
       illegalTableColumnMoveToIndex = e.getToIndex(); 
      } else { 
       illegalTableColumnMoveFromIndex = -1; 
       illegalTableColumnMoveToIndex = -1; 
      } 
     } 

     @Override 
     public void columnMarginChanged(ChangeEvent e) { 
     } 

     @Override 
     public void columnSelectionChanged(ListSelectionEvent e) { 
     } 
    }); 

Tenga en cuenta que el último movimiento válido es aceptado en lugar de revertir completamente la resistencia de la columna.

frozenColumnModelIndex es el índice de la columna "inmovilizada" en el modelo de tabla.

illegalTableColumnMoveFromIndex es el índice de la columna desde donde se movió cuando se detectó el movimiento ilegal más reciente.

illegalTableColumnMoveToIndex es el índice de la columna donde se movió cuando se detectó el último movimiento ilegal.

El código dentro de mouseDragged es suficiente para evitar que la columna congelada se arrastre, el resto permite evitar que otra columna se arrastre a la columna inmovilizada.

Funciona como es bajo Microsoft Windows como extiendo WindowsTableHeaderUI sino más bien utilizar la API de reflexión para establecer el oyente entrada del ratón de la interfaz de usuario de cabecera de la tabla, llame uninstallerListeners() y finalmente llamo header.addMouseListener (mouseInputListener) y header.addMouseMotionListener (mouseInputListener) para dirigir mi solución multiplataforma sin hacer ninguna suposición sobre el nombre de la clase para cada UI de encabezado de tabla.

Admito que podría ser un poco menos robusto que la solución de Kleopatra. Les agradezco a todos por su ayuda, estoy muy agradecido y estoy muy contento de ver que el trabajo colaborativo simplemente funciona :)

0

Voy a poner la columna de nuevo después de que se complete el traslado. Entonces algo así como

@Override 
public void moveColumn(int from, int to) { 
     super.moveColumn(from, to); 
     if (from == 0 || to == 0) { 
      super.moveColumn(to, from); 
     } 
} 
Cuestiones relacionadas