我有一個運行在32位Windows 2008 Server上的Java(Swing)應用程序,它需要將其輸出呈現爲離屏圖像(然後由另一個C++申請在其他地方渲染)。大多數組件都能正確渲染,除非奇怪的情況下,剛剛失去焦點的組件被另一個組件遮擋,例如,如果兩個JComboBox接近,則用戶與較低組件互動,然後單擊上面的那個,所以它的下拉與另一個盒子重疊。將Swing組件渲染到離線緩衝區
在這種情況下,失去焦點的組件會在其遮擋後呈現,因此會出現在輸出頂部。它在正常的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在其上重新繪製。
當您使用默認的RepaintManager時會發生什麼?另外,我不明白你是如何開始渲染組件的。例如,如果我有一個打開的組合框並單擊一個按鈕來啓動某個動作,組合框將關閉,所以我不知道如何重現您描述的情況。我建議幫助張貼您的SSCCE(http://sscce.org)來證明問題。 – camickr 2010-05-25 20:54:15
不幸的是,我不能使用默認的重繪管理器,因爲它還必須爲C++應用程序編寫一些元數據以供使用,例如,識別屏幕圖像中的髒區域,以便重繪C++應用程序。 渲染是通過標準方式啓動的,我在主JFrame中替換的所有內容都是RepaintManager,因此就我所知,每個組件在變爲無效時都應該導致重繪。 我不確定我可以發佈一個完整的例子,因爲涉及的本地代碼的性質,但我會把它的Java一面。 – 2010-05-26 09:06:41
在updateSharedMemory的for循環中,大的if-else鏈沒有最後的else。嘗試添加,看看是否有你不處理的區域。 – 2010-05-27 20:52:45