【问题标题】:paintComponent executed twice and drawing failurepaintComponent 执行了两次,绘制失败
【发布时间】:2018-11-29 12:33:11
【问题描述】:

我正在做一个小小的个人项目来提高我的 Java 技能。

我在那里做的是创建 3 个 JPanel(一个全局的,2 个全局的)。

在其中一个(右侧)中,我想绘制积木。这些块具有随机的高度、宽度和唯一的 ID。

在我的一个 java 类中,我创建了随机数量的块。然后,我正在做一个绘制这些块的函数,这就是问题所在。 首先,paintComponent 函数执行了两次,我只希望它执行一次。

其次,我的积木……画得不是很好……这是一张 20 块积木画到我的面板中的图片(为了调试,我放了固定数量的积木)。我的窗户是 900x700。

这是我的paintComponent函数,我试图看看我在哪里做错了,但是 我把头发扯掉了。。

【问题讨论】:

  • “我的积木……画得不是很好”——预期的结果是什么?您能否提供您期望的示例图像以及由您的代码生成的相应图像,其中块是可区分的(例如通过不同的颜色)?
  • "paintComponent 函数执行了两次,我只希望它执行一次" - 组件更新/重绘的频率由 ui 框架决定,因此您不应仅依赖于这种情况一次。而是以一种与调用一次或 100 次无关的方式编写代码 - 这意味着将模型(可能包含 listeBlocPlacePanelBloc 等)分开并从绘图代码中更新它(使用模型)。
  • UI 会在它认为有必要时重新绘制组件。当您调整窗口大小、移动它、以编程方式使其无效等时,可能会发生这种情况 - 主要教训是:不要依赖重绘的次数!这必然会导致问题,而且无论如何都是不好的风格。具有副作用的绘制方法(即,它们会做除绘制之外的其他事情(例如您将元素添加到非本地列表)会导致比它们解决的问题更多的问题。
  • 您需要将处理列表添加等与绘画分开。也就是说,为您的面板提供一些模型(可以像面板内的列表一样简单)并在一些直接参与绘画的代码中添加块。然后在您的绘画方法中,您只需遍历该列表,但您不会更改它。 - 在您的代码中,似乎listeBlocPasEncorePlacePanel 可能是该列表,尽管名称会产生误导。您不会从该列表中删除块,而是每次都对其进行迭代(并且您不再需要listeBlocPlacePanelBloc)。
  • 在绘画时,您不应在该列表中添加或删除任何内容。如果列表发生更改,则使面板无效以使 UI 重新绘制它 - 但这应该发生在绘制代码之外(包括 paintXxx 方法和其他方法)。这意味着您的绘画代码仅从列表中读取 - 并且为了防止在绘画时列表更改时可能发生的视觉伪影或ConcurrentModificationExceptions,您可能希望使用 2 个列表:您从中读取的一个一个已更改且更改完成后两者都被交换(也称为“双缓冲”)。

标签: java eclipse swing


【解决方案1】:

您的目标图片表明您基本上想要按行渲染图像,即如果图像不适合当前行,您将开始一个新的。因此,您需要跟踪 x 和 y 的偏移量以及该行中最高块的高度。这意味着对于每个块,您都要做这样的事情(未经测试,但如果不是 100% 正确,稍微调试一下应该会有所帮助):

//maximum width of a line
int maxWidth = 300;

//Space in pixels between the border as well as the blocks
int paddingX = 5;
int paddingY = 5;

//the offset for the next block
int offsetX = paddingX;
int offsetY = paddingY;

int currentLargestHeight = 0;

for( Block block : blocks ) {
  //if the block doesn't fit into the "line" start a new one
  //we assume an empty line always can take at least one block
  if( block.getWidth() > (maxWidth - offsetX - paddingX) ) {
    //advance down
    offsetY += currentLargestHeight + paddingY;

    //we have a new line so the current largest height is 0 again
    currentLargestHeight = 0;

    //start left again
    offsetX = paddingX;
  }

  //render the block
  g2.fillRect(offsetX, offsetY, block.getWidth(), block.getHeight());

  offsetX += block.getWidth() + paddingX;
  if( block.getHeight() > currentLargestHeight ) {
    currentLargestHeight = block.getHeight();
  }
}

【讨论】:

  • 嗯我明白你刚刚做了什么!但是“对于每个”,我需要放一个 List 而不是 blocks 对吗?
  • 我放了一些 println 来检查它是否仍然执行两次paintComponent,它仍然执行此操作,有什么线索吗?
  • 正如我所说,可能有多种原因,但这并不重要。许多框架在每个“帧”中多次重绘组件(您可能将其定义为只需要一次重绘的工作单元)。这并不少见,也是模型更改与渲染代码分开的另一个原因。
  • @A.Arthur 可能应该进入一个单独的问题并且为您调试代码超出了 SO 的范围。使用调试器逐步检查您的逻辑,并查看您正在生成的值或面板在绘制时的尺寸(调整它的大小会导致重新绘制)。
猜你喜欢
  • 2011-06-15
  • 1970-01-01
  • 2011-06-16
  • 2017-05-11
  • 1970-01-01
  • 1970-01-01
  • 2013-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多