如何在Swing中将一个窗口停靠到另一个窗口?

时间:2011-04-15 20:59:43

标签: java window docking

我打开了两个JFrame。我有窗口B产生在窗口A的右边,紧贴窗口A的右边缘。我希望它坚持并使用窗口A移动。我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:3)

我创建了一个名为DockingManager的类,它允许您对接Dialogs和Frames伪Winamp样式

它还可以让你制作不同的星团并设置磁场水平来选择哪个帧拖动其他帧

package com.gsteren.docking;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;

public class DockingManager {

    public static enum DockingMode {
        EAST, WEST, NORTH, SOUTH, NONE
    }

    int magneticDistance = 20;

    public static Map<Component, DockingApplier> monitoredComponents = new HashMap<Component, DockingApplier>();

    /**
     * Para saber cual es el último Component movido por el usuario.
     */
    private static DockingApplier focusedApplier;

    public void registerComponent(Component component) {
        this.registerComponent(component, 1);
    }

    public void registerComponent(Component component, int magneticLevel) {
        DockingApplier applier = new DockingApplier(component, magneticLevel);
        monitoredComponents.put(component, applier);
    }

    /**
     * Indica el grupo completo de componentes pegados al que pertenece un
     * componente.
     * 
     * @author Guillermo
     * 
     */
    protected class DockingApplierCluster {
        Set<DockingApplier> appliers = new HashSet<DockingApplier>();
        DockingApplier leader = null;

        public DockingApplierCluster(DockingApplier applier) {
            this.addToCluster(applier);
        }

        public DockingApplierCluster(Set<DockingApplier> appliers) {
            for (DockingApplier applier : appliers) {
                this.addToCluster(applier);
            }
        }

        public void addToCluster(DockingApplier applier) {
            appliers.add(applier);
            applier.setCluster(this);
            if (null == leader || applier.getMagneticLevel() < leader.getMagneticLevel()) {
                this.setLeader(applier);
            }
        }

        public int getSize() {
            return getAppliers().size();
        }

        public void mergeCluster(DockingApplierCluster cluster) {
            DockingApplierCluster bigCluster;
            DockingApplierCluster smallCluster;
            if (getSize() > cluster.getSize()) {
                bigCluster = this;
                smallCluster = cluster;
            } else {
                bigCluster = cluster;
                smallCluster = this;
            }
            for (DockingApplier applier : smallCluster.getAppliers()) {
                bigCluster.addToCluster(applier);
            }

        }

        public Set<DockingApplier> getAppliers() {
            return this.appliers;
        }

        /**
         * @return the leader
         */
        public DockingApplier getLeader() {
            return leader;
        }

        /**
         * @param leader
         *            the leader to set
         */
        public void setLeader(DockingApplier leader) {
            this.leader = leader;
        }

        public void remove(DockingApplier applier) {
            if (this.getAppliers().size() == 1) {
                /* No sacamos el único elemento */
                return;
            }

            this.getAppliers().remove(applier);

            if (this.leader == applier) {
                this.leader = findLeader();
            }

            /* Pude haber dividido en varios clusters */
            this.recalculateClustersAfterRemoval();
        }

        public void recalculateClustersAfterRemoval() {
            Set<DockingApplier> myAppliersCopy = new HashSet<DockingManager.DockingApplier>(getAppliers());
            Set<DockingApplier> visitedAppliers = new HashSet<DockingManager.DockingApplier>();
            for (DockingApplier applier : myAppliersCopy) {
                if (visitedAppliers.contains(applier)) {
                    continue;
                }
                Set<DockingApplier> newClusterComponents = findClusteredAppliers(applier);
                if (newClusterComponents.size() == myAppliersCopy.size()) {
                    /* No se dividieron los clusters */
                    return;
                }
                visitedAppliers.addAll(newClusterComponents);
                /* Creo un nuevo cluster, y le agrego los elementos */
                new DockingApplierCluster(newClusterComponents);
            }

        }

        /**
         * Devuelve todos los DockingAppliers anexos entre si.
         * 
         * @param anApplier
         * @return
         */
        public Set<DockingApplier> findClusteredAppliers(DockingApplier anApplier) {
            return findClusteredAppliers(anApplier, new HashSet<DockingManager.DockingApplier>());
        }

        public Set<DockingApplier> findClusteredAppliers(DockingApplier anApplier, Set<DockingApplier> currentSet) {
            currentSet.add(anApplier);
            for (DockingApplier applier : anApplier.getAttachedComponents()) {
                if (currentSet.contains(applier)) {
                    continue;
                }
                currentSet.add(applier);
                currentSet.addAll(findClusteredAppliers(applier, currentSet));
            }
            return currentSet;
        }

        private DockingApplier findLeader() {
            DockingApplier leaderCandidate = null;
            for (DockingApplier applier : getAppliers()) {
                if (leaderCandidate == null || applier.getMagneticLevel() < leaderCandidate.getMagneticLevel()) {
                    leaderCandidate = applier;
                    if (applier.getMagneticLevel() == 1) {
                        /* Encontramos óptimo */
                        break;
                    }
                }
            }
            return leaderCandidate;
        }

    }

    protected class DockingApplier implements ComponentListener, FocusListener {

        private Component component = null;
        private DockingApplierCluster cluster;

        public DockingApplier(Component component, int magneticLevel) {
            this.component = component;
            this.cluster = new DockingApplierCluster(this);
            this.magneticLevel = magneticLevel;
            this.updateLastLocation();

            /*
             * Esto no es necesario ya que registerComponent es quien crea el
             * applier
             */
            if (!monitoredComponents.containsKey(component)) {
                monitoredComponents.put(component, this);
            }

            this.component.addComponentListener(this);
            this.component.addFocusListener(this);
            componentFinishedMovingDetector.setRepeats(false);
        }

        public void setCluster(DockingApplierCluster cluster) {
            this.cluster = cluster;
        }

        /**
         * @return the cluster
         */
        public DockingApplierCluster getCluster() {
            return cluster;
        }

        boolean isClusterLeader() {
            return getCluster().getLeader() == this;
        }

        /**
         * @return the magneticLevel
         */
        public Integer getMagneticLevel() {
            return magneticLevel;
        }

        /**
         * @param magneticLevel
         *            the magneticLevel to set
         */
        public void setMagneticLevel(Integer magneticLevel) {
            this.magneticLevel = magneticLevel;
        }

        /**
         * @return the isDocking
         */
        protected boolean isDocking() {
            return isDocking;
        }

        /**
         * @param isDocking
         *            the isDocking to set
         */
        protected void setDocking(boolean isDocking) {
            this.isDocking = isDocking;
        }

        /**
         * @return the attachedComponents
         */
        public Set<DockingApplier> getAttachedComponents() {
            return attachedComponents;
        }

        int northY;
        int southY;
        int westX;
        int eastX;

        public void recalculateBorderCoordinates() {
            Point compLoc = component.getLocation();
            Dimension compDim = component.getSize();
            northY = compLoc.y;
            southY = northY + compDim.height;
            westX = compLoc.x;
            eastX = westX + compDim.width;
        }

        public DockingMode calculateWhereToDock(DockingApplier other, int magneticDistance) {
            return calculateWhereToDock(this, other, magneticDistance);
        }

        /**
         * Indica si me debería lockearse con other
         * 
         * @param me
         * @param other
         * @return
         */
        public DockingMode calculateWhereToDock(DockingApplier me, DockingApplier other, int magneticDistance) {

            /* Talvez innecesario */
            me.recalculateBorderCoordinates();
            other.recalculateBorderCoordinates();

            if (me.getAttachedComponents().contains(other)) {
                /* Ya estan conectados */
                return DockingMode.NONE;
            }

            int dockDistN = me.northY - other.southY;
            int dockDistS = other.northY - me.southY;
            int dockDistW = me.westX - other.eastX;
            int dockDistE = other.westX - me.eastX;

            if (dockDistN > 0 && magneticDistance > dockDistN && checkOverlappingEastWest(me, other)) {
                return DockingMode.NORTH;
            } else if (dockDistS > 0 && magneticDistance > dockDistS && checkOverlappingEastWest(me, other)) {
                return DockingMode.SOUTH;
            } else if (dockDistW > 0 && magneticDistance > dockDistW && checkOverlappingNorthSouth(me, other)) {
                return DockingMode.WEST;
            } else if (dockDistE > 0 && magneticDistance > dockDistE && checkOverlappingNorthSouth(me, other)) {
                return DockingMode.EAST;
            }
            return DockingMode.NONE;
        }

        /**
         * Checks whether components overlap in north/south direction.
         */
        protected boolean checkOverlappingEastWest(DockingApplier me, DockingApplier other) {
            return checkOverlappingEastWest_aux(me, other) || checkOverlappingEastWest_aux(other, me);
        }

        /**
         * Checks whether components overlap in east/west direction.
         */
        protected boolean checkOverlappingEastWest_aux(DockingApplier me, DockingApplier other) {
            return me.westX >= other.westX && me.westX <= other.eastX || me.eastX >= other.westX
                    && me.eastX <= other.eastX;
        }

        /**
         * Checks whether components overlap in north/south direction.
         */
        protected boolean checkOverlappingNorthSouth(DockingApplier me, DockingApplier other) {
            return checkOverlappingNorthSouth_aux(me, other) || checkOverlappingNorthSouth_aux(other, me);
        }

        /**
         * Checks whether components overlap in north/south direction.
         */
        protected boolean checkOverlappingNorthSouth_aux(DockingApplier me, DockingApplier other) {
            return me.northY >= other.northY && me.northY <= other.southY || me.southY >= other.northY
                    && me.southY <= other.southY;
        }

        public Point calculateDockedLocation(DockingApplier other, DockingMode mode) {
            return calculateDockedLocation(this, other, mode);
        }

        public Point calculateDockedLocation(DockingApplier me, DockingApplier other, DockingMode mode) {
            final Point meLoc = me.getComponent().getLocation();
            final Point otherLoc = other.getComponent().getLocation();
            final Dimension otherDim = other.getComponent().getSize();
            final Dimension meDim = me.getComponent().getSize();

            /* Posiciones relativas a other */
            switch (mode) {
            case NORTH:
                return new Point(meLoc.x, otherLoc.y + otherDim.height);
            case SOUTH:
                return new Point(meLoc.x, otherLoc.y - meDim.height);
            case WEST:
                return new Point(otherLoc.x + otherDim.width, meLoc.y);
            case EAST:
                return new Point(otherLoc.x - meDim.width, meLoc.y);
            default:
                return new Point(meLoc.x - otherLoc.x, meLoc.y);
            }
        }

        /**
         * Retrasa la accion a ejecutar en onComponentFinishedMoving hasta que
         * pasan 10ms sin que se mueva el componente afectado.
         */
        Timer componentFinishedMovingDetector = new Timer(300, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onComponentFinishedMoving();
            }
        });

        /**
         * Resetea el componentFinishedMovingDetector, se debe llamar cada vez
         * que se mueve o cambia el tamaño de la ventana.
         */
        protected void startComponentFinishedMovingDetector() {
            if (componentFinishedMovingDetector.isRunning()) {
                componentFinishedMovingDetector.restart();
            } else {
                componentFinishedMovingDetector.start();
            }
        }

        /* Mientras es menor, más probable es ser lider */
        int magneticLevel = 1;

        /* Indica si el componente esta en proceso de hacer dock */
        boolean isDocking = false;

        /* Para desconectarlos en resize */
        Set<DockingApplier> attachedComponents = new HashSet<DockingApplier>();
        /* Indica la posición del componente */
        private Point lastLocation;

        public boolean isDocked() {
            return getCluster().getSize() > 1;
        }

        /**
         * @return the component
         */
        protected Component getComponent() {
            return component;
        }

        public void componentResized(ComponentEvent e) {
            startComponentFinishedMovingDetector();
            this.recalculateBorderCoordinates();
            this.unDock();
        }

        public void componentMoved(ComponentEvent e) {

            this.recalculateBorderCoordinates();

            /*
             * Si el movimiento es consecuencia de hacer dock lo ignoro y marco
             * como que el docking se completó
             */
            if (this.isDocking()) {
                this.setDocking(false);
                return;
            }

            startComponentFinishedMovingDetector();

            if (this != focusedApplier) {
                return;
            }

            if (getCluster().getSize() == 1) {
                return;
            }

            if (!this.isClusterLeader()) {
                this.updateLastLocation();
                this.unDock();
                return;
            }

            positionAttachedComponents();

        }

        public void onComponentFinishedMoving() {
            this.recalculateBorderCoordinates();
            for (DockingApplier otherDockingApplier : getMonitoredComponents().values()) {
                if (otherDockingApplier == this) {
                    continue;
                }
                DockingMode dockMode = calculateWhereToDock(this, otherDockingApplier);
                if (!DockingMode.NONE.equals(dockMode)) {
                    System.out.println("shouldAttach");
                    this.dock(otherDockingApplier, dockMode);                   
                    this.updateLastLocation();
                } else {
                    System.out.println("shouldNotAttach");
                }
            }
        }

        public void setLocation(int x, int y) {
            this.setLocation(new Point(x, y));
        }

        public void setLocation(Point location) {
            this.getComponent().removeComponentListener(this);
            this.getComponent().setLocation(location);
            this.setLastLocation(location);
            this.getComponent().addComponentListener(this);
        }

        private void setLastLocation(Point location) {
            this.lastLocation = location;
        }

        public Point getLocation() {
            return this.getComponent().getLocation();
        }

        /**
         * @return the lastLocation
         */
        public Point getLastLocation() {
            return lastLocation;
        }

        protected void dock(DockingApplier otherDockingApplier, DockingMode dockMode) {
            this.setDocking(true);
            Point dockInfo = this.calculateDockedLocation(otherDockingApplier, dockMode);
            this.setLocation(dockInfo.x, dockInfo.y);
            this.bindAppliers(otherDockingApplier);
            /* Uno los clusters */
            otherDockingApplier.getCluster().mergeCluster(this.getCluster());
        }

        public void bindAppliers(DockingApplier otherDockingApplier) {
            this.getAttachedComponents().add(otherDockingApplier);
            otherDockingApplier.getAttachedComponents().add(this);
        }

        public void unDock() {
            if (this.getCluster().getSize() == 1) {
                return;
            }
            /*
             * Primero lo quito de sus vecinos, luego del cluster, el orden es
             * importante para el calculo de clusters en caso de división.
             */
            Set<DockingApplier> attachedComponentsCopy = new HashSet<DockingManager.DockingApplier>(
                    this.getAttachedComponents());
            for (DockingApplier applier : attachedComponentsCopy) {
                this.unbind(applier);
            }

            this.getCluster().remove(this);
            this.setCluster(new DockingApplierCluster(this));
        }

        public void unbind(DockingApplier dockingApplier) {
            this.getAttachedComponents().remove(dockingApplier);
            dockingApplier.getAttachedComponents().remove(this);
        }

        private DockingMode calculateWhereToDock(DockingApplier component, DockingApplier otherDockingApplier) {
            return this.calculateWhereToDock(otherDockingApplier, magneticDistance);
        }

        public void componentShown(ComponentEvent e) {
            getMonitoredComponents().get(component).recalculateBorderCoordinates();
            // positionAttachedComponents(e);
        }

        public void componentHidden(ComponentEvent e) {
            getMonitoredComponents().get(component).recalculateBorderCoordinates();
            // positionAttachedComponents(e);
        }

        private void positionAttachedComponents() {

            /* El lider del cluster debe arrastrar a los otros componentes */
            Point currentLocation = getComponent().getLocation();
            int xDiff = currentLocation.x - getLastLocation().x;
            int yDiff = currentLocation.y - getLastLocation().y;

            if (xDiff == 0 && yDiff == 0) {
                return;
            }

            this.updateLastLocation();

            for (DockingApplier otherApplier : getCluster().getAppliers()) {
                if (otherApplier == this) {
                    continue;
                }
                Point otherComponentLocation = otherApplier.getComponent().getLocation();
                otherApplier.getComponent().removeComponentListener(otherApplier);
                otherApplier.setDocking(true);
                otherApplier.getComponent().setLocation(otherComponentLocation.x + xDiff,
                        otherComponentLocation.y + yDiff);
                otherApplier.getComponent().addComponentListener(otherApplier);
            }
        }

        public void updateLastLocation() {
            this.setLastLocation(getComponent().getLocation());
        }

        @Override
        public void focusGained(FocusEvent e) {
            DockingManager.setFocusedApplier(this);
        }

        @Override
        public void focusLost(FocusEvent e) {
            // TODO Auto-generated method stub

        }
    }

    public DockingManager() {

    }

    public static void setFocusedApplier(DockingApplier applier) {
        DockingManager.focusedApplier = applier;
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }
        JFrame frame1 = new JFrame("Frame 1");
        JDialog dialog2 = new JDialog((JFrame) null, "Dialog 2");
        frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame1.setSize(200, 50);
        dialog2.setSize(200, 50);
        frame1.setVisible(true);
        dialog2.setVisible(true);

        JDialog dialog1 = new JDialog((JFrame) null, "Dialog 1");
        dialog1.setSize(200, 50);
        dialog1.setVisible(true);

        DockingManager manager = new DockingManager();

        manager.registerComponent(frame1, 1);
        manager.registerComponent(dialog2, 2);
        manager.registerComponent(dialog1, 2);

    }

    /**
     * @return the monitoredComponents
     */
    protected static Map<Component, DockingApplier> getMonitoredComponents() {
        return monitoredComponents;
    }
}

答案 1 :(得分:0)

尝试JInternalFrame!

有关http://download.oracle.com/javase/tutorial/uiswing/components/internalframe.html

的更多信息,请参阅