问题
The situation: drawing a stack of playing cards, like in the Solitaire game. Nicely stacked.
To achieve this, I'm using a JLayeredPane
in combination with a custom implementation of the LayoutManager
interface. The reason for using a custom LayoutManager is that the stack orientation varies, sometimes the playing cards cover each other completely, sometimes partially, and this logic seems to be a good job for a LayoutManager, because this basically boils down to setting the location of the cards.
So, the LayoutManager
is responsible for setting the X- and Y-coordinates of all components in my stack. The JLayeredPane
on the other hand is responsible for their Z-coordinates (via its layers).
Adding a component to a JLayeredPane
goes like this:
JLayeredPane pane = new JLayeredPane();
pane.setLayout(new StackLayout(...));
pane.add(new CardView(...), new Integer(j));
where new Integer(j)
is the layer of the card. This must be an Integer
due to the contract of JLayeredPane
.
The problem here is, that my StackLayout
cannot have any other constraint object than an Integer
, due to the reason stated above. The LayoutManager
interface requires you to implement the following method:
addLayoutComponent(Component comp, Object constraints);
and the passed Object
will here always be an Integer
.
In my particular situation, I am lucky, as my XY-coordinates can be calculated based on the Z-coordinates. For example, the card in layer k
has to be located at Y-coordinate k * offset
. So in my situation, the constraints object being an Integer
is not a problem.
I was wondering what you should be doing when there is no correlation between the Z-coordinates and the XY-coordinates? How can you solve this then? For example, how would I use a GridBagLayout
in combination with a JLayeredPane
, where the first requires a GridBagConstraints
object and the second an Integer
object? Of course, a GBL will layout in such a way that components do not overlap, but it's just the idea.
回答1:
Based on the comment of Hovercraft Full Of Eels, I'll answer my own question.
Calling JLayeredPane.add(Component comp, Object constraints)
will call addImpl(Component comp, Object constraints, int index)
. This method is overridden by JLayeredPane itself, here's the source:
protected void addImpl(Component comp, Object constraints, int index) {
int layer;
int pos;
if(constraints instanceof Integer) {
layer = ((Integer)constraints).intValue();
setLayer(comp, layer);
} else
layer = getLayer(comp);
pos = insertIndexForLayer(layer, index);
super.addImpl(comp, constraints, pos);
comp.validate();
comp.repaint();
validateOptimizedDrawing();
}
As you can see, the Integer
object is intercepted, so that the JLayeredPane knows the layer in which comp
should be placed. Then, it passes on constraints
, the Integer, to the super implementation. The essential part in the super implementation, Container
, is:
protected void addImpl(Component comp, Object constraints, int index) {
...
if (layoutMgr != null) {
if (layoutMgr instanceof LayoutManager2) {
((LayoutManager2)layoutMgr).addLayoutComponent(comp, constraints);
} else if (constraints instanceof String) {
layoutMgr.addLayoutComponent((String)constraints, comp);
}
}
...
}
The solution would therefore be to extend JLayeredPane
and override its addImpl(Component comp, Object constraints, int index)
method, which can accept any Object of your choice: this object should contain the Integer
needed for the JLayeredPane's layer decision, and it should also contain the constraints object for the chosen LayoutManager.
StackConstraints:
public final class StackConstraints {
public final int layer;
public final Object layoutConstraints;
public StackConstraints (int layer, Object layoutConstraints){
this.layer = layer;
this.layoutConstraints = layoutConstraints;
}
}
JLayeredPane extension:
protected void addImpl(Component comp, Object constraints, int index) {
int layer;
int pos;
Object constr;
if(constraints instanceof StackConstraints) {
layer = constraints.layer.intValue();
constr = ((StackConstraints) constraints).layoutConstraints;
setLayer(comp, layer);
} else {
layer = getLayer(comp);
constr = constraints;
}
pos = insertIndexForLayer(layer, index);
super.addImpl(comp, constraints, pos);
comp.validate();
comp.repaint();
validateOptimizedDrawing();
}
回答2:
So, the LayoutManager is responsible for setting the X- and Y-coordinates of all components in my stack. The JLayeredPane on the other hand is responsible for their Z-coordinates (via its layers).
You don't need to use a layered pane for this. A panel also supports Z-Order. Check out the Overlap Layout which explains more about this and may do what you need.
来源:https://stackoverflow.com/questions/18334874/jlayeredpane-with-a-layoutmanager