我有一个在32位Windows 2008 Server上运行的Java(Swing)应用程序,它需要将其输出呈现为屏幕外图像(然后由另一个C++应用程序拾取以便在其他地方呈现).大多数组件都能正确呈现,除非在奇怪的情况下,刚刚失去焦点的组件被另一个组件遮挡,例如,如果有两个JComboBox彼此接近,如果用户与较低的组件交互,则点击上面一个,所以它的下拉与另一个盒子重叠.
在这种情况下,失去焦点的组件在遮挡它之后呈现,因此出现在输出的顶部.它在正常的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);
}
}
Run Code Online (Sandbox Code Playgroud)
有问题的小组:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
使用此示例,如果用户与comboBox2交互,则使用comboBox1,comboBox1的下拉重叠comboBox2,但comboBox2在其上重绘.
我发现了一些可能有助于您所看到的内容的事情。
在updateSharedMemory
处理窗口重绘的代码中,代码调用contentPane.paint
. 这是最有可能的罪魁祸首,因为窗口可能不是您的 contentPane。JPopupMenu(由 JComboBox 使用)的代码可以选择将弹出窗口呈现为重量级组件。因此,该窗口可能是 JComboBox 之一的弹出窗口。
此外,sharedMemoryUpdater 被安排在 EDT 上,一旦事件队列为空,它将运行。addDirtyRegion
因此,调用时间和updateSharedMemory
调用时间之间可能存在延迟。里面updateSharedMemory
有一个电话region.component.paint
。如果任何已排队的事件发生更改component
,则绘制调用的实际结果可能会有所不同。
测试结果的一些建议:
sharedMemoryUpdater
像这样创建:
private Runnable scheduled = null;
sharedMemoryUpdater = Runnable {
public void run() {
scheduled = null;
updateSharedMemory();
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在addDirtyRegion
if (scheduled == null) {
scheduled = sharedMemoryUpdater;
SwingUtilities.invokeLater(sharedMemoryUpdater);
}
Run Code Online (Sandbox Code Playgroud)
这将减少 的调用次数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;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2378 次 |
最近记录: |