【问题标题】:Swing - Custom Buttons w/SynthSwing - 带合成器的自定义按钮
【发布时间】:2025-12-23 14:25:12
【问题描述】:

我正在开发一个使用 Synth 作为 UI 的项目,并希望实现一些自定义按钮。按钮需要使用合成 XML 设置文件中的样式设置 - 例如不同状态(MOUSE_OVER、PRESSED 等)的不同字体颜色。

我遇到的问题是一些按钮需要有额外的子组件 - 例如有些需要多个标签。我希望子组件采用与标准按钮子组件相同的样式设置。

我觉得我应该能够扩展 JButton 和覆盖/扩展 paintComponent 来调用一些子组件的绘制方法。不过,我对这种方法的一些方面有点不确定:例如将哪些参数传递给paintComponent;以及如何确保子组件获得正确的 Synth 样式设置(尤其是状态)。

顺便说一句:我尝试过扩展JPanel,但在使用这种方法时遇到了一些困难(请参阅此处:JPanel states for Synth)。

编辑:所以,我发现可以将子组件添加到按钮并让它们正确呈现。似乎即使JButton.getLayout() 返回空值,按钮也会使用OverlayLayout,除非您调用JButton.setLayout()。调用 JButton.setLayout(null) 确实会阻止使用 OverlayLayout,所以这就是我处理布局的方式。

我正在研究几种不同的方法来更新子控件的样式,稍后会报告这些方法。

【问题讨论】:

  • 绘制由组件的 UI 委托处理。请参阅 @mKorbel 的 answer,在您之前的 question 中引用过此主题。
  • 我知道 UI 委托处理绘图,但由于(据我所知)基本上不可能扩展 Synth UI 委托,我希望有不止一种方法可以给这只猫剥皮。

标签: java swing jbutton look-and-feel synth


【解决方案1】:

所以,如果它对其他人有用,这是我最后采用的方法:

class CustomButton extends JButton {
    CustomButton() {
        // ... normal button init

        // Enable absolute positioning of sub-components.
        setLayout(null);

        updateStyles();

        getModel().addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                updateStyles();
            }
        });
    }

    private void updateStyles() {
        // See below for implementation.
    }

    private int getSynthComponentState() {
        // This is basically a copy of SynthButtonUI.getComponentState(JComponent)
        int state = SynthConstants.ENABLED;
        if (!isEnabled()) {
            state = SynthConstants.DISABLED;
        }

        if (model.isPressed()) {
            if (model.isArmed()) {
                state = SynthConstants.PRESSED;
            } else {
                state = SynthConstants.MOUSE_OVER;
            }
        }
        if (model.isRollover()) {
            state |= SynthConstants.MOUSE_OVER;
        }
        if (model.isSelected()) {
            state |= SynthConstants.SELECTED;
        }
        if (isFocusOwner() && isFocusPainted()) {
            state |= SynthConstants.FOCUSED;
        }
        if (isDefaultButton()) {
            state |= SynthConstants.DEFAULT;
        }
        return state;
    }
}

我发现了 2 种方法来实现 updateStyles() 方法:(A) 更改组件的名称以使用不同的命名样式,或 (B) 将样式设置从按钮复制​​到子组件。方法(A)非常简单,方法(B)的工作原理如下:

private void updateStyles() {
    SynthStyle ss = SynthLookAndFeel.getStyle(this, Region.BUTTON);
    SynthContext sc = new SynthContext(this, Region.BUTTON, ss, getSynthComponentState());

    for (Component c : getComponents()) {
        c.setFont(ss.getFont(sc));
        c.setBackground(ss.getColor(sc, ColorType.BACKGROUND));
        c.setForeground(ss.getColor(sc, ColorType.FOREGROUND));
        // ... and so on if you have other style elements to be changed.
    }
}

如果您为每个不同的状态更改多个样式设置,则方法 (A) 可能会更好,但如果您为许多不同的状态设置不同的样式,它可能会变得笨拙。如果您只更改几个样式设置(例如,在我的情况下,我只关心颜色,至少目前如此),那么方法 (B) 似乎是最好的。

trashgod 还建议了实现自定义 UI 委托(扩展 BasicButtonUI)的方法,但如果您采用该方法,我认为您将不得不重新实现 SynthButtonUI 的大部分内容。

【讨论】:

    最近更新 更多