原文: 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() {
@Override
public 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() {
@Override
public 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;
}
@Override
public Insets getBorderInsets(Component component) {
return new Insets(dist, dist, dist, dist);
}
@Override
public boolean isBorderOpaque() {
return false;
}
@Override
public 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() {
@Override
public void mouseMoved(MouseEvent me) {
if (hasFocus()) {
var resizableBorder = (ResizableBorder) getBorder();
setCursor(Cursor.getPredefinedCursor(resizableBorder.getCursor(me)));
}
}
@Override
public void mouseExited(MouseEvent mouseEvent) {
setCursor(Cursor.getDefaultCursor());
}
private int cursor;
private Point startPos = null;
@Override
public void mousePressed(MouseEvent me) {
var resizableBorder = (ResizableBorder) getBorder();
cursor = resizableBorder.getCursor(me);
startPos = me.getPoint();
requestFocus();
repaint();
}
@Override
public 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));
}
}
@Override
public void mouseReleased(MouseEvent mouseEvent) {
startPos = null;
}
};
}
Resizable
类表示正在调整大小并在窗口上移动的组件。
private void resize() {
if (getParent() != null) {
getParent().revalidate();
}
}
调整组件大小后,将调用resize()
方法。 revalidate()
方法导致重画组件。
MouseInputListener resizeListener = new MouseInputAdapter() {
@Override
public void mouseMoved(MouseEvent me) {
if (hasFocus()) {
var border = (ResizableBorder) getBorder();
setCursor(Cursor.getPredefinedCursor(border.getCursor(me)));
}
}
...
}
当我们将光标悬停在抓取点上时,我们将更改光标类型。 仅当组件具有焦点时,光标类型才会更改。
@Override
public 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 教程的这一部分中,我们创建了一个可调整大小的组件。