Tengo una aplicación Java (Swing), que se ejecuta en un servidor Windows 2008 de 32 bits, que necesita mostrar su salida a una imagen fuera de pantalla (que luego es captada por otro C++ aplicación para renderizar en otro lugar). La mayoría de los componentes se procesan correctamente, excepto en el caso extraño donde un componente que acaba de perder el enfoque está ocluido por otro componente, por ejemplo, donde hay dos JComboBoxes cerca uno del otro, si el usuario interactúa con el inferior y luego hace clic el superior, así que es desplegable se superpone a la otra caja.Representación de componentes Swing a un buffer sin pantalla
En esta situación, el componente que ha perdido el foco se representa después del que lo ocluyó, y así aparece en la parte superior de la salida. Se procesa correctamente en la pantalla Java normal (se ejecuta en pantalla completa en la pantalla principal), y el intento de cambiar las capas de los componentes en cuestión no ayuda.
Estoy usando un RepaintManager personalizado para pintar los componentes a la imagen fuera de pantalla, y supongo que el problema radica en el orden en el que addDirtyRegion() se llama para cada uno de los componentes en cuestión, pero no puedo pensar en una buena forma de identificar cuándo ocurre este estado particular para prevenirlo. Cortarlo para que el objeto que acaba de perder el foco no se vuelva a pintar detiene el problema, pero obviamente causa el problema mayor de que no se repinta en todas las demás circunstancias normales.
¿Hay alguna forma de identificar mediante programación este estado, o de reordenar cosas para que no ocurra?
Muchas gracias,
Nick
[editar] añadido algo de código como ejemplo:
gestor de repintado y clases asociadas:
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);
}
}
El Grupo Especial de que está teniendo el problemas:
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);
}
}
Usando este ejemplo, si el usuario interactúa con comboBox2, entonces con comboBox1, el menú desplegable de comboBox1 se superpone comboBox2, pero comboBox2 se vuelve a dibujar encima de él.
¿Qué sucede cuando usa el RepaintManager predeterminado? Además, no entiendo cómo se inicia la representación de los componentes. Por ejemplo, si tengo un cuadro combinado abierto y hago clic en un botón para iniciar alguna acción, el cuadro combinado se cierra, por lo que no sé cómo reproducir la situación que describes. Sugiero ayuda para publicar su SSCCE (http://sscce.org) para demostrar el problema. – camickr
Desafortunadamente no puedo usar el administrador de repintado predeterminado ya que también tiene que escribir algunos metadatos para que la aplicación C++ los use, p. Ej. identificar regiones sucias en la imagen fuera de pantalla para que la aplicación C++ vuelva a dibujar. La representación se inicia con los medios estándar, todo lo que he reemplazado en el JFrame principal es el RepaintManager, por lo que yo entiendo, ¿cada componente debería causar un repintado cuando se vuelve inválido? No estoy seguro de poder publicar un ejemplo completo debido a la naturaleza del código nativo involucrado, pero lo pondré al lado de Java. –
En el ciclo for en updateSharedMemory, la gran cadena if-else no tiene otra cosa. Intenta agregar eso y ver si hay regiones que no estás manejando. –