如何解决重绘后 JComponents 移动到原点 计划的意图:结构:
类似于 JButton showing up in the wrong spot after repaint is called,但对该问题的回答仅说明他没有使用布局管理器,而我正在使用布局管理器(很差),所以不幸的是,它并没有真正帮助。
详情
计划的意图:
要显示 1920x1080 网格的预览(缩小到 640x480 以节省空间),随着您更改每个正方形的高度、每个正方形的宽度以及它将具有的列数进行拉伸和收缩。 (您首先指定要在网格中的总方块数,因此程序会推断出行数。)
结构:
- 一个顶级 JFrame。
- 包含两个 JPanel:Grid 和侧边栏,使用 BorderLayout 将它们对齐到东西两侧。
- 侧边栏是一个 JPanel,包含 Y 轴对齐的 BoxLayout 中的所有 JComponent。
- Grid 扩展了 JComponent,并使用 Graphics.drawLine() 来绘制网格。
- 边栏中的每个组件在更改网格时都会调用 Grid.repaint()。
Current UI,with the two main JPanels outlined in red.
问题
每当我更改任何组件并因此调用 Grid.repaint() 时:
我的尝试
- 将 repaint() 区域更改为仅覆盖网格的矩形,
- 检查文档是否有任何相关信息,
- 谷歌,
- 问你们。
代码
Reprex:(简化为“按按钮重现”,同时仍保留潜在问题区域的本质。)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BuildGridGUI2
{
static final int WIDTH_MIN = 100;
static final int WIDTH_MAX = 2000;
static final int WIDTH_INIT = 520;
static final int HEIGHT_MIN = 100;
static final int HEIGHT_MAX = 2000;
static final int HEIGHT_INIT = 300;
int widthOfFrames = 255;
int heightOfFrames = 255;
int numCols = 3;
int numFrames = 1;
private final JComponent makeUI()
{
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
Grid g = new Grid();
JComponent j = makeSideMenu(g);
g.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.red),g.getBorder()));
j.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.red),j.getBorder()));
p.add(j,BorderLayout.EAST);
p.add(g,BorderLayout.WEST);
return p;
}
private final JComponent makeSideMenu(Grid grid)
{
JPanel p = new JPanel();
JLabel numColsSelectorLabel = new JLabel("Frames Per Column");
JSpinner numColsSelectorField = new JSpinner();
JLabel widthLabel = new JLabel("Width per Frame (Pixels)");
JSpinner widthSpinner = new JSpinner();
JSlider widthSlider = new JSlider(WIDTH_MIN,WIDTH_MAX,WIDTH_INIT);
JLabel heightLabel = new JLabel("Height per Frame (Pixels)");
JSpinner heightSpinner = new JSpinner();
JSlider heightSlider = new JSlider(BuildGridGUI2.HEIGHT_MIN,BuildGridGUI2.HEIGHT_MAX,BuildGridGUI2.HEIGHT_INIT);
JButton confirmButton = new JButton("Confirm");
numColsSelectorField.setEditor(new JSpinner.NumberEditor(numColsSelectorField));
numColsSelectorField.setMaximumSize(numColsSelectorField.getPreferredSize());
widthSlider.setMajorTickSpacing(300);
widthSlider.setMinorTickSpacing(20);
widthSlider.setPaintTicks(true);
widthSlider.setPaintLabels(true);
widthSpinner.setEditor(new JSpinner.NumberEditor(widthSpinner));
widthSpinner.setPreferredSize(new Dimension(70,30));
widthSpinner.setMaximumSize(widthSpinner.getPreferredSize());
heightSlider.setMajorTickSpacing(300);
heightSlider.setMinorTickSpacing(20);
heightSlider.setPaintTicks(true);
heightSlider.setPaintLabels(true);
heightSpinner.setEditor(new JSpinner.NumberEditor(heightSpinner));
heightSpinner.setPreferredSize(new Dimension(70,30));
heightSpinner.setMaximumSize(heightSpinner.getPreferredSize());
confirmButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e)
{
widthOfFrames = 200;
grid.refresh();
}
});
p.setLayout(new BoxLayout(p,BoxLayout.Y_AXIS));
p.setPreferredSize(new Dimension(300,480));
p.setSize(new Dimension(300,480));
numColsSelectorLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
numColsSelectorField.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
confirmButton.setAlignmentX(Component.CENTER_ALIGNMENT);
widthLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
heightLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
p.add(Box.createRigidArea(new Dimension(300,30)));
p.add(numColsSelectorLabel);
p.add(Box.createRigidArea(new Dimension(300,5)));
p.add(numColsSelectorField);
p.add(Box.createRigidArea(new Dimension(300,25)));
p.add(widthLabel);
p.add(Box.createRigidArea(new Dimension(300,3)));
p.add(widthSpinner);
p.add(widthSlider);
p.add(Box.createRigidArea(new Dimension(300,25)));
p.add(heightLabel);
p.add(Box.createRigidArea(new Dimension(300,3)));
p.add(heightSpinner);
p.add(heightSlider);
p.add(Box.createRigidArea(new Dimension(300,45)));
p.add(confirmButton);
return p;
}
private static void createAndShowGUI() {
BuildGridGUI2 b = new BuildGridGUI2();
JFrame mainFrame = new JFrame("Grid Builder");
mainFrame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(940,480);
mainFrame.getContentPane().add(b.makeUI());
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokelater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
class Grid extends JComponent
{
static final int CANVAS_WIDTH = 640;
static final int CANVAS_HEIGHT = 480;
private final double conversionRatio = 4.0/9.0; // 1080p scaled down to 480p preview
private int squareHeight,squareWidth,numColumns,numRows;
public Grid()
{
setopaque(true);
setSize(CANVAS_WIDTH,CANVAS_HEIGHT); // trying to get the size to work properly.
setPreferredSize(new Dimension(CANVAS_WIDTH,CANVAS_HEIGHT));
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(CANVAS_WIDTH,CANVAS_HEIGHT);
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
updateVars();
int k;
for (k = 0; k < numColumns; k++)
{
// @param: (x1,y1),(x2,y2)
g.drawLine(k * squareWidth,k * squareWidth,CANVAS_HEIGHT);
}
for (k = 0; k < numRows; k++)
{
g.drawLine(0,k * squareHeight,CANVAS_WIDTH,k * squareHeight);
}
}
public void refresh()
{
// Attempting to set the repaint area to only the grid region
// because CTRL+F is free and parameters are not
repaint(0,CANVAS_HEIGHT);
}
public void updateVars()
{
this.squareWidth = (int)(
(double)BuildGridGUI2.this.widthOfFrames
*
conversionRatio);
this.squareHeight = (int)(
(double)BuildGridGUI2.this.heightOfFrames
*
conversionRatio);
this.numColumns = BuildGridGUI2.this.numCols;
this.numRows = (int)(
(
(double)BuildGridGUI2.this.numFrames
/
numColumns
)
+ 0.5);
}
}
}
Actual Source Code(并非严格相关,但如果您对 code review 感兴趣,那么我很想学习更好的编码约定。)
谢谢!
解决方法
我对您发布的代码进行了一些更改。
- 我更改了类
Grid
,使其扩展JPanel
而不是JComponent
,因为自定义绘画通常是在JPanel
上完成的。 - 我向类
grid
添加了一个类型为Grid
的实例成员变量BuildGridGUI2
,而不是创建一个并将其作为参数发送给方法makeSideMenu
。
这是经过我修改后的代码(以及我喜欢的编码风格)。它只是解决了您报告的问题,仅此而已。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BuildGridGUI2 {
static final int WIDTH_MIN = 100;
static final int WIDTH_MAX = 2000;
static final int WIDTH_INIT = 520;
static final int HEIGHT_MIN = 100;
static final int HEIGHT_MAX = 2000;
static final int HEIGHT_INIT = 300;
int widthOfFrames = 255;
int heightOfFrames = 255;
int numCols = 3;
int numFrames = 1;
Grid grid;
private final JComponent makeUI() {
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
grid = new Grid();
JComponent j = makeSideMenu();
grid.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.red),grid.getBorder()));
j.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.red),j.getBorder()));
p.add(j,BorderLayout.EAST);
p.add(grid,BorderLayout.WEST);
return p;
}
private final JComponent makeSideMenu() {
JPanel p = new JPanel();
JLabel numColsSelectorLabel = new JLabel("Frames Per Column");
JSpinner numColsSelectorField = new JSpinner();
JLabel widthLabel = new JLabel("Width per Frame (Pixels)");
JSpinner widthSpinner = new JSpinner();
JSlider widthSlider = new JSlider(WIDTH_MIN,WIDTH_MAX,WIDTH_INIT);
JLabel heightLabel = new JLabel("Height per Frame (Pixels)");
JSpinner heightSpinner = new JSpinner();
JSlider heightSlider = new JSlider(BuildGridGUI2.HEIGHT_MIN,BuildGridGUI2.HEIGHT_MAX,BuildGridGUI2.HEIGHT_INIT);
JButton confirmButton = new JButton("Confirm");
numColsSelectorField.setEditor(new JSpinner.NumberEditor(numColsSelectorField));
numColsSelectorField.setMaximumSize(numColsSelectorField.getPreferredSize());
widthSlider.setMajorTickSpacing(300);
widthSlider.setMinorTickSpacing(20);
widthSlider.setPaintTicks(true);
widthSlider.setPaintLabels(true);
widthSpinner.setEditor(new JSpinner.NumberEditor(widthSpinner));
widthSpinner.setPreferredSize(new Dimension(70,30));
widthSpinner.setMaximumSize(widthSpinner.getPreferredSize());
heightSlider.setMajorTickSpacing(300);
heightSlider.setMinorTickSpacing(20);
heightSlider.setPaintTicks(true);
heightSlider.setPaintLabels(true);
heightSpinner.setEditor(new JSpinner.NumberEditor(heightSpinner));
heightSpinner.setPreferredSize(new Dimension(70,30));
heightSpinner.setMaximumSize(heightSpinner.getPreferredSize());
confirmButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
widthOfFrames = 200;
grid.refresh();
}
});
p.setLayout(new BoxLayout(p,BoxLayout.Y_AXIS));
p.setPreferredSize(new Dimension(300,480));
p.setSize(new Dimension(300,480));
numColsSelectorLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
numColsSelectorField.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
confirmButton.setAlignmentX(Component.CENTER_ALIGNMENT);
widthLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
heightLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
p.add(Box.createRigidArea(new Dimension(300,30)));
p.add(numColsSelectorLabel);
p.add(Box.createRigidArea(new Dimension(300,5)));
p.add(numColsSelectorField);
p.add(Box.createRigidArea(new Dimension(300,25)));
p.add(widthLabel);
p.add(Box.createRigidArea(new Dimension(300,3)));
p.add(widthSpinner);
p.add(widthSlider);
p.add(Box.createRigidArea(new Dimension(300,25)));
p.add(heightLabel);
p.add(Box.createRigidArea(new Dimension(300,3)));
p.add(heightSpinner);
p.add(heightSlider);
p.add(Box.createRigidArea(new Dimension(300,45)));
p.add(confirmButton);
return p;
}
private static void createAndShowGUI() {
BuildGridGUI2 b = new BuildGridGUI2();
JFrame mainFrame = new JFrame("Grid Builder");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(940,480);
mainFrame.getContentPane().add(b.makeUI());
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
class Grid extends JPanel {
static final int CANVAS_WIDTH = 640;
static final int CANVAS_HEIGHT = 480;
private final double conversionRatio = 4.0 / 9.0; // 1080p scaled down to 480p preview
private int squareHeight,squareWidth,numColumns,numRows;
public Grid() {
setOpaque(true);
setSize(CANVAS_WIDTH,CANVAS_HEIGHT); // trying to get the size to work properly.
setPreferredSize(new Dimension(CANVAS_WIDTH,CANVAS_HEIGHT));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(CANVAS_WIDTH,CANVAS_HEIGHT);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
updateVars();
int k;
for (k = 0; k < numColumns; k++) {
// @param: (x1,y1),(x2,y2)
g.drawLine(k * squareWidth,k * squareWidth,CANVAS_HEIGHT);
}
for (k = 0; k < numRows; k++) {
g.drawLine(0,k * squareHeight,CANVAS_WIDTH,k * squareHeight);
}
}
public void refresh() {
// Attempting to set the repaint area to only the grid region
// because CTRL+F is free and parameters are not
repaint(0,CANVAS_HEIGHT);
}
public void updateVars() {
this.squareWidth = (int) ((double) BuildGridGUI2.this.widthOfFrames * conversionRatio);
this.squareHeight = (int) ((double) BuildGridGUI2.this.heightOfFrames * conversionRatio);
this.numColumns = BuildGridGUI2.this.numCols;
this.numRows = (int) (((double) BuildGridGUI2.this.numFrames / numColumns) + 0.5);
}
}
}
一个提示:尝试使用 JComponent
而不是 JComponent
的特定子类。例如,我建议将方法 makeSideMenu()
的返回类型更改为 JPanel
而不是 JComponent
,因为该方法实际上返回的是 JPanel
。
所有侧边栏组件都在左上角绘制,同时仍然在侧边栏上显示/运行;
<!-- <span class="backgroundImg"> -->
<header>
<h1>Maurice Rogers</h1>
<nav>
<ul class="navbar">
<li class="nav-link"><a class="nav-link" href="index.html">Bio</a></li>
<li class="nav-link"><a class="nav-link" href="technical-resume.html">Technical Resume</a>.
</li>
<li class="nav-link"><a class="nav-link" href="my-animations.html">Animations</a></li>
<li class="nav-link"><a class="nav-link" href="me-surfing.html">Surfing</a></li>
<li class="nav-link"><a class="nav-link" href="my-art.html">Art</a></li>
</ul>
</nav>
</header>
<main>
<br/>
<h2>My Animations</h2>
<br/>
<!-- <iframe width="560" height="315" src="https://www.youtube.com/embedvideoseries?
list=PL9xPrB7vjr6kZYms6RFtpyPGmZqvNj7MS" frameborder="0" allow="accelerometer; autoplay;
clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
enablejsapi="1"></iframe>
<p>My animations took many hours of hard work to create. Enjoy.</p> -->
</main>
<footer>
<nav>
<a class="ftr-nav-link" href="my-animations.html">Back to top</a>
</nav>
<p>© 2021 Maurice Rogers</p>
</footer>
<!-- </span> -->
是所有 Swing 组件都从其扩展的抽象类。它没有默认的绘画逻辑。
因此调用 JComponent
并没有真正做任何事情。
特别是在进行自定义绘制之前不会清除组件的背景。这将导致您看到的绘画伪像。
任何时候你扩展 super.paintComponent(...)
你的逻辑应该是这样的:
JComponent
注意:这就是 Abra 提到的许多人为了简单的自定义绘画而覆盖 protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// clear background
g.setColor( getBackground() );
g.fillRect(0,getWidth(),getHeight());
// do custom painting
g.setColor( getForeground() );
...
}
的原因。 JPanel
的 paintComponent(...)
方法将默认清除背景。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。