Come mantenere una singola colonna da essere riordinata in una tabella?

Ho un JTable e devo essere in grado di riordinare le colonne. Comunque voglio che la prima colonna non possa essere riordinata. Ho usato quanto segue per abilitare il riordino:

 table.getTableHeader().setReorderingAllowed(true); 

Ora è ansible riordinare le colonne includendo la prima colonna che non desidero. C’è un modo per bloccare la prima colonna?

Ho visto alcune soluzioni che usano due tabelle con la prima colonna in una tabella separata, ma forse c’è un modo migliore / più semplice.

Penso che sia necessario sovrascrivere il metodo columnMoved() in TableColumnModelListener . la class TableColumnModelEvent ha un metodo getFromIndex() che dovresti essere in grado di esaminare per determinare se è la tua colonna fissa, e quindi dovresti riuscire a cancellare l’evento.

Spero possa aiutare. UN

Questa è la soluzione che ho usato per evitare di riordinare la prima colonna

 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; } }); 

Saluti,

Quasi 4 anni dopo, non esiste ancora una soluzione ottimale in vista.

Un altro approccio non ottimale per evitare il trascinamento della prima colonna (e di altre colonne rispetto al primo) consiste nell’intercettare il mouseEvents prima che il mouseInputListener installato da uidelegate possa gestirli ( simile a un recente QA ).

I collaboratori

  • un mouseMotionListener personalizzato che delega tutti gli eventi all’installazione originariamente installata, ad eccezione del trascinato se portasse a un’altra colonna sopra la prima
  • sostituire l’originale con l’abitudine
  • aggiornare la sostituzione ogni volta che viene modificato il LAF (poiché l’originale è controllato dall’ii). Ciò richiede la sottoclass di JTableHeader e il cablaggio in updateUI

Il mouseInputListener personalizzato:

 /** * 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() - rx -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 che sopravvive al passaggio di 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; } }; 

Per prima cosa devi definire un modo migliore e più semplice. Cosa non ti piace dell’approccio a 2 tavoli?

Non è ansible utilizzare un TableColumnModelListener, poiché l’evento viene generato “dopo” la colonna è già stata spostata.

Il codice per il trascinamento della colonna si trova in BasicTableHeaderUI. Quindi potresti provare a sovrascrivere il codice lì, ma poi avresti bisogno di farlo per tutti i LAF.

Il codice precedente richiama JTableHeader.getReorderingAllowed () su un evento mousePressed per determinare se è consentito il riordino della colonna. Immagino che potresti sovrascrivere quel metodo in JTableHeader e forse usare la class MouseInfo per ottenere la posizione corrente del mouse per determinare se era sopra la prima colonna e quindi restituire false. Ma ora dovresti anche creare un JTable personalizzato che usi l’intestazione della tabella personalizzata.

Naturalmente con uno dei suggerimenti sopra riportati potresti essere in grado di impedire che la prima colonna venga spostata. Ma non dimenticare che devi anche impedire che la seconda colonna venga inserita prima della prima colonna. Non credo che ci sia una breve risposta semplice alla domanda.

Fixed Column Table è la mia versione di come questo sarebbe stato implementato con due tabelle. È meglio? Non lo so, ma è semplice dal momento che è solo una singola riga di codice per usarlo.

Ho avuto lo stesso problema e lo stavo cercando. Finora ho trovato due modi per farlo.

  • Il metodo “se dovessi riscriverlo da solo” : Modifica delle classi base da Java.

TableColumn avrebbe bisogno di una nuova proprietà, come “resizingAllowed”, avrebbe bisogno del “reorderingAllowed”. Da questo, le modifiche avvengono in BasicTableHeaderUI :

C’è già :

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

Ne avrebbe bisogno anche:

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

(Si noti che se non si desidera che la prima colonna non si sposti, è ansible fare a meno di modificare TableColumns:

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

)

Dopo, due posizioni da modificare in MouseInputListener :

  • nel mousePressed , chiamando canMove() invece di header.getReorderingAllowed() . Questo assicura che una colonna che non dovrebbe essere spostata, non lo sarà.
  • Ma questo non è abbastanza, abbiamo bisogno di impedire che le colonne immobili vengano spostate durante il trascinamento di un altro. È necessario modificare anche il mouseDragged quando riceve “newColumnIndex”:

    if (0

È necessario aggiungere la condizione se questo nuovo indice può essere spostato, ad esempio utilizzando il metodo “canMove ()”. In questo modo, quando trascini una colonna su questa immobile, la trascinerai comunque, ma non la scambierà.

Tieni presente che questo metodo richiede di impostare in modo esplicito l’interfaccia utente per JTableHeader utilizzato per JTable, che non è proprio l’ideale. Ma questo è il più adatto, visto che si occupa del problema sul posto che dovrebbe.


  • Il metodo “Proviamo a bloccare il comportamento normale con quello che effettivamente abbiamo” : non modificando l’interfaccia utente, questo metodo si concentra su JTableHeader per bloccare i comandi eseguiti dall’interfaccia utente.

Per prima cosa, per bloccare il trascinamento della prima colonna, abbiamo bisogno di una sottoclass da JTableHeader, con questo metodo sovrascritto:

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

Ciò impedirà a un utente di trascinare la prima colonna. Ma come descritto in precedenza, questa è solo una parte del problema, dobbiamo evitare che un’altra colonna trascinata si sposti con questa prima.

Finora, non ho un metodo corretto per questo. Ho provato sottoclassi il TableColumnModel e sovrascrivendo il metodo 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); } 

Ma questo non funzionerà, poiché l’interfaccia utente aggiornerà comunque la posizione del mouse nel metodo mouseDragged , si avrà un salto dalla colonna trascinata in un’altra posizione.

Quindi sto ancora cercando e mi chiedo se qualcuno ha delle proposte riguardo questa parte.

Ho usato il “The” Proviamo a bloccare il comportamento normale con quello che effettivamente abbiamo un approccio “metodo”. Gnoupi ha detto che non ha risolto la seconda parte del problema. Ecco la soluzione per solo Windows XP L & F:

  1. copia la class XPStyle a te stesso.
  2. estendere WindowsTableHeaderUI . Dai un’occhiata al codice sorgente .
  3. getTableHeader().setUI(new TreeTableWindowsTableHeaderUI()); : getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

Grazie a Gnoupi per gli sforzi.

All’inizio, ho usato l’ultimo suggerimento di Gnoupi consistente nella sottoclass di TableColumnModel e nella sovrascrittura di moveColumn, ma c’erano ancora alcuni salti fastidiosi.

Questa è la “mia” soluzione completamente funzionante e testata, senza brutti salti, che si basa principalmente sui suggerimenti di StanislavKo e kleopatra. Ho aggiunto un meccanismo più complicato per annullare la mossa indesiderata quando rilasciando il pulsante del mouse:

 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) { } }); 

Si noti che l'ultima mossa valida viene accettata invece di annullare completamente il trascinamento della colonna.

frozenColumnModelIndex è l'indice della colonna "congelata" nel modello di tabella.

illegalTableColumnMoveFromIndex è l'indice della colonna da cui è stato spostato quando è stata rilevata l'ultima mossa illegale.

illegalTableColumnMoveToIndex è l'indice della colonna in cui è stato spostato quando è stata rilevata l'ultima mossa illegale.

Il codice all'interno di mouseDragged è sufficiente per impedire il trascinamento della colonna congelata, il resto consente di impedire che un'altra colonna venga trascinata nella colonna congelata.

Funziona come in Microsoft Windows mentre estendo WindowsTableHeaderUI, ma piuttosto utilizzo l'API reflection per impostare il listener di input del mouse dell'interfaccia utente dell'intestazione della tabella, chiamare uninstallerListeners () e infine chiamare header.addMouseListener (mouseInputListener) e header.addMouseMotionListener (mouseInputListener) in per guidare la mia soluzione multipiattaforma senza fare alcuna ipotesi sul nome della class per ogni UI di intestazione della tabella.

Ammetto che potrebbe essere un po 'meno robusto della soluzione di kleopatra. Vi ringrazio tutti per il vostro aiuto, sono davvero grato e sono davvero felice di vedere che il lavoro collaborativo funziona 🙂

Mi limiterò a rimettere la colonna dopo che la mossa è stata completata. Quindi qualcosa di simile.

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