【问题标题】:JLayeredPane with a LayoutManager带有 LayoutManager 的 JLayeredPane
【发布时间】:2013-08-20 12:19:27
【问题描述】:

情况:抽一摞扑克牌,就像在纸牌游戏中一样。叠得很好。

为了实现这一点,我将JLayeredPaneLayoutManager 接口的自定义实现结合使用。使用自定义 LayoutManager 的原因是堆栈方向不同,有时扑克牌完全相互覆盖,有时会部分覆盖,而这个逻辑对于 LayoutManager 来说似乎是一个很好的工作,因为这基本上归结为设置卡片。

所以,LayoutManager 负责设置堆栈中所有组件的 X 和 Y 坐标。另一方面,JLayeredPane 负责它们的 Z 坐标(通过其层)。

将组件添加到JLayeredPane 如下所示:

JLayeredPane pane = new JLayeredPane();
pane.setLayout(new StackLayout(...));
pane.add(new CardView(...), new Integer(j));

其中new Integer(j) 是卡片的层。由于JLayeredPane 的合同,这必须是Integer

这里的问题是,由于上述原因,我的StackLayout 不能有除Integer 之外的任何其他约束对象。 LayoutManager接口需要你实现如下方法:

addLayoutComponent(Component comp, Object constraints);

而传递的Object 将永远是Integer

在我的特殊情况下,我很幸运,因为我的 XY 坐标可以根据 Z 坐标计算。例如,k 层中的卡片必须位于 Y 坐标 k * offset。所以在我的情况下,约束对象是 Integer 不是问题。

我想知道当 Z 坐标和 XY 坐标之间没有相关性时你应该怎么做?那你怎么解决这个问题?例如,我如何将GridBagLayoutJLayeredPane 结合使用,其中第一个需要GridBagConstraints 对象,第二个需要Integer 对象?当然,GBL 会以组件不重叠的方式进行布局,但这只是想法。

【问题讨论】:

  • 您是否考虑过扩展 JLayeredPane,因为您需要更改基本行为?通过这种方式,您可以创建自己的 add 方法,该方法将允许使用其自己的特定 Object 2nd 参数,该方法结合了 JLayeredPane 超所需的 Integer ,如果布局使用 GridBagLayout 或类似的东西,可能还有 GridBagConstraints 。
  • @HovercraftFullOfEels 似乎这可能是正确的解决方案。我想我可能需要覆盖 protected void addImpl(Component comp, Object constraints, int index) ,其中 JLayeredPane 覆盖自身,首先拦截用于层确定的 Integer 对象。然后它将约束传递给它的超类Container,它最终不会对它做任何事情,因为默认JLayeredPane 没有LayoutManager。我会为这种方法写一个答案。

标签: java swing constraints layout-manager jlayeredpane


【解决方案1】:

根据 Hovercraft Full Of Eels 的评论,我会回答我自己的问题。

调用JLayeredPane.add(Component comp, Object constraints) 将调用addImpl(Component comp, Object constraints, int index)。这个方法被 JLayeredPane 本身覆盖,这里是源代码:

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();
}

如您所见,Integer 对象被截获,因此 JLayeredPane 知道应该放置comp 的层。然后,它将整数 constraints 传递给超级实现。超级实现Container 的基本部分是:

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);
        }
    }
    ...
}

因此,解决方案是扩展JLayeredPane 并覆盖其addImpl(Component comp, Object constraints, int index) 方法,该方法可以接受您选择的任何对象:此对象应包含JLayeredPane 层决策所需的Integer,它还应包含所选 LayoutManager 的约束对象。

堆栈约束:

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 扩展:

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();

}

【讨论】:

  • 我很欣赏这个建议(它对我有很大帮助),但代码无法正常工作。 1) layer = constraints.layer.intValue() 应该改为((StackConstraints) constraints).layer。 2) 如果 constr 恰好是 Integer 类型,super.addImpl(...) 将重置分层。 3) validateOptimizedDrawing() 是JLayeredPane 的私有方法。你不能从子类中调用它。
  • 这里有一个替代方案:protected void addImpl(Component comp, Object constraints, int index) { int layer = 0; int pos = 0; Object constr; if (constraints instanceof StackConstraints) { layer = ((StackConstraints) constraints).layer; constr = ((StackConstraints) constraints).layoutConstraints; } else { layer = getLayer(comp); constr = constraints; } pos = insertIndexForLayer(layer, index); super.addImpl(comp, constr, pos); setLayer(comp, layer, pos); comp.validate(); comp.repaint(); }
【解决方案2】:

因此,LayoutManager 负责设置堆栈中所有组件的 X 和 Y 坐标。另一方面,JLayeredPane 负责它们的 Z 坐标(通过其层)。

您不需要为此使用分层窗格。面板还支持 Z-Order。查看Overlap Layout,它对此进行了更多解释,并且可以满足您的需要。

【讨论】:

  • 虽然您的 LM 适用于大多数游戏,但我的布局完全是矢量/相对定义的。因此,我不会根据首选子级大小来计算父级大小,而是反过来。因此,使用子组件的首选/最小/最大尺寸的 LM 不是最佳选择,至少在我的情况下不是。不过,我很感谢您的回答,我会调查是否可以在我的项目中使用它。但问题是关于 JLayeredPane 与 LayoutManager 的组合,而不是替代方案。有什么理由不使用 JLayeredPane 的内置分层功能?
  • @Timmos, the question was about the combination of JLayeredPane with a LayoutManager, not about alternatives. - 我提供了另一种选择,因为您可能没有考虑过这种方法,它可能会给您带来新的想法。 Any reason why you wouldn't use JLayeredPane's built-in layering? - 您的解决方案涉及覆盖 JLayeredPane 和创建自定义布局管理器。我的建议将代码保留在我认为更清洁的布局管理器中。也许您可以将您的矢量布局方法与我的 ZOrder 方法结合起来?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
  • 2021-11-28
  • 2012-07-08
相关资源
最近更新 更多