У меня есть приложение Java (Swing), работающее на 32-битном Windows 2008 Server, которое должно визуализировать его вывод за пределы экрана (который затем выбирается другим приложением 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);
}
}
Панель с проблемами:
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.