MultiSplitPane 拖拽到边缘反弹问题解决

本文介绍了一个名为 MultiSplitPane 的自定义 Java Swing 组件,该组件允许用户通过拖动分割条来调整面板大小。MultiSplitPane 支持连续布局更新,并提供了自定义分割条绘制的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


/*
 * $Id: MultiSplitPane.java,v 1.15 2005/10/26 14:29:54 hansmuller Exp $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package zhipu.customview.mulipanel;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;

import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;

import org.jdesktop.swingx.*;
import org.jdesktop.swingx.MultiSplitLayout.Divider;
import org.jdesktop.swingx.MultiSplitLayout.Node;

/**
 *
 * <p>
 * All properties in this class are bound: when a properties value
 * is changed, all PropertyChangeListeners are fired.
 *
 * @author Hans Muller - SwingX
 */
public class MultiSplitPane extends JPanel {
  private AccessibleContext accessibleContext = null;
  private boolean continuousLayout = true;
  private DividerPainter dividerPainter = new DefaultDividerPainter();

  /**
   * Creates a MultiSplitPane with it's LayoutManager set to
   * to an empty MultiSplitLayout.
   */
  public MultiSplitPane() {
    super(new org.jdesktop.swingx.MultiSplitLayout());
    InputHandler inputHandler = new InputHandler();
    addMouseListener(inputHandler);
    addMouseMotionListener(inputHandler);
    addKeyListener(inputHandler);
    setFocusable(true);
  }

  /**
   * A convenience method that returns the layout manager cast
   * to MutliSplitLayout.
   *
   * @return this MultiSplitPane's layout manager
   * @see java.awt.Container#getLayout
   * @see #setModel
   */
  public final org.jdesktop.swingx.MultiSplitLayout getMultiSplitLayout() {
    return (org.jdesktop.swingx.MultiSplitLayout)getLayout();
  }

  /**
   * A convenience method that sets the MultiSplitLayout model.
   * Equivalent to <code>getMultiSplitLayout.setModel(model)</code>
   *
   * @param model the root of the MultiSplitLayout model
   * @see #getMultiSplitLayout
   * @see org.jdesktop.swingx.MultiSplitLayout#setModel
   */
  public final void setModel(Node model) {
    getMultiSplitLayout().setModel(model);
  }

  /**
   * A convenience method that sets the MultiSplitLayout dividerSize
   * property. Equivalent to
   * <code>getMultiSplitLayout().setDividerSize(newDividerSize)</code>.
   *
   * @param dividerSize the value of the dividerSize property
   * @see #getMultiSplitLayout
   * @see org.jdesktop.swingx.MultiSplitLayout#setDividerSize
   */
  public final void setDividerSize(int dividerSize) {
    getMultiSplitLayout().setDividerSize(dividerSize);
  }

  /**
   * Sets the value of the <code>continuousLayout</code> property.
   * If true, then the layout is revalidated continuously while
   * a divider is being moved.  The default value of this property
   * is true.
   *
   * @param continuousLayout value of the continuousLayout property
   * @see #isContinuousLayout
   */
  public void setContinuousLayout(boolean continuousLayout) {
    boolean oldContinuousLayout = continuousLayout;
    this.continuousLayout = continuousLayout;
    firePropertyChange("continuousLayout", oldContinuousLayout, continuousLayout);
  }

  /**
   * Returns true if dragging a divider only updates
   * the layout when the drag gesture ends (typically, when the
   * mouse button is released).
   *
   * @return the value of the <code>continuousLayout</code> property
   * @see #setContinuousLayout
   */
  public boolean isContinuousLayout() {
    return continuousLayout;
  }

  /**
   * Returns the Divider that's currently being moved, typically
   * because the user is dragging it, or null.
   *
   * @return the Divider that's being moved or null.
   */
  public Divider activeDivider() {
    return dragDivider;
  }

  /**
   * Draws a single Divider.  Typically used to specialize the
   * way the active Divider is painted.
   *
   * @see #getDividerPainter
   * @see #setDividerPainter
   */
  public abstract static class DividerPainter {
    /**
     * Paint a single Divider.
     *
     * @param g the Graphics object to paint with
     * @param divider the Divider to paint
     */
    public abstract void paint(Graphics g, Divider divider);
  }

  private class DefaultDividerPainter extends DividerPainter {
    @Override
    public void paint(Graphics g, Divider divider) {
      if ((divider == activeDivider()) && !isContinuousLayout()) {
        Graphics2D g2d = (Graphics2D)g;
        g2d.setColor(Color.black);
        g2d.fill(divider.getBounds());
      }
    }
  }

  /**
   * The DividerPainter that's used to paint Dividers on this MultiSplitPane.
   * This property may be null.
   *
   * @return the value of the dividerPainter Property
   * @see #setDividerPainter
   */
  public DividerPainter getDividerPainter() {
    return dividerPainter;
  }

  /**
   * Sets the DividerPainter that's used to paint Dividers on this
   * MultiSplitPane.  The default DividerPainter only draws
   * the activeDivider (if there is one) and then, only if
   * continuousLayout is false.  The value of this property is
   * used by the paintChildren method: Dividers are painted after
   * the MultiSplitPane's children have been rendered so that
   * the activeDivider can appear "on top of" the children.
   *
   * @param dividerPainter the value of the dividerPainter property, can be null
   * @see #paintChildren
   * @see #activeDivider
   */
  public void setDividerPainter(DividerPainter dividerPainter) {
    this.dividerPainter = dividerPainter;
  }

  /**
   * Uses the DividerPainter (if any) to paint each Divider that
   * overlaps the clip Rectangle.  This is done after the call to
   * <code>super.paintChildren()</code> so that Dividers can be
   * rendered "on top of" the children.
   * <p>
   * {@inheritDoc}
   */
  @Override
  protected void paintChildren(Graphics g) {
    super.paintChildren(g);
    DividerPainter dp = getDividerPainter();
    Rectangle clipR = g.getClipBounds();
    if ((dp != null) && (clipR != null)) {
      Graphics dpg = g.create();
      try {
        org.jdesktop.swingx.MultiSplitLayout msl = getMultiSplitLayout();
        for(Divider divider : msl.dividersThatOverlap(clipR)) {
          dp.paint(dpg, divider);
        }
      }
      finally {
        dpg.dispose();
      }
    }
  }

  private boolean dragUnderway = false;
  private Divider dragDivider = null;
  private Rectangle initialDividerBounds = null;
  private boolean oldFloatingDividers = true;
  private int dragOffsetX = 0;
  private int dragOffsetY = 0;
  private int dragMin = -1;
  private int dragMax = -1;

  private void startDrag(int mx, int my) {
    requestFocusInWindow();
    org.jdesktop.swingx.MultiSplitLayout msl = getMultiSplitLayout();
    Divider divider = msl.dividerAt(mx, my);
    if (divider != null) {
      Node prevNode = divider.previousSibling();
      Node nextNode = divider.nextSibling();
      if ((prevNode == null) || (nextNode == null)) {
        dragUnderway = false;
      }
      else {
        initialDividerBounds = divider.getBounds();
        dragOffsetX = mx - initialDividerBounds.x;
        dragOffsetY = my - initialDividerBounds.y;
        dragDivider  = divider;
        Rectangle prevNodeBounds = prevNode.getBounds();
        Rectangle nextNodeBounds = nextNode.getBounds();
        if (dragDivider.isVertical()) {
          dragMin = prevNodeBounds.x+5;//解决拖拽到边缘界面异常的问题
          dragMax = nextNodeBounds.x + nextNodeBounds.width;
          dragMax -= dragDivider.getBounds().width+5;//解决拖拽到边缘界面异常的问题
        }
        else {
          dragMin = prevNodeBounds.y+5;
          dragMax = nextNodeBounds.y + nextNodeBounds.height;
          dragMax -= dragDivider.getBounds().height+5;
        }
        oldFloatingDividers = getMultiSplitLayout().getFloatingDividers();
        getMultiSplitLayout().setFloatingDividers(false);
        dragUnderway = true;
      }
    }
    else {
        dragUnderway = false;
    }
  }

  private void repaintDragLimits() {
    Rectangle damageR = dragDivider.getBounds();
    if (dragDivider.isVertical()) {
      damageR.x = dragMin;
      damageR.width = dragMax - dragMin;
    }
    else {
      damageR.y = dragMin;
      damageR.height = dragMax - dragMin;
    }
    repaint(damageR);
  }

  private void updateDrag(int mx, int my) {
    if (!dragUnderway) {
      return;
    }
    Rectangle oldBounds = dragDivider.getBounds();
    Rectangle bounds = new Rectangle(oldBounds);
    if (dragDivider.isVertical()) {
      bounds.x = mx - dragOffsetX;
      bounds.x = Math.max(bounds.x, dragMin);
      bounds.x = Math.min(bounds.x, dragMax);
    }
    else {
      bounds.y = my - dragOffsetY;
      bounds.y = Math.max(bounds.y, dragMin);
      bounds.y = Math.min(bounds.y, dragMax);
    }
    dragDivider.setBounds(bounds);
    if (isContinuousLayout()) {
      revalidate();
      repaintDragLimits();
    }
    else {
      repaint(oldBounds.union(bounds));
    }
  }

  private void clearDragState() {
    dragDivider = null;
    initialDividerBounds = null;
    oldFloatingDividers = true;
    dragOffsetX = dragOffsetY = 0;
    dragMin = dragMax = -1;
    dragUnderway = false;
  }

  private void finishDrag(int x, int y) {
    if (dragUnderway) {
      clearDragState();
      if (!isContinuousLayout()) {
        revalidate();
        repaint();
      }
    }
  }

  private void cancelDrag() {
    if (dragUnderway) {
      dragDivider.setBounds(initialDividerBounds);
      getMultiSplitLayout().setFloatingDividers(oldFloatingDividers);
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      repaint();
      revalidate();
      clearDragState();
    }
  }

  private void updateCursor(int x, int y, boolean show) {
    if (dragUnderway) {
      return;
    }
    int cursorID = Cursor.DEFAULT_CURSOR;
    if (show) {
      Divider divider = getMultiSplitLayout().dividerAt(x, y);
      if (divider != null) {
        cursorID  = (divider.isVertical()) ? Cursor.E_RESIZE_CURSOR : Cursor.N_RESIZE_CURSOR;
      }
    }
    setCursor(Cursor.getPredefinedCursor(cursorID));
  }

  private class InputHandler extends MouseInputAdapter implements KeyListener {

    @Override
    public void mouseEntered(MouseEvent e) {
      updateCursor(e.getX(), e.getY(), true);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
      updateCursor(e.getX(), e.getY(), true);
    }

    @Override
    public void mouseExited(MouseEvent e) {
      updateCursor(e.getX(), e.getY(), false);
    }

    @Override
    public void mousePressed(MouseEvent e) {
      startDrag(e.getX(), e.getY());
    }
    @Override
    public void mouseReleased(MouseEvent e) {
      finishDrag(e.getX(), e.getY());
    }
    @Override
    public void mouseDragged(MouseEvent e) {
      updateDrag(e.getX(), e.getY());
    }
    @Override
    public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
        cancelDrag();
      }
    }
    @Override
    public void keyReleased(KeyEvent e) { }
    @Override
    public void keyTyped(KeyEvent e) { }
  }

  @Override
  public AccessibleContext getAccessibleContext() {
    if( accessibleContext == null ) {
      accessibleContext = new AccessibleMultiSplitPane();
    }
    return accessibleContext;
  }

  protected class AccessibleMultiSplitPane extends AccessibleJPanel {
    @Override
    public AccessibleRole getAccessibleRole() {
      return AccessibleRole.SPLIT_PANE;
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI算法网奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值