原文: http://zetcode.com/tutorials/javaswingtutorial/resizablecomponent/
在 Java Swing 教程的这一部分中,我们将创建一个可调整大小的组件。
可调整大小的组件
在创建图表时,经常使用可调整大小的组件。 常见的可调整大小的组件是电子表格应用中的图表。 可以将图表移到应用的表格小部件上并调整大小。
为了创建可以在面板上自由拖动的组件,我们使用启用了绝对定位的面板。 在我们的示例中,我们将创建一个可以在父窗口上自由移动并调整大小的组件。
当可调整大小的组件具有焦点时,将在其可调整大小的组件的边框上绘制八个小矩形。 矩形用作拖动点,我们可以在其中绘制组件并开始调整大小。
ResizableComponentEx.java
package com.zetcode;import java.awt.Color;import java.awt.EventQueue;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import javax.swing.JFrame;import javax.swing.JPanel;public class ResizableComponentEx extends JFrame {private Resizable res;public ResizableComponentEx() {initUI();}private void initUI() {var pnl = new JPanel(null);add(pnl);var area = new JPanel();area.setBackground(Color.white);res = new Resizable(area);res.setBounds(50, 50, 200, 150);pnl.add(res);addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent me) {requestFocus();res.repaint();}});setSize(350, 300);setTitle("Resizable component");setLocationRelativeTo(null);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}public static void main(String[] args) {EventQueue.invokeLater(() -> {var ex = new ResizableComponentEx();ex.setVisible(true);});}}
ResizableComponentEx设置面板和组件。
var pnl = new JPanel(null);
我们对可调整大小的组件使用绝对定位。 通过将null提供给JPanel的构造器,我们创建了具有绝对定位的面板。
addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent me) {requestFocus();res.repaint();}});
如果我们在父面板上(即在可调整大小的组件外部)按下,我们将抓住焦点并重新绘制该组件。 边框上的矩形将消失。
ResizableBorder.java
package com.zetcode;import java.awt.Color;import java.awt.Component;import java.awt.Cursor;import java.awt.Graphics;import java.awt.Insets;import java.awt.Rectangle;import java.awt.event.MouseEvent;import javax.swing.SwingConstants;import javax.swing.border.Border;public class ResizableBorder implements Border {private int dist = 8;int locations[] = {SwingConstants.NORTH, SwingConstants.SOUTH, SwingConstants.WEST,SwingConstants.EAST, SwingConstants.NORTH_WEST,SwingConstants.NORTH_EAST, SwingConstants.SOUTH_WEST,SwingConstants.SOUTH_EAST};int cursors[] = {Cursor.N_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR,Cursor.E_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,Cursor.SW_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR};public ResizableBorder(int dist) {this.dist = dist;}@Overridepublic Insets getBorderInsets(Component component) {return new Insets(dist, dist, dist, dist);}@Overridepublic boolean isBorderOpaque() {return false;}@Overridepublic void paintBorder(Component component, Graphics g, int x, int y,int w, int h) {g.setColor(Color.black);g.drawRect(x + dist / 2, y + dist / 2, w - dist, h - dist);if (component.hasFocus()) {for (int i = 0; i < locations.length; i++) {var rect = getRectangle(x, y, w, h, locations[i]);g.setColor(Color.WHITE);g.fillRect(rect.x, rect.y, rect.width - 1, rect.height - 1);g.setColor(Color.BLACK);g.drawRect(rect.x, rect.y, rect.width - 1, rect.height - 1);}}}private Rectangle getRectangle(int x, int y, int w, int h, int location) {switch (location) {case SwingConstants.NORTH:return new Rectangle(x + w / 2 - dist / 2, y, dist, dist);case SwingConstants.SOUTH:return new Rectangle(x + w / 2 - dist / 2, y + h - dist, dist, dist);case SwingConstants.WEST:return new Rectangle(x, y + h / 2 - dist / 2, dist, dist);case SwingConstants.EAST:return new Rectangle(x + w - dist, y + h / 2 - dist / 2, dist, dist);case SwingConstants.NORTH_WEST:return new Rectangle(x, y, dist, dist);case SwingConstants.NORTH_EAST:return new Rectangle(x + w - dist, y, dist, dist);case SwingConstants.SOUTH_WEST:return new Rectangle(x, y + h - dist, dist, dist);case SwingConstants.SOUTH_EAST:return new Rectangle(x + w - dist, y + h - dist, dist, dist);}return null;}public int getCursor(MouseEvent me) {var c = me.getComponent();int w = c.getWidth();int h = c.getHeight();for (int i = 0; i < locations.length; i++) {var rect = getRectangle(0, 0, w, h, locations[i]);if (rect.contains(me.getPoint())) {return cursors[i];}}return Cursor.MOVE_CURSOR;}}
ResizableBorder负责绘制组件的边框并确定要使用的光标的类型。
int locations[] = {SwingConstants.NORTH, SwingConstants.SOUTH, SwingConstants.WEST,SwingConstants.EAST, SwingConstants.NORTH_WEST,SwingConstants.NORTH_EAST, SwingConstants.SOUTH_WEST,SwingConstants.SOUTH_EAST};
这些是绘制矩形的位置。 这些位置也是抓取点,可以在其中抓取组件并调整其大小。
g.setColor(Color.black);g.drawRect(x + dist / 2, y + dist / 2, w - dist, h - dist);
在paintBorder()方法中,我们绘制了可调整大小组件的边框。 上面的代码绘制了组件的外边界。
if (component.hasFocus()) {for (int i = 0; i < locations.length; i++) {var rect = getRectangle(x, y, w, h, locations[i]);g.setColor(Color.WHITE);g.fillRect(rect.x, rect.y, rect.width - 1, rect.height - 1);g.setColor(Color.BLACK);g.drawRect(rect.x, rect.y, rect.width - 1, rect.height - 1);}}
仅当可调整大小的组件当前具有焦点时才绘制八个矩形。
private Rectangle getRectangle(int x, int y, int w, int h, int location) {switch (location) {case SwingConstants.NORTH:return new Rectangle(x + w / 2 - dist / 2, y, dist, dist);case SwingConstants.SOUTH:return new Rectangle(x + w / 2 - dist / 2, y + h - dist, dist, dist);...}
getRectangle()方法返回矩形的坐标。
public int getCursor(MouseEvent me) {var c = me.getComponent();int w = c.getWidth();int h = c.getHeight();for (int i = 0; i < locations.length; i++) {var rect = getRectangle(0, 0, w, h, locations[i]);if (rect.contains(me.getPoint())) {return cursors[i];}}return Cursor.MOVE_CURSOR;}
getCursor()方法获取相关抓点的光标类型。
Resizable.java
package com.zetcode;import javax.swing.JComponent;import javax.swing.event.MouseInputAdapter;import javax.swing.event.MouseInputListener;import java.awt.BorderLayout;import java.awt.Component;import java.awt.Cursor;import java.awt.Point;import java.awt.event.MouseEvent;public class Resizable extends JComponent {public Resizable(Component comp) {this(comp, new ResizableBorder(8));}public Resizable(Component comp, ResizableBorder border) {setLayout(new BorderLayout());add(comp);addMouseListener(resizeListener);addMouseMotionListener(resizeListener);setBorder(border);}private void resize() {if (getParent() != null) {getParent().revalidate();}}MouseInputListener resizeListener = new MouseInputAdapter() {@Overridepublic void mouseMoved(MouseEvent me) {if (hasFocus()) {var resizableBorder = (ResizableBorder) getBorder();setCursor(Cursor.getPredefinedCursor(resizableBorder.getCursor(me)));}}@Overridepublic void mouseExited(MouseEvent mouseEvent) {setCursor(Cursor.getDefaultCursor());}private int cursor;private Point startPos = null;@Overridepublic void mousePressed(MouseEvent me) {var resizableBorder = (ResizableBorder) getBorder();cursor = resizableBorder.getCursor(me);startPos = me.getPoint();requestFocus();repaint();}@Overridepublic void mouseDragged(MouseEvent me) {if (startPos != null) {int x = getX();int y = getY();int w = getWidth();int h = getHeight();int dx = me.getX() - startPos.x;int dy = me.getY() - startPos.y;switch (cursor) {case Cursor.N_RESIZE_CURSOR:if (!(h - dy < 50)) {setBounds(x, y + dy, w, h - dy);resize();}break;case Cursor.S_RESIZE_CURSOR:if (!(h + dy < 50)) {setBounds(x, y, w, h + dy);startPos = me.getPoint();resize();}break;case Cursor.W_RESIZE_CURSOR:if (!(w - dx < 50)) {setBounds(x + dx, y, w - dx, h);resize();}break;case Cursor.E_RESIZE_CURSOR:if (!(w + dx < 50)) {setBounds(x, y, w + dx, h);startPos = me.getPoint();resize();}break;case Cursor.NW_RESIZE_CURSOR:if (!(w - dx < 50) && !(h - dy < 50)) {setBounds(x + dx, y + dy, w - dx, h - dy);resize();}break;case Cursor.NE_RESIZE_CURSOR:if (!(w + dx < 50) && !(h - dy < 50)) {setBounds(x, y + dy, w + dx, h - dy);startPos = new Point(me.getX(), startPos.y);resize();}break;case Cursor.SW_RESIZE_CURSOR:if (!(w - dx < 50) && !(h + dy < 50)) {setBounds(x + dx, y, w - dx, h + dy);startPos = new Point(startPos.x, me.getY());resize();}break;case Cursor.SE_RESIZE_CURSOR:if (!(w + dx < 50) && !(h + dy < 50)) {setBounds(x, y, w + dx, h + dy);startPos = me.getPoint();resize();}break;case Cursor.MOVE_CURSOR:var bounds = getBounds();bounds.translate(dx, dy);setBounds(bounds);resize();}setCursor(Cursor.getPredefinedCursor(cursor));}}@Overridepublic void mouseReleased(MouseEvent mouseEvent) {startPos = null;}};}
Resizable类表示正在调整大小并在窗口上移动的组件。
private void resize() {if (getParent() != null) {getParent().revalidate();}}
调整组件大小后,将调用resize()方法。 revalidate()方法导致重画组件。
MouseInputListener resizeListener = new MouseInputAdapter() {@Overridepublic void mouseMoved(MouseEvent me) {if (hasFocus()) {var border = (ResizableBorder) getBorder();setCursor(Cursor.getPredefinedCursor(border.getCursor(me)));}}...}
当我们将光标悬停在抓取点上时,我们将更改光标类型。 仅当组件具有焦点时,光标类型才会更改。
@Overridepublic void mousePressed(MouseEvent me) {var resizableBorder = (ResizableBorder) getBorder();cursor = resizableBorder.getCursor(me);startPos = me.getPoint();requestFocus();repaint();}
如果单击可调整大小的组件,则将更改光标,获得拖动的起点,将焦点放在该组件上,然后重新绘制它。
int x = getX();int y = getY();int w = getWidth();int h = getHeight();int dx = me.getX() - startPos.x;int dy = me.getY() - startPos.y;
在mouseDragged()方法中,我们确定光标的 x 和 y 坐标以及组件的宽度和高度。 我们计算鼠标拖动事件期间的距离。
case Cursor.N_RESIZE_CURSOR:if (!(h - dy < 50)) {setBounds(x, y + dy, w, h - dy);resize();}break;
对于所有大小调整,我们确保该组件不小于 50px。 否则,我们可以将其减小到最终将其隐藏的程度。 setBounds()方法重新定位组件并调整其大小。

图:可调整大小的组件
在 Java Swing 教程的这一部分中,我们创建了一个可调整大小的组件。
