Frage So beschleunigen Sie die Scroll-Geschwindigkeit beim Ziehen eines JTree-Knotens innerhalb von JScrollPane


Ich habe ein JTree in einem JScrollPane. Das JTree ist ziemlich lang, also dauert es eine Weile, um einen Knoten von oben nach unten zu ziehen. Beim Ziehen eines Knotens wird der JScrollPane scrollt, aber nicht annähernd so schnell wie es mit dem Mausrad scrollt. Implementieren setUnitIncrement wie in der ausgewählten Antwort vorgeschlagen Hier erhöht die Scrollgeschwindigkeit des Mausrads noch schneller, ändert jedoch nicht die Geschwindigkeit des Ziehens eines Knotens. Das Gleiche gilt für die Implementierung setBlockInkrement. Die Geschwindigkeit des Bildlaufs beim Ziehen eines Knotens ist ungefähr so, als würde ich den Pfeil nach oben oder unten halten und den Pfeil durchqueren JTree auf diese Weise.

Wie kann ich die Geschwindigkeit beim Ziehen eines Knotens beschleunigen?

UPDATE 1:

Hier ist ein SSCCE wie angefordert. Den größten Teil dieses Codes habe ich abgezockt Hier weil es gut ist, das Problem zu illustrieren, das ich habe. Ziehen Sie einfach einen Knoten auf einen Teil der Struktur, der im Bildlauffenster nicht sichtbar ist, und Sie werden sehen, wie langsam das Scrollen ist. Das möchte ich beschleunigen.

package example;

import java.awt.datatransfer.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;

public class Example {

    private JScrollPane getContent() {
        ArrayList<String> arrayList = new ArrayList<String>();
        for( int i = 0; i < 3000; i++ ) {
            arrayList.add( String.format( "Node %d", i ) );
        }
        DefaultMutableTreeNode root = new DefaultMutableTreeNode( "Root" );
        for( String s : arrayList ) {
            root.add( new DefaultMutableTreeNode( s ) );
        }
        JTree tree = new JTree( root );
        tree.setDragEnabled( true );
        tree.setDropMode( DropMode.ON_OR_INSERT );
        tree.setTransferHandler( new TreeTransferHandler() );
        tree.getSelectionModel().setSelectionMode( TreeSelectionModel.CONTIGUOUS_TREE_SELECTION );
        expandTree( tree );
        return new JScrollPane( tree );
    }

    private void expandTree( JTree tree ) {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode)tree.getModel().getRoot();
        Enumeration e = root.breadthFirstEnumeration();
        while( e.hasMoreElements() ) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.nextElement();
            if( node.isLeaf() ) {
                continue;
            }
            int row = tree.getRowForPath( new TreePath( node.getPath() ) );
            tree.expandRow( row );
        }
    }

    public static void main( String[] args ) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        f.add( new Example().getContent() );
        f.setSize( 400, 400 );
        f.setLocation( 200, 200 );
        f.setVisible( true );
    }
}

class TreeTransferHandler extends TransferHandler {

    DataFlavor nodesFlavor;
    DataFlavor[] flavors = new DataFlavor[1];
    DefaultMutableTreeNode[] nodesToRemove;

    public TreeTransferHandler() {
        try {
            String mimeType = DataFlavor.javaJVMLocalObjectMimeType
                    + ";class=\""
                    + javax.swing.tree.DefaultMutableTreeNode[].class.getName()
                    + "\"";
            nodesFlavor = new DataFlavor( mimeType );
            flavors[0] = nodesFlavor;
        } catch( ClassNotFoundException e ) {
            System.out.println( "ClassNotFound: " + e.getMessage() );
        }
    }

    public boolean canImport( TransferHandler.TransferSupport support ) {
        if( !support.isDrop() ) {
            return false;
        }
        support.setShowDropLocation( true );
        if( !support.isDataFlavorSupported( nodesFlavor ) ) {
            return false;
        }
        // Do not allow a drop on the drag source selections.
        JTree.DropLocation dl = (JTree.DropLocation)support.getDropLocation();
        JTree tree = (JTree)support.getComponent();
        int dropRow = tree.getRowForPath( dl.getPath() );
        int[] selRows = tree.getSelectionRows();
        for( int i = 0; i < selRows.length; i++ ) {
            if( selRows[i] == dropRow ) {
                return false;
            }
        }
        // Do not allow MOVE-action drops if a non-leaf node is
        // selected unless all of its children are also selected.
        int action = support.getDropAction();
        if( action == MOVE ) {
            return haveCompleteNode( tree );
        }
        // Do not allow a non-leaf node to be copied to a level
        // which is less than its source level.
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode target = (DefaultMutableTreeNode)dest.getLastPathComponent();
        TreePath path = tree.getPathForRow( selRows[0] );
        DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode)path.getLastPathComponent();
        if( firstNode.getChildCount() > 0 && target.getLevel() < firstNode.getLevel() ) {
            return false;
        }
        return true;
    }

    private boolean haveCompleteNode( JTree tree ) {
        int[] selRows = tree.getSelectionRows();
        TreePath path = tree.getPathForRow( selRows[0] );
        DefaultMutableTreeNode first = (DefaultMutableTreeNode)path.getLastPathComponent();
        int childCount = first.getChildCount();
        // first has children and no children are selected.
        if( childCount > 0 && selRows.length == 1 ) {
            return false;
        }
        // first may have children.
        for( int i = 1; i < selRows.length; i++ ) {
            path = tree.getPathForRow( selRows[i] );
            DefaultMutableTreeNode next = (DefaultMutableTreeNode)path.getLastPathComponent();
            if( first.isNodeChild( next ) ) {
                // Found a child of first.
                if( childCount > selRows.length - 1 ) {
                    // Not all children of first are selected.
                    return false;
                }
            }
        }
        return true;
    }

    protected Transferable createTransferable( JComponent c ) {
        JTree tree = (JTree)c;
        TreePath[] paths = tree.getSelectionPaths();
        if( paths != null ) {
            // Make up a node array of copies for transfer and
            // another for/of the nodes that will be removed in
            // exportDone after a successful drop.
            List<DefaultMutableTreeNode> copies = new ArrayList<DefaultMutableTreeNode>();
            List<DefaultMutableTreeNode> toRemove = new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)paths[0].getLastPathComponent();
            DefaultMutableTreeNode copy = copy( node );
            copies.add( copy );
            toRemove.add( node );
            for( int i = 1; i < paths.length; i++ ) {
                DefaultMutableTreeNode next = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
                // Do not allow higher level nodes to be added to list.
                if( next.getLevel() < node.getLevel() ) {
                    break;
                } else if( next.getLevel() > node.getLevel() ) {  // child node
                    copy.add( copy( next ) );
                    // node already contains child
                } else {                                        // sibling
                    copies.add( copy( next ) );
                    toRemove.add( next );
                }
            }
            DefaultMutableTreeNode[] nodes = copies.toArray( new DefaultMutableTreeNode[copies.size()] );
            nodesToRemove = toRemove.toArray( new DefaultMutableTreeNode[toRemove.size()] );
            return new NodesTransferable( nodes );
        }
        return null;
    }

    /**
     * Defensive copy used in createTransferable.
     */
    private DefaultMutableTreeNode copy( TreeNode node ) {
        return new DefaultMutableTreeNode( node );
    }

    protected void exportDone( JComponent source, Transferable data, int action ) {
        if( ( action & MOVE ) == MOVE ) {
            JTree tree = (JTree)source;
            DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
            // Remove nodes saved in nodesToRemove in createTransferable.
            for( int i = 0; i < nodesToRemove.length; i++ ) {
                model.removeNodeFromParent( nodesToRemove[i] );
            }
        }
    }

    public int getSourceActions( JComponent c ) {
        return COPY_OR_MOVE;
    }

    public boolean importData( TransferHandler.TransferSupport support ) {
        if( !canImport( support ) ) {
            return false;
        }
        // Extract transfer data.
        DefaultMutableTreeNode[] nodes = null;
        try {
            Transferable t = support.getTransferable();
            nodes = (DefaultMutableTreeNode[])t.getTransferData( nodesFlavor );
        } catch( UnsupportedFlavorException ufe ) {
            System.out.println( "UnsupportedFlavor: " + ufe.getMessage() );
        } catch( java.io.IOException ioe ) {
            System.out.println( "I/O error: " + ioe.getMessage() );
        }
        // Get drop location info.
        JTree.DropLocation dl = (JTree.DropLocation)support.getDropLocation();
        int childIndex = dl.getChildIndex();
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode parent = (DefaultMutableTreeNode)dest.getLastPathComponent();
        JTree tree = (JTree)support.getComponent();
        DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
        // Configure for drop mode.
        int index = childIndex;    // DropMode.INSERT
        if( childIndex == -1 ) {     // DropMode.ON
            index = parent.getChildCount();
        }
        // Add data to model.
        for( int i = 0; i < nodes.length; i++ ) {
            model.insertNodeInto( nodes[i], parent, index++ );
        }
        return true;
    }

    public String toString() {
        return getClass().getName();
    }

    public class NodesTransferable implements Transferable {

        DefaultMutableTreeNode[] nodes;

        public NodesTransferable( DefaultMutableTreeNode[] nodes ) {
            this.nodes = nodes;
        }

        public Object getTransferData( DataFlavor flavor )
                throws UnsupportedFlavorException {
            if( !isDataFlavorSupported( flavor ) ) {
                throw new UnsupportedFlavorException( flavor );
            }
            return nodes;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported( DataFlavor flavor ) {
            return nodesFlavor.equals( flavor );
        }
    }
}

ERFOLG!

Ich war schließlich in der Lage, das Scrollen beim Ziehen zu beschleunigen. Tatsächlich war ich auch in der Lage, die Scrollgeschwindigkeit variabel zu machen, basierend darauf, wie nahe der Cursor an der Ober- oder Unterseite des Baumes ist. Am Ende war es viel einfacher als ich es gemacht habe. Hatte nicht einmal einen Zuhörer. Just diff diese beiden Codebeispiele, um zu sehen, was ich hinzugefügt habe.

package example;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;

public class Example {

    private JScrollPane getContent() {
        ArrayList<String> arrayList = new ArrayList<String>();
        for( int i = 0; i < 3000; i++ ) {
            arrayList.add( String.format( "Node %d", i ) );
        }
        DefaultMutableTreeNode root = new DefaultMutableTreeNode( "Root" );
        for( String s : arrayList ) {
            root.add( new DefaultMutableTreeNode( s ) );
        }
        JTree tree = new JTree( root );
        tree.setDragEnabled( true );
        tree.setDropMode( DropMode.ON_OR_INSERT );
        tree.setTransferHandler( new TreeTransferHandler() );
        tree.getSelectionModel().setSelectionMode( TreeSelectionModel.CONTIGUOUS_TREE_SELECTION );
        expandTree( tree );
        return new JScrollPane( tree );
    }

    private void expandTree( JTree tree ) {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode)tree.getModel().getRoot();
        Enumeration e = root.breadthFirstEnumeration();
        while( e.hasMoreElements() ) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.nextElement();
            if( node.isLeaf() ) {
                continue;
            }
            int row = tree.getRowForPath( new TreePath( node.getPath() ) );
            tree.expandRow( row );
        }
    }

    public static void main( String[] args ) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        f.add( new Example().getContent() );
        f.setSize( 400, 400 );
        f.setLocation( 200, 200 );
        f.setVisible( true );
    }
}

class TreeTransferHandler extends TransferHandler {

    DataFlavor nodesFlavor;
    DataFlavor[] flavors = new DataFlavor[1];
    DefaultMutableTreeNode[] nodesToRemove;

    public TreeTransferHandler() {
        try {
            String mimeType = DataFlavor.javaJVMLocalObjectMimeType
                    + ";class=\""
                    + javax.swing.tree.DefaultMutableTreeNode[].class.getName()
                    + "\"";
            nodesFlavor = new DataFlavor( mimeType );
            flavors[0] = nodesFlavor;
        } catch( ClassNotFoundException e ) {
            System.out.println( "ClassNotFound: " + e.getMessage() );
        }
    }

    public boolean canImport( TransferHandler.TransferSupport support ) {
        if( !support.isDrop() ) {
            return false;
        }

        boolean isScrolling = false;
        JTree tree = (JTree)support.getComponent();
        JViewport vp = (JViewport)tree.getParent();
        Point vpMousePosition = vp.getMousePosition();
        Rectangle treeVisibleRectangle = tree.getVisibleRect();

        // Don't attempt scroll if mouse isn't over tree
        if( vpMousePosition != null ) {
            Integer newY = null;

            // Make sure we aren't already scrolled all the way down
            if( tree.getHeight() - treeVisibleRectangle.y != vp.getHeight() ) {
                /*
                 * Get Y coordinate for scrolling down
                 */
                if( vp.getHeight() - vpMousePosition.y < 10 ) {
                    newY = treeVisibleRectangle.y + 500;
                } else if( vp.getHeight() - vpMousePosition.y < 20 ) {
                    newY = treeVisibleRectangle.y + 400;
                } else if( vp.getHeight() - vpMousePosition.y < 30 ) {
                    newY = treeVisibleRectangle.y + 300;
                } else if( vp.getHeight() - vpMousePosition.y < 40 ) {
                    newY = treeVisibleRectangle.y + 200;
                } else if( vp.getHeight() - vpMousePosition.y < 50 ) {
                    newY = treeVisibleRectangle.y + 100;
                } else if( vp.getHeight() - vpMousePosition.y < 60 ) {
                    newY = treeVisibleRectangle.y + 50;
                } else if( vp.getHeight() - vpMousePosition.y < 70 ) {
                    newY = treeVisibleRectangle.y + 25;
                } else if( vp.getHeight() - vpMousePosition.y < 80 ) {
                    newY = treeVisibleRectangle.y + 10;
                }
            }

            // Make sure we aren't already scrolled all the way up
            if( newY == null && treeVisibleRectangle.y != 0 ) {
                /*
                 * Get Y coordinate for scrolling up
                 */
                if( 10 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 500;
                } else if( 20 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 400;
                } else if( 30 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 300;
                } else if( 40 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 200;
                } else if( 50 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 100;
                } else if( 60 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 50;
                } else if( 70 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 25;
                } else if( 80 > vpMousePosition.y ) {
                    newY = treeVisibleRectangle.y - 10;
                }
            }

            // Do the scroll
            if( newY != null  ) {
                Rectangle treeNewVisibleRectangle = new Rectangle( treeVisibleRectangle.x, newY, treeVisibleRectangle.width, treeVisibleRectangle.height );
                tree.scrollRectToVisible( treeNewVisibleRectangle );
                isScrolling = true;
            }
        }

        if( isScrolling ) {
            return false;
        }

        support.setShowDropLocation( true );
        if( !support.isDataFlavorSupported( nodesFlavor ) ) {
            return false;
        }
        // Do not allow a drop on the drag source selections.
        JTree.DropLocation dl = (JTree.DropLocation)support.getDropLocation();
        int dropRow = tree.getRowForPath( dl.getPath() );
        int[] selRows = tree.getSelectionRows();
        for( int i = 0; i < selRows.length; i++ ) {
            if( selRows[i] == dropRow ) {
                return false;
            }
        }
        // Do not allow MOVE-action drops if a non-leaf node is
        // selected unless all of its children are also selected.
        int action = support.getDropAction();
        if( action == MOVE ) {
            return haveCompleteNode( tree );
        }
        // Do not allow a non-leaf node to be copied to a level
        // which is less than its source level.
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode target = (DefaultMutableTreeNode)dest.getLastPathComponent();
        TreePath path = tree.getPathForRow( selRows[0] );
        DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode)path.getLastPathComponent();
        if( firstNode.getChildCount() > 0 && target.getLevel() < firstNode.getLevel() ) {
            return false;
        }
        return true;
    }

    private boolean haveCompleteNode( JTree tree ) {
        int[] selRows = tree.getSelectionRows();
        TreePath path = tree.getPathForRow( selRows[0] );
        DefaultMutableTreeNode first = (DefaultMutableTreeNode)path.getLastPathComponent();
        int childCount = first.getChildCount();
        // first has children and no children are selected.
        if( childCount > 0 && selRows.length == 1 ) {
            return false;
        }
        // first may have children.
        for( int i = 1; i < selRows.length; i++ ) {
            path = tree.getPathForRow( selRows[i] );
            DefaultMutableTreeNode next = (DefaultMutableTreeNode)path.getLastPathComponent();
            if( first.isNodeChild( next ) ) {
                // Found a child of first.
                if( childCount > selRows.length - 1 ) {
                    // Not all children of first are selected.
                    return false;
                }
            }
        }
        return true;
    }

    protected Transferable createTransferable( JComponent c ) {
        JTree tree = (JTree)c;
        TreePath[] paths = tree.getSelectionPaths();
        if( paths != null ) {
            // Make up a node array of copies for transfer and
            // another for/of the nodes that will be removed in
            // exportDone after a successful drop.
            List<DefaultMutableTreeNode> copies = new ArrayList<DefaultMutableTreeNode>();
            List<DefaultMutableTreeNode> toRemove = new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)paths[0].getLastPathComponent();
            DefaultMutableTreeNode copy = copy( node );
            copies.add( copy );
            toRemove.add( node );
            for( int i = 1; i < paths.length; i++ ) {
                DefaultMutableTreeNode next = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
                // Do not allow higher level nodes to be added to list.
                if( next.getLevel() < node.getLevel() ) {
                    break;
                } else if( next.getLevel() > node.getLevel() ) {  // child node
                    copy.add( copy( next ) );
                    // node already contains child
                } else {                                        // sibling
                    copies.add( copy( next ) );
                    toRemove.add( next );
                }
            }
            DefaultMutableTreeNode[] nodes = copies.toArray( new DefaultMutableTreeNode[copies.size()] );
            nodesToRemove = toRemove.toArray( new DefaultMutableTreeNode[toRemove.size()] );
            return new NodesTransferable( nodes );
        }
        return null;
    }

    /**
     * Defensive copy used in createTransferable.
     */
    private DefaultMutableTreeNode copy( TreeNode node ) {
        return new DefaultMutableTreeNode( node );
    }

    protected void exportDone( JComponent source, Transferable data, int action ) {
        if( ( action & MOVE ) == MOVE ) {
            JTree tree = (JTree)source;
            DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
            // Remove nodes saved in nodesToRemove in createTransferable.
            for( int i = 0; i < nodesToRemove.length; i++ ) {
                model.removeNodeFromParent( nodesToRemove[i] );
            }
        }
    }

    public int getSourceActions( JComponent c ) {
        return COPY_OR_MOVE;
    }

    public boolean importData( TransferHandler.TransferSupport support ) {
        if( !canImport( support ) ) {
            return false;
        }
        // Extract transfer data.
        DefaultMutableTreeNode[] nodes = null;
        try {
            Transferable t = support.getTransferable();
            nodes = (DefaultMutableTreeNode[])t.getTransferData( nodesFlavor );
        } catch( UnsupportedFlavorException ufe ) {
            System.out.println( "UnsupportedFlavor: " + ufe.getMessage() );
        } catch( java.io.IOException ioe ) {
            System.out.println( "I/O error: " + ioe.getMessage() );
        }
        // Get drop location info.
        JTree.DropLocation dl = (JTree.DropLocation)support.getDropLocation();
        int childIndex = dl.getChildIndex();
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode parent = (DefaultMutableTreeNode)dest.getLastPathComponent();
        JTree tree = (JTree)support.getComponent();
        DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
        // Configure for drop mode.
        int index = childIndex;    // DropMode.INSERT
        if( childIndex == -1 ) {     // DropMode.ON
            index = parent.getChildCount();
        }
        // Add data to model.
        for( int i = 0; i < nodes.length; i++ ) {
            model.insertNodeInto( nodes[i], parent, index++ );
        }
        return true;
    }

    public String toString() {
        return getClass().getName();
    }

    public class NodesTransferable implements Transferable {

        DefaultMutableTreeNode[] nodes;

        public NodesTransferable( DefaultMutableTreeNode[] nodes ) {
            this.nodes = nodes;
        }

        public Object getTransferData( DataFlavor flavor )
                throws UnsupportedFlavorException {
            if( !isDataFlavorSupported( flavor ) ) {
                throw new UnsupportedFlavorException( flavor );
            }
            return nodes;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported( DataFlavor flavor ) {
            return nodesFlavor.equals( flavor );
        }
    }
}

5
2018-01-23 17:29


Ursprung


Antworten:


Wenn es nicht gut mit SDK funktioniert, dann beginne ich meine hackischen Methoden, in diesem Fall ist es eine Art von: hör die Maus. Wenn die Maus um 10 Pixel nach unten bewegt wurde, wird ein Ereignis ausgelöst, wenn die Maus 100 Pixel in die gleiche Richtung bewegt. In Windows, in Excel, wenn Sie die Taskleiste mit der Maus ziehen, wird das Scrollen beschleunigt. Etwas ähnliches sollte hier sein, es ist einen Versuch wert.


2
2018-01-23 17:35