How to keep a single column from being reordered in a JTable?

前端 未结 8 1790
时光说笑
时光说笑 2020-11-30 09:17

I have a JTable and I need to be able to reorder the columns. However I want the first column to not be able to be re-ordered. I used the following to enable re

相关标签:
8条回答
  • 2020-11-30 09:23

    I will just put the column back after the move is complete. So something like.

    @Override
    public void moveColumn(int from, int to) {
          super.moveColumn(from, to);
          if (from == 0 || to == 0) {
               super.moveColumn(to, from);
          }
    }
    
    0 讨论(0)
  • 2020-11-30 09:28

    This is the solution that I used to prevent the 1st column from being re-ordered

    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,

    0 讨论(0)
  • 2020-11-30 09:30

    I have used the "The 'Let's try to block the normal behavior with what we actually have' method" approach. Gnoupi said that he did not solve the second part of the problem. Here is the solution for just Windows XP L&F:

    1. copy XPStyle class to yourself.
    2. extend WindowsTableHeaderUI. Take a look at the source code.
    3. use it: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI());

    Thanks to Gnoupi for the efforts.

    0 讨论(0)
  • 2020-11-30 09:31

    I had the same issue, and I was searching about it. So far I found two ways of doing that.

    • The "if I was rewriting it myself" method : Modifying the base classes from Java.

    TableColumn would need a new property, like the "resizingAllowed", it would need the "reorderingAllowed". From this, the modifications take place in BasicTableHeaderUI :

    There is already :

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

    It would need too :

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

    (Note that if you don't want the first column only to not move, you can do without changing the TableColumns :

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

    )

    After, two places to modify in the MouseInputListener :

    • in the mousePressed, calling the canMove() instead of the header.getReorderingAllowed(). This ensures that a column which shouldn't be moved, won't be.
    • But this is not enough, we need to prevent the immobile columns from being moved during dragging another one. You need to change the mouseDragged, too, when it is getting the "newColumnIndex" :

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

    You need to add the condition if this new index can be moved, for example using the "canMove()" method. This way, when you will drag a column to this immobile one, you will still drag it, but it won't swap them.

    Note that this method would require you to explicitly set the UI for the JTableHeader used for your JTable, which is not really ideal. But this is the most adapted though, as it deals with the problem on the place it is supposed to.


    • The "Let's try to block the normal behavior with what we actually have" method : Not modifying the UI, this method focus on the JTableHeader to block the commands made by the UI.

    First, to block dragging the first column, we need a subclass from JTableHeader, with this overridden method :

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

    This will prevent a user from dragging the first column. But like described earlier, this is only one part of the problem, we need to prevent another dragged column from swapping with this first one.

    So far, I don't have a correct method for this. I tried by subclassing the TableColumnModel, and overriding the moveColumn() method :

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

    But this won't work, as the UI will update anyway the mouse position in the mouseDragged method, you will have a jump from your dragged column to another place.

    So I'm still searching, and wonder if someone has propositions concerning this part.

    0 讨论(0)
  • 2020-11-30 09:38

    I think that you need to override the columnMoved() method in TableColumnModelListener. the TableColumnModelEvent class has a getFromIndex() method that you should be able to look at to determine if it's your fixed column, and then you should be able to cancel the event.

    Hope that helps. A

    0 讨论(0)
  • 2020-11-30 09:45

    Nearly 4 years later, there's still no optimal solution in sight anywhere.

    Yet another suboptimal approach to prevent dragging of the first column (and other columns over the first) is to intercept the mouseEvents before the mouseInputListener installed by the uidelegate can handle them (similar to a recent QA).

    The collaborators

    • a custom MouseMotionListener which delegates all events to the originally installed, except the dragged if it would lead to another column above the first
    • replace the original with the custom
    • update the replacement whenever the LAF is changed (because the original is controlled by the ui). This requires subclassing of JTableHeader and do the wiring in updateUI

    The custom MouseInputListener:

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

    Usage which survives switching of LAF, f.i.:

    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 讨论(0)
提交回复
热议问题