JSplitPane与paintComponent冲突:自定义City组件圆形不显示求助
嘿,我一眼就发现问题所在了——你在使用**绝对布局(null布局)**的时候,没有给你的City组件指定尺寸和位置!
问题根源解释
你给左右两侧的面板lp和rp都用了new JPanel(null),也就是放弃了Swing的布局管理器,采用手动定位。这种情况下,所有添加到面板的组件都必须通过setBounds(x, y, width, height)手动设置位置和大小,否则组件的默认尺寸是0×0,自然就看不到了。
你提到把City直接加到JFrame时能显示,那是因为JFrame的默认内容面板用的是BorderLayout,它会自动拉伸组件填满可用空间,所以City能被渲染出来,但这时候你替换了原来的JSplitPane,导致其他组件被覆盖消失。
方案1:快速修复——给City组件设置边界
在主类中实例化City之后,直接添加一行代码指定它的位置和大小:
City test = new City(0, 0); // 这里的坐标改为相对于右侧面板rp的位置,比如(0,0) rp.add(test); test.setBounds(301, 301, 10, 10); // 位置(x,y) + 宽高(width,height)
注意:这里的xPos和yPos要和setBounds的位置对应,或者调整City类里的绘制逻辑,让圆形绘制在组件自身的坐标范围内(比如把xPos和yPos设为相对于组件左上角的偏移,而不是相对于整个窗口)。
方案2:推荐方案——放弃绝对布局,使用Swing布局管理器
绝对布局在Swing中是非常不推荐的,它无法适配窗口大小变化、不同平台的UI差异,还容易出现组件错位的问题。我们可以改用布局管理器来解决这个问题:
步骤1:修改右侧面板的布局
把右侧面板rp的初始化改为使用BorderLayout:
JPanel rp = new JPanel(new BorderLayout()); // 替换null布局
步骤2:调整City类的绘制逻辑
让圆形可以根据组件的尺寸自适应位置(比如画在组件中心):
public class City extends JPanel{ // 可以保留xPos和yPos,或者改为动态计算位置 int xPos, yPos; City(int x, int y){ xPos = x; yPos = y; } @Override public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(Color.BLACK); // 如果要固定位置,就用xPos和yPos;如果要自适应,就用组件的宽高计算 // 示例:画在组件中心 int centerX = getWidth() / 2; int centerY = getHeight() / 2; g.fillOval(centerX - 5, centerY - 5, 10, 10); } }
步骤3:添加City到右侧面板
用布局管理器的方式添加组件,不需要手动设置边界:
City test = new City(0, 0); rp.add(test, BorderLayout.CENTER); // 让City填满右侧面板
另外,左侧面板lp也建议改用布局管理器(比如GridBagLayout或BoxLayout)来排列标签、Spinner和按钮,这样组件的布局会更稳定、更符合Swing的设计规范。
修复后的完整主类示例(方案2)
import java.awt.*; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JSplitPane; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; public class TSP{ public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new TSP(); } }); } TSP(){ JLabel instructions = new JLabel("Enter the number of cities: "); SpinnerNumberModel numMod = new SpinnerNumberModel(2, 2, 10, 1); JSpinner numOfCities = new JSpinner(numMod); JButton start = new JButton("Start Simulation"); // 左侧面板改用GridBagLayout来排列组件(替代null布局) JPanel lp = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(10, 10, 10, 10); gbc.gridx = 0; gbc.gridy = 0; lp.add(instructions, gbc); gbc.gridx = 1; lp.add(numOfCities, gbc); gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 2; lp.add(start, gbc); // 右侧面板用BorderLayout JPanel rp = new JPanel(new BorderLayout()); JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); sp.setDividerLocation(300); sp.setLeftComponent(lp); sp.setRightComponent(rp); sp.setEnabled(false); JFrame frame = new JFrame("Travelling Salesperson"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 用常量替代魔数3 frame.setSize(1100,600); frame.setResizable(false); // 添加City到右侧面板 City test = new City(0, 0); rp.add(test, BorderLayout.CENTER); frame.add(sp); frame.setVisible(true); } }
这样修改后,圆形会稳定显示在JSplitPane的右侧面板中,同时左侧的组件也能正常显示,布局也更健壮。
内容的提问来源于stack exchange,提问作者SteamedBun

