A lényeg, amit a megjegyzésemben ki akartam fejteni:
ha alszik az edt, az ebből eredő helytelen viselkedés alapvetően kiszámíthatatlan.
Kiszámíthatatlan abban az értelemben, hogy nem tudhatod, hogy mi fog történni vagy sem. Nem tehetünk mást, mint kitalálni...
Ennek technikai oka az, hogy a legtöbb UI frissítés nem azonnal, hanem ütemezetten történik: nem igazán tudhatjuk, mi vár a sorban mögöttünk. Ne feledje: ez csak egy sáv, és amikor a Performed akcióban van, mi ülünk benne.
Oktatási okokból az alábbi kód az eredeti kód, néhány sorral a megjegyzések eltávolításához/megjegyzésekhez a különböző forgatókönyvek bemutatása érdekében.
- [0] az ikon visszaállítása alvás előtt/után: ahogy már észrevette, az első nem jelenik meg, bár a tulajdonság foglalt. Technikai ok: a vizuális frissítés a következőn keresztül történik:
label.repaint()
, amely az EDT-n van ütemezve az utóbbi feldolgozáshoz (ahogyan az api-dokumentumban szerepel)
- [1] átfutva az api doc-ot, egy másik módszert is észreveszünk:
paintImmediately(...)
ami dokumentáltan pontosan azt csinálja, amit a neve mond, és megengedett - ahogyan az EDT-n is - megengedik, hogy hívják. Sikernek tűnik, megjelenik a sárga ikon.
- [2], de várj: a Borderline közepén a címke úgyis kitölti azt a területet, függetlenül attól, hogy van-e rajta ikon vagy sem. Próbáljuk meg egy olyan régióba helyezni, amely újratervezést igényel, mivel f.i. délre. Vissza a térre [0], a sárga ikon nem jelenik meg.
- [3] megvizsgálva a
setIcon(..)
forrását, kiderül, hogy az elrendezés... ismét ütemezett. Az [1] négyzetben megtanultuk, hogy a dolgokat azonnal megtörténhetõvé tudjuk kényszeríteni, ha az elrendezés a invalidate()
/ validate()
pár lenne. Bingó, sárga ikon még délen is.
- [4] csúnya alosztály, amely ütemezi az ikon tulajdonságainak beállítását (megjegyzés: bár itt van kitalálva, a szerződésében semmi sem akadályozza meg az alosztályokat!). Mivel a tulajdonság még nincs beállítva, a sárga nem jelenik meg, vissza a négyzethez [0]
A nap végén (de elalvás előtt az EDT :-)) egyszerűen nem lehet megbízhatóan megjósolni a vizuális eredményt alvás közben. A látvány pedig csak a jég csúcsa...
/**
* Unpredictable behaviour when sleeping the EDT.
* http://stackoverflow.com/q/15600203/203657
*
* [0] run as-is: property set but yellow not showing
* [1] uncomment paintImmediately: yellow showing in center
* [2] add label to south: yellow not showing
* [3] force immediate in-/validation: yellow showing in south
* [4] subclass label with invoked property setting:
* property not set, yellow not showing
*
*/
public class ThreadSleeping {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("Load");
JLabel label = new JLabel() {
// [4] subclass implemented to schedule the property setting
// @Override
// public void setIcon(final Icon icon) {
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
// superSetIcon(icon);
//
// }
// });
// }
//
// protected void superSetIcon(Icon icon) {
// super.setIcon(icon);
// }
//
};
public ThreadSleeping() {
panel.add(button);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Icon firstIcon = new FixedIcon(Color.YELLOW, 100, 100);
Icon secondIcon = new FixedIcon(Color.RED, 500, 500);
label.setIcon(firstIcon);
// check if the property is already set
System.out.println(label.getIcon());
// following lines try to force the output before going to sleep
// [3] paintImmediately + force immediate re-layout
// label.invalidate();
// label.getParent().validate();
// {1] paintImmediately (fine for center, no effect for south)
// ((JComponent) label.getParent()).paintImmediately(0, 0, 5000, 5000);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
label.setIcon(secondIcon);
}
});
frame.add(panel, BorderLayout.NORTH);
// un/comment one of the following, placing the
// label either in CENTER (= sized to fill)
frame.add(label, BorderLayout.CENTER);
// [2] or in SOUTH (= sized to pref)
// frame.add(label, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(1024, 768);
frame.setVisible(true);
}
/**
* Simple fixed size Icon filling its area.
*/
public static class FixedIcon implements Icon {
private Color background;
private int width;
private int height;
public FixedIcon(Color background, int width, int height) {
this.background = background;
this.width = width;
this.height = height;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(background);
g.fillRect(0, 0, width, height);
}
@Override
public int getIconWidth() {
return width;
}
@Override
public int getIconHeight() {
return height;
}
@Override
public String toString() {
return "[" + background + " w/h " + width + "/" + height + "]";
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new ThreadSleeping();
}
});
}
}
25.03.2013