Java Swing:在装饰器模式中复用父JPanel的Graphics2D引用
解决Java Swing中装饰器模式传递Graphics对象的方案
1. 定义统一图表渲染接口(装饰器模式核心)
先创建一个接口,让基础图表和装饰器类都实现它,接口方法直接接收Graphics对象与绘制区域,这是传递Graphics的核心入口:
public interface Chart { // 传入Graphics对象与当前绘制区域的边界 void draw(Graphics g, Rectangle drawArea); }
2. 实现基础折线图组件(被装饰者)
这是最基础的简单折线图,实现Chart接口,在draw方法中使用传入的Graphics完成折线绘制:
import java.awt.*; import java.awt.geom.*; public class LineChart implements Chart { @Override public void draw(Graphics g, Rectangle drawArea) { // 创建Graphics副本,避免修改原始对象的全局状态 Graphics2D g2d = (Graphics2D) g.create(); // 设置折线样式 g2d.setColor(Color.BLUE); g2d.setStroke(new BasicStroke(2)); // 基于绘制区域计算折线坐标(示例数据) int[] xPoints = {drawArea.x + 50, drawArea.x + 150, drawArea.x + 250, drawArea.x + 350}; int[] yPoints = {drawArea.y + 150, drawArea.y + 80, drawArea.y + 120, drawArea.y + 50}; g2d.drawPolyline(xPoints, yPoints, 4); // 释放副本资源 g2d.dispose(); } }
3. 实现柱状图装饰器(装饰者)
装饰器持有一个Chart类型的对象(可以是基础组件或其他装饰器),在draw方法中先调用被装饰对象的绘制逻辑,再用同一个Graphics对象添加柱状图:
import java.awt.*; public class BarChartDecorator implements Chart { private Chart decoratedChart; public BarChartDecorator(Chart decoratedChart) { this.decoratedChart = decoratedChart; } @Override public void draw(Graphics g, Rectangle drawArea) { // 先绘制被装饰的基础图表(比如折线图) decoratedChart.draw(g, drawArea); // 复用同一个Graphics对象绘制柱状图 Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(new Color(255, 165, 0, 120)); // 半透明橙色 // 基于绘制区域计算柱子坐标(示例数据) int barWidth = 40; int[] barHeights = {100, 70, 90, 120}; for (int i = 0; i < barHeights.length; i++) { int x = drawArea.x + 30 + i * 80; int y = drawArea.y + drawArea.height - barHeights[i] - 20; g2d.fillRect(x, y, barWidth, barHeights[i]); } g2d.dispose(); } }
4. 构建分割式JPanel,传递Graphics到装饰器
自定义JPanel,在paintComponent方法中将面板划分为上下均等区域,分别调用基础图表与装饰后图表的draw方法,传入对应区域的Graphics和边界:
import java.awt.*; import javax.swing.*; public class ChartPanel extends JPanel { private Chart topLineChart; private Chart bottomCombinedChart; public ChartPanel() { // 初始化顶部简单折线图 topLineChart = new LineChart(); // 用柱状图装饰器包装基础折线图,得到底部组合图表 bottomCombinedChart = new BarChartDecorator(new LineChart()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 计算上下两个均等区域的边界 int halfHeight = getHeight() / 2; Rectangle topArea = new Rectangle(0, 0, getWidth(), halfHeight); Rectangle bottomArea = new Rectangle(0, halfHeight, getWidth(), halfHeight); // 绘制顶部折线图 topLineChart.draw(g, topArea); // 绘制底部组合图表(折线+柱状) bottomCombinedChart.draw(g, bottomArea); // 绘制分割线区分区域 g.setColor(Color.GRAY); g.drawLine(0, halfHeight, getWidth(), halfHeight); } }
5. 测试主程序
将自定义JPanel嵌入窗口运行:
import javax.swing.*; public class ChartApplication { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame("装饰器模式图表示例"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500, 600); frame.add(new ChartPanel()); frame.setVisible(true); }); } }
关键细节说明
- Graphics传递逻辑:通过
Chart接口的draw方法直接传递对象,装饰器复用同一个Graphics完成附加绘制,保证渲染上下文一致。 - 状态隔离:每次绘制时创建
Graphics副本,绘制完成后释放,避免修改原始对象的全局状态(如颜色、画笔样式)影响其他组件。 - 区域划分:在
paintComponent中动态计算上下区域边界,确保两个图表各占一半空间,可根据需求调整比例。
内容的提问来源于stack exchange,提问作者Vaishu
相关产品推荐
相关产品推荐

