- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我有一个在 32 位 Windows 2008 Server 上运行的 Java (Swing) 应用程序,它需要将其输出呈现为离屏图像(然后由另一个 C++ 应用程序拾取以在其他地方呈现)。大多数组件都正确呈现,除了在刚刚失去焦点的组件被另一个组件遮挡的奇怪情况下,例如有两个 JComboBoxes 彼此靠近的情况下,如果用户与较低的 JComboBoxes 交互,然后单击上面的下拉框与另一个框重叠。
在这种情况下,失去焦点的组件在遮挡它的组件之后渲染,因此出现在输出的顶部。它在正常的 Java 显示中正确呈现(在主显示器上全屏运行),并且尝试更改有问题的组件的层没有帮助。
我正在使用自定义 RepaintManager 将组件绘制到屏幕外图像,我认为问题在于为每个相关组件调用 addDirtyRegion() 的顺序,但我想不出识别这种特定状态何时发生以防止它发生的好方法。对其进行黑客攻击,使刚刚失去焦点的对象不被重新绘制可以解决问题,但显然会导致更大的问题,即在所有其他正常情况下都不会重新绘制。
有没有办法以编程方式识别此状态,或重新排序以使其不会发生?
非常感谢,
尼克
[编辑]添加了一些代码作为示例:
重绘管理器和相关类:
class NativeObject {
private long nativeAddress = -1;
protected void setNativeAddress(long address) {
if ( nativeAddress != -1 ) {
throw new IllegalStateException("native address already set for " + this);
}
this.nativeAddress = address;
NativeObjectManager.getInstance().registerNativeObject(this, nativeAddress);
}
}
public class MemoryMappedFile extends NativeObject {
private ByteBuffer buffer;
public MemoryMappedFile(String name, int size)
{
setNativeAddress(create(name, size));
buffer = getNativeBuffer();
}
private native long create(String name, int size);
private native ByteBuffer getNativeBuffer();
public native void lock();
public native void unlock();
public ByteBuffer getBuffer() {
return buffer;
}
}
private static class CustomRepaintManager extends RepaintManager{
class PaintLog {
Rectangle bounds;
Component component;
Window window;
PaintLog(int x, int y, int w, int h, Component c) {
bounds = new Rectangle(x, y, w, h);
this.component = c;
}
PaintLog(int x, int y, int w, int h, Window win) {
bounds = new Rectangle(x, y, w, h);
this.window= win;
}
}
private MemoryMappedFile memoryMappedFile;
private BufferedImage offscreenImage;
private List<PaintLog> regions = new LinkedList<PaintLog>();
private final Component contentPane;
private Component lastFocusOwner;
private Runnable sharedMemoryUpdater;
private final IMetadataSource metadataSource;
private Graphics2D offscreenGraphics;
private Rectangle offscreenBounds = new Rectangle();
private Rectangle repaintBounds = new Rectangle();
public CustomRepaintManager(Component contentPane, IMetadataSource metadataSource) {
this.contentPane = contentPane;
this.metadataSource = metadataSource;
offscreenBounds = new Rectangle(0, 0, 1920, 1080);
memoryMappedFile = new MemoryMappedFile("SystemConfigImage", offscreenBounds.width * offscreenBounds.height * 3 + 1024);
offscreenImage = new BufferedImage(offscreenBounds.width, offscreenBounds.height, BufferedImage.TYPE_3BYTE_BGR);
offscreenGraphics = offscreenImage.createGraphics();
sharedMemoryUpdater = new Runnable(){
@Override
public void run()
{
updateSharedMemory();
}
};
}
private boolean getLocationRelativeToContentPane(Component c, Point screen) {
if(!c.isVisible()) {
return false;
}
if(c == contentPane) {
return true;
}
Container parent = c.getParent();
if(parent == null) {
System.out.println("can't get parent!");
return true;
}
if(!parent.isVisible()) {
return false;
}
while ( !parent.equals(contentPane)) {
screen.x += parent.getX();
screen.y += parent.getY();
parent = parent.getParent();
if(parent == null) {
System.out.println("can't get parent!");
return true;
}
if(!parent.isVisible()) {
return false;
}
}
return true;
}
protected void updateSharedMemory() {
if ( regions.isEmpty() ) return;
List<PaintLog> regionsCopy = new LinkedList<PaintLog>();
synchronized ( regions ) {
regionsCopy.addAll(regions);
regions.clear();
}
memoryMappedFile.lock();
ByteBuffer mappedBuffer = memoryMappedFile.getBuffer();
int imageDataSize = offscreenImage.getWidth() * offscreenImage.getHeight() * 3;
mappedBuffer.position(imageDataSize);
if ( mappedBuffer.getInt() == 0 ) {
repaintBounds.setBounds(0, 0, 0, 0);
} else {
repaintBounds.x = mappedBuffer.getInt();
repaintBounds.y = mappedBuffer.getInt();
repaintBounds.width = mappedBuffer.getInt();
repaintBounds.height = mappedBuffer.getInt();
}
for ( PaintLog region : regionsCopy ) {
if ( region.component != null && region.bounds.width > 0 && region.bounds.height > 0) {
Point regionLocation = new Point(region.bounds.x, region.bounds.y);
Point screenLocation = region.component.getLocation();
boolean isVisible = getLocationRelativeToContentPane(region.component, screenLocation);
if(!isVisible) {
continue;
}
if(region.bounds.x != 0 && screenLocation.x == 0 || region.bounds.y != 0 && screenLocation.y == 0){
region.bounds.width += region.bounds.x;
region.bounds.height += region.bounds.y;
}
Rectangle2D.intersect(region.bounds, offscreenBounds, region.bounds);
if ( repaintBounds.isEmpty() ){
repaintBounds.setBounds( screenLocation.x, screenLocation.y, region.bounds.width, region.bounds.height);
} else {
Rectangle2D.union(repaintBounds, new Rectangle(screenLocation.x, screenLocation.y, region.bounds.width, region.bounds.height), repaintBounds);
}
offscreenGraphics.translate(screenLocation.x, screenLocation.y);
region.component.paint(offscreenGraphics);
DataBufferByte byteBuffer = (DataBufferByte) offscreenImage.getData().getDataBuffer();
int srcIndex = (screenLocation.x + screenLocation.y * offscreenImage.getWidth()) * 3;
byte[] srcData = byteBuffer.getData();
int maxY = Math.min(screenLocation.y + region.bounds.height, offscreenImage.getHeight());
int regionLineSize = region.bounds.width * 3;
for (int y = screenLocation.y; y < maxY; ++y){
mappedBuffer.position(srcIndex);
if ( srcIndex + regionLineSize > srcData.length ) {
break;
}
if ( srcIndex + regionLineSize > mappedBuffer.capacity() ) {
break;
}
try {
mappedBuffer.put( srcData, srcIndex, regionLineSize);
}
catch ( IndexOutOfBoundsException e) {
break;
}
srcIndex += 3 * offscreenImage.getWidth();
}
offscreenGraphics.translate(-screenLocation.x, -screenLocation.y);
offscreenGraphics.setClip(null);
} else if ( region.window != null ){
repaintBounds.setBounds(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight() );
offscreenGraphics.setClip(repaintBounds);
contentPane.paint(offscreenGraphics);
DataBufferByte byteBuffer = (DataBufferByte) offscreenImage.getData().getDataBuffer();
mappedBuffer.position(0);
mappedBuffer.put(byteBuffer.getData());
}
}
mappedBuffer.position(imageDataSize);
mappedBuffer.putInt(repaintBounds.isEmpty() ? 0 : 1);
mappedBuffer.putInt(repaintBounds.x);
mappedBuffer.putInt(repaintBounds.y);
mappedBuffer.putInt(repaintBounds.width);
mappedBuffer.putInt(repaintBounds.height);
metadataSource.writeMetadata(mappedBuffer);
memoryMappedFile.unlock();
}
@Override
public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
super.addDirtyRegion(c, x, y, w, h);
synchronized ( regions ) {
regions.add(new PaintLog(x, y, w, h, c));
}
SwingUtilities.invokeLater(sharedMemoryUpdater);
}
@Override
public void addDirtyRegion(Window window, int x, int y, int w, int h) {
super.addDirtyRegion(window, x, y, w, h);
synchronized (regions) {
regions.add(new PaintLog(x, y, w, h, window));
}
SwingUtilities.invokeLater(sharedMemoryUpdater);
}
}
出现问题的小组:
private static class EncodingParametersPanel extends JPanel implements ActionListener
{
private JLabel label1 = new JLabel();
private JComboBox comboBox1 = new JComboBox();
private JLabel label2 = new JLabel();
private JComboBox comboBox2 = new JComboBox();
private JLabel label3 = new JLabel();
private JComboBox comboBox3 = new JComboBox();
private JButton setButton = new JButton();
public EncodingParametersPanel()
{
super(new BorderLayout());
JPanel contentPanel = new JPanel(new VerticalFlowLayout());
JPanel formatPanel = new JPanel(new VerticalFlowLayout());
sdiFormatPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder(), "Format"));
label1.setText("First Option:");
label2.setText("Second Option:");
label3.setText("Third OPtion:");
setButton.addActionListener(this);
formatPanel.add(label1);
formatPanel.add(comboBox1);
formatPanel.add(label2);
formatPanel.add(comboBox2);
formatPanel.add(label3);
formatPanel.add(comboBox3);
contentPanel.add(formatPanel);
contentPanel.add(setButton);
add(contentPanel);
}
}
使用此示例,如果用户与 comboBox2 交互,然后与 comboBox1 交互,comboBox1 的下拉菜单会与 comboBox2 重叠,但 comboBox2 会在其之上重绘。
最佳答案
我发现了一些可能有助于您所看到的东西。
在用于处理窗口重绘的updateSharedMemory
代码中,代码调用了contentPane.paint
。这是最有可能的罪魁祸首,因为 Window 可能不是您的 contentPane。 JPopupMenu 的代码(由 JComboBox 使用)可以选择将弹出窗口显示为重量级组件。因此,Window 可能是 JComboBox 之一的弹出窗口。
此外,sharedMemoryUpdater 被安排在 EDT 上,一旦事件队列为空,它就会运行。因此,调用 addDirtyRegion
和调用 updateSharedMemory
之间可能存在延迟。在 updateSharedMemory
中调用了 region.component.paint
。如果任何已排队的事件更改 component
,则绘制调用的实际结果可能会有所不同。
测试的一些建议:
像这样创建 sharedMemoryUpdater
:
private Runnable scheduled = null;
sharedMemoryUpdater = Runnable {
public void run() {
scheduled = null;
updateSharedMemory();
}
}
然后,在addDirtyRegion
if (scheduled == null) {
scheduled = sharedMemoryUpdater;
SwingUtilities.invokeLater(sharedMemoryUpdater);
}
这将减少 sharedMemoryUpdater
的调用次数(在我的测试中减少了 99%)。由于对 addDirtyRegion
的所有调用都应该在 EDT 上发生,因此您不需要在 scheduled
上进行同步,但添加不会有太大影响。
由于存在滞后,要处理的区域数量可能会变得非常大。在我的测试中,我看到它一度超过 400。
这些更改将减少操作区域列表所花费的时间,因为创建 1 个新列表比创建复制现有列表所需的所有条目更快。
private final Object regionLock = new Opject;
private List<PaintLog> regions = new LinkedList<PaintLog>();
// In addDirtyRegions()
synchronized(regionLock) {
regions.add(...);
}
// In updateSharedMemory()
List<PaintLog> regionsCopy;
List<PaintLog> tmp = new LinkedList<PaintLog>()
synchronized(regionLock) {
regionsCopy = regions;
regions = tmp;
}
关于java - 将 Swing 组件渲染到屏幕外缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2908418/
我试图弄清楚如何为聊天气泡制作外 Angular 圆形设计,以获得所需的结果: 我必须使用气泡作为不同背景的组件,没有相同和纯色,但有一些设计元素,所以气泡周围的空间必须是透明的: 我试过将元素添加为
我尝试了 display:table-cell 但它没有用。我怎样才能在div中显示这个词。现在它显示溢出了 div。我在我的网页上使用 CSS2。提前致谢。 Visit W3Schools
我有一个使用 CSS 隐藏在 View (对于移动设备)之外的菜单: #filter-column { position:absolute; left:-400px; } 当用户单击链
我想创建一个这样的问题行 http://imageshack.us/photo/my-images/200/questionh.png/ 此时我的html源是: question label
我要mock a class with Ruby . 如何编写处理样板代码的方法? 以下代码: module Mailgun end module Acani def self.mock_mail
请不要担心循环,但我的问题是关于这些关键字:outer、middle 和 inner。它们不是声明为实例变量,为什么IDE让代码编译?我在谷歌上搜索了一下,这是java标签吗? Java中的某种关键字
我有一个数据框(df),看起来像, Id Name Activity. 1 ABC a;sldkj kkkdk 2 two
Elasticsearch内存中有哪些东西可以使搜索如此快速? 是所有json本身都在内存中,还是仅倒排索引和映射将在内存中24 * 7? 最佳答案 这是一个很好的问题,然后简而言之就是: 不仅仅是数
我正在尝试添加用户在用户界面上选择的值。对于数据库中的特定列,我已经与数据库建立了连接,当我按“保存”时,新的 id 会添加到数据库中,控制台中不会显示任何错误,但我要提交的值不会放入数据库,我怎样才
我不确定这个问题是否应该涉及电子领域,但由于它是关于编程的,所以我在这里问了它。 我正在制作一个数字时钟,使用由移位寄存器供电的 LED,而不是 7 段显示器。无论如何,当使用 CCS 编译代码时,我
我希望用户在 div 中选择文本 (html)。然而,这样做会在浏览器中显示选择背景,也在 div 之外。 我可以用(参见 http://jsfiddle.net/lborgman/aWbgT/)来防
我有以下 Razor View @{ ViewBag.Title = "UserCost"; }
我使用 KineticJS 和 D3.js 制作了以下内容。当用户将鼠标悬停在其中一个点上时,我使用 KineticJS 让我弹出工具提示。但是,由于 Canvas 的边界,工具提示似乎被切断了。有没
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 2 年前。 Improve this qu
我正在使用 primefaces 学习 Java Web 和 jsf。 我的项目当前只有一个index.xhtml 文件,当我访问localhost:8080/appname/时,index.xhtm
我是 ios 新手。 我有一个 View ,其中我使用 Quarts 核心绘制了一个圆圈。 我在该圆圈中放置了一个 UIButton,并赋予了拖放该按钮的功能。 现在我想要限制按钮不能被拖出那个圆圈区
这个问题已经有答案了: How to add two strings as if they were numbers? [duplicate] (20 个回答) How to force JS to
我正在创建简单的文本从右侧滑动到页面的 css 动画。我正在使用 jQuery 通过向元素添加一个类来触发动画。但是起始位置必须在视口(viewport)之外,这会触发底部滚动条出现。如何预防? 这是
我编写了一个简单的代码来评估一段代码并将输出写入文件。这样它减少了我的一些,因为我需要很多很多文件,每一行都包含返回值! 无论如何,我正在使用的代码是: #!/usr/bin/ruby -w def
所以我试图在我的一款游戏中加入一个非常基本的“手电筒”式的东西。 我让它工作的方式是在我的游戏屏幕顶部有一个层,这个层会绘制一个黑色矩形,不透明度约为 80%,在我的游戏场景顶部创建黑暗的外观。 cc
我是一名优秀的程序员,十分优秀!