Когда непрозрачный компонент перекрывает другой, намеренно пропустить рисование полностью затемненных компонентов. Это может стать проблемой только тогда, когда компонент ошибочно объявлен непрозрачным. Поэтому, когда вы реализуете свои собственные компоненты, рисующие прозрачное изображение, они должны реализовать метод isOpaque()
, возвращающий false
.
Интересно, что я все еще мог воспроизвести проблему перерисовки с прозрачными компонентами, используя следующий код:
JFrame f = new JFrame("Test");
JLayeredPane pane = f.getLayeredPane();
JButton b = new JButton("Normal Text");
b.setHorizontalTextPosition(SwingConstants.LEFT);
b.setBounds(20, 20, 300, 100);
JLabel l = new JLabel("Overlay");
l.setHorizontalTextPosition(SwingConstants.RIGHT);
l.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
l.setBounds(20, 20, 300, 100);
l.setOpaque(false);
pane.add(l);
pane.add(b);
f.setSize(350, 200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
Здесь текст «Наложение» может отображаться или не отображаться изначально, но как только изменяется одно из свойств кнопки (например, получение фокуса или нажатие), текст исчезает.
Проблема заключается в следующем свойстве:
System.out.println(pane.isOptimizedDrawingEnabled());
напечатает true
. А «оптимизированный рисунок» означает, что некоторые дети могут не отображаться в определенных ситуациях. Как документация гласит:
Возвращает true, если этот компонент размещает свои дочерние элементы, то есть, если он может гарантировать, что дочерние элементы не будут перекрываться.
Конечно, JLayeredPane
никогда не будет помещать детей в плитки, но укладывать их друг на друга и никогда не сможет гарантировать, что они не будут перекрываться. Однако по какой-то причине разработчики Swing решили реализовать следующую логику:
private void validateOptimizedDrawing() {
boolean layeredComponentFound = false;
synchronized(getTreeLock()) {
Integer layer;
for (Component c : getComponents()) {
layer = null;
if(SunToolkit.isInstanceOf(c, "javax.swing.JInternalFrame") ||
(c instanceof JComponent &&
(layer = (Integer)((JComponent)c).
getClientProperty(LAYER_PROPERTY)) != null))
{
if(layer != null && layer.equals(FRAME_CONTENT_LAYER))
continue;
layeredComponentFound = true;
break;
}
}
}
if(layeredComponentFound)
optimizedDrawingPossible = false;
else
optimizedDrawingPossible = true;
}
Поэтому, когда компоненты не являются экземплярами JComponent
или не имеют свойства LAYER_PROPERTY
, что всегда имеет место при использовании слоя по умолчанию, как в примере выше, панель решает, что оптимизированная краска была возможна, несмотря ни на что в фактической раскладке логика изменилась.
Итак, когда мы меняемся
pane.add(l);
pane.add(b);
до
Object myLayer = JLayeredPane.DEFAULT_LAYER+1;
pane.add(l, myLayer);
pane.add(b, myLayer);
проблема исчезнет, так как System.out.println(pane.isOptimizedDrawingEnabled());
выведет false
сейчас.
Остерегайтесь автобокса. Когда вы используете pane.add(l, JLayeredPane.DEFAULT_LAYER+1);
или просто pane.add(l, 1);
, он будет вызывать add(Component comp, int index)
вместо желаемого add(Component comp, Object constraints)
.