Java Swing 模块化配色方案

Posted

技术标签:

【中文标题】Java Swing 模块化配色方案【英文标题】:Java Swing modular color scheme 【发布时间】:2015-04-09 15:03:21 【问题描述】:

我正在使用 Java 的 Swing 工具包设置一个大型 GUI(比我以前做过的任何事情都大),我想设置我自己的自定义配色方案来绘制颜色,以便所有颜色定义都在一个地方。为此,我决定创建一个名为 ColorPalette 的伪静态***类(来自 https://***.com/a/7486111/4547020 帖子),其中包含一个 SchemeEnum程序员为整个 GUI 设置配色方案。

我希望颜色选择独立于配色方案的知识。有谁知道设计模式或有效的方法来做到这一点?我不完全相信我当前的设置是实现这一点的最佳方式,但我想设置一个模块化设计,添加更多 ColorEnumsSchemeEnums(在编译时,而不是运行时)。

为了澄清起见,我希望程序员能够简单地选择一个 ColorEnum 并根据 ColorEnum 返回一个 java.awt.Color 对象和定义的SchemeEnum

例如:

        // Use the BASIC color scheme
        ColorPalette.setCurrentScheme(ColorPalette.SchemeEnum.BASIC);

        // Set button backgrounds
        testButton.setBackground(ColorPalette.ColorEnum.DARK_RED.getColor());
        testButton2.setBackground(ColorPalette.ColorEnum.BLUE.getColor());

应该返回不同的Color对象而不是

        // Use the DARK color scheme
        ColorPalette.setCurrentScheme(ColorPalette.SchemeEnum.DARK);

        // Set button backgrounds
        testButton.setBackground(ColorPalette.ColorEnum.DARK_RED.getColor());
        testButton2.setBackground(ColorPalette.ColorEnum.BLUE.getColor());

因为他们有不同的 SchemeEnums,即使他们向 ColorPalette 请求相同的颜色。这样,更改 SchemeEnum 会更改 GUI 中的每种颜色,只需更改一行代码(或者甚至可以在运行时更改颜色)。

我听说过 HashTables 用于像这样的大型数据存储,但我不知道它们是如何工作的。这可能适用于这里吗?

到目前为止,这是我的代码。提前致谢!

package common.lookandfeel;

import java.awt.Color;

/**
 * Class which contains the members for the color scheme used throughout the project.
 * <p>This class is essentially static (no constructor, class is final, all members static) and
 * should not be instantiated.
 */
public final class ColorPalette

    /**
     * The list of color schemes to choose from.
     */
    public static enum SchemeEnum
    
        BASIC, DARK, METALLIC
    

    /**
     * The list of color descriptions to choose from.
     */
    public static enum ColorEnum
    
        LIGHT_RED(256,0,0), RED(192,0,0), DARK_RED(128,0,0),
        LIGHT_GREEN(0,256,0), GREEN(0,192,0), DARK_GREEN(0,128,0),
        LIGHT_BLUE(0,0,256), BLUE(0,0,192), DARK_BLUE(0,0,128),
        LIGHT_ORANGE(256,102,0), ORANGE(256,102,0), DARK_ORANGE(192,88,0),
        LIGHT_YELLOW(256,204,0), YELLOW(256,204,0), DARK_YELLOW(192,150,0),
        LIGHT_PURPLE(136,0,182), PURPLE(102,0,153), DARK_PURPLE(78,0,124);

        private int red;
        private int green;
        private int blue;

        private ColorEnum(int r, int g, int b)
        
            this.red = r;
            this.green = g;
            this.blue = b;
        

        /**
         * Get the selected color object for this Enum.
         * @return The color description as a Color object.
         */
        public Color getColor()
        
            // WANT TO RETURN A COLOR BASED ON currentScheme
            return new Color(red, green, blue);
        
    

    private static SchemeEnum currentScheme = SchemeEnum.BASIC;

    /**
     * Default constructor is private to prevent instantiation of this makeshift 'static' class.
     */
    private ColorPalette()
    
    

    /**
     * Get the color scheme being used on this project.
     * @return The current color scheme in use on this project.
     */
    public static SchemeEnum getCurrentScheme()
    
        return currentScheme;
    

    /**
     * Set the overall color scheme of this project.
     * @param currentPalette The color scheme to set for use on this project.
     */
    public static void setCurrentScheme(SchemeEnum cp)
    
        currentScheme = cp;
    

    /**
     * Main method for test purposes only.  Unpredictable results.
     * @param args Command line arguments.  Should not be present.
     */
    public static void main(String[] args)
    
        // Declare and define swing data members
        JFrame frame = new JFrame("Test Environment");
        CustomButton testButton = new CustomButton ("Hello World");
        CustomButton testButton2 = new CustomButton ("I am a button!");

        // Use a particular color scheme
        ColorPalette.setCurrentScheme(ColorPalette.SchemeEnum.BASIC);

        // Set button backgrounds
        testButton.setBackground(ColorPalette.ColorEnum.DARK_RED.getColor());
        testButton2.setBackground(ColorPalette.ColorEnum.BLUE.getColor());

        // Place swing components in Frame
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(testButton, BorderLayout.NORTH);
        frame.getContentPane().add(testButton2, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);

        // Set allocated memory to null
        frame = null;
        testButton = null;
        testButton2 = null;

        // Suggest garbage collecting to deallocate memory
        System.gc();
    

【问题讨论】:

直接在 UIManager 中定义颜色或创建自己的外观和感觉会更简单,也许使用Synth 大部分 JComponents 都有属性数组,例如作为 JButton,这些属性对于所有可能的事件(选中、按下、武装......)都是不同的,使用自定义 L&F,在某些情况下可以设置配色方案(以避免重新发明***) == 在 UIManager 中的键内循环 【参考方案1】:

看起来和听起来您只需将 SchemeEnum 组合成由 ColorEnums 组成,就像您如何让 ColorEnum 由 rgb 值组成一样。

public static enum SchemeEnum

    // Don't really know what colors you actually want
    BASIC(ColorEnum.RED, ColorEnum.GREEN, ColorEnum.ORANGE),
    DARK(ColorEnum.DARK_RED, ColorEnum.DARK_GREEN, ColorEnum.DARK_ORANGE),
    METALLIC(ColorEnum.LIGHT_RED, ColorEnum.LIGHT_GREEN, ColorEnum.LIGHT_ORANGE);

    // nor know how many colors make up a scheme
    public ColorEnum mainColor;
    public ColorEnum secondaryColor;
    public ColorEnum borderColor;

    private SchemeEnum(ColorEnum mainColor, ColorEnum secondaryColor, 
                       ColorEnum borderColor)
    
        this.mainColor = mainColor;
        this.secondaryColor = secondaryColor;
        this.borderColor = borderColor;
    

然后,使用如下代码,其中颜色基于所选方案:

testButton.setBackground(ColorPalette.getCurrentScheme().mainColor.getColor());

【讨论】:

这种方法看起来非常模块化,不需要很多改变!我也喜欢为颜色标记用途而不是命名标记描述的想法(即mainColorborderColor 等而不是YELLOWDARK_GREEN 等)。我最初的概念是调整每种颜色的外观(即DARK 方案将只是BASIC 方案,但阴影有点暗)。如果我不是新成员,我会提供 +1 :) @FallDownT 新用户可以通过查看和滚动帮助 -> 游览页面来获得大量代表点。 @FallDownT 另外,您可能有兴趣查看 Nimbus 的外观和感觉,它能够设置看起来不错的配色方案。 docs.oracle.com/javase/tutorial/uiswing/lookandfeel/color.html 我之前看过 Numbus,认为它没有提供足够的多功能性。不过现在,我遇到了这样一个问题:每个 SchemeEnum 大约有 50 个 Color 对象,而 50 个参数的构造函数不一定是好习惯。关于如何在构造函数中为每个 SchemeEnum 设置每个 Color 的任何想法? @FallDownT 我可能会,不过我必须去一段时间。这似乎很奇怪,你有 50 种独特的颜色来构成 UI 的配色方案……这似乎不对,50 种独特的颜色似乎是正确的,但由 50 种颜色组成的方案却不是。【参考方案2】:

在重新发明***之前,Swing 基于可插拔的外观 API,请参阅 Modifying the Look and Feel。

正确的方法是定义自己的外观和感觉并加载它。因为您想提供可变数量的更改,所以使用Synth 之类的东西可能会更好。这允许您为对象定义级联属性,并允许您从其他属性继承(因此您可以设计一组基本属性,然后只更改您在每个后续外观中需要的属性)。

作弊方法是直接修改UIManager,更改当前外观使用的各种属性。如果您想进行小的调整,这有时会更容易。

无论哪种方式,这都会影响应用程序创建的所有组件,而无需在启动时更改外观即可

【讨论】:

我刚刚阅读了有关 Synth 的 Oracle 教程,它看起来很有前途。在编写简单的 android 应用程序时,我只接触过几次 XML,所以我对它比较陌生。然而,根据我的阅读,这似乎是一种更有效的方式来改变 GUI 的整体外观,而无需为了改变它们的外观而重载 JComponents 去年年底我为我的工作场所做了一个原型,它使用基本外观并构建了 4 种不同的外观和感觉。我不会说这很容易开始,但最终结果非常棒,特别是当您可以仅基于组件 name 属性为单个组件(如 JButton)提供自定义时,因此您可以拥有一个基本按钮外观和一些自定义外观,例如“确定”和“取消”按钮...... 我一直在尝试使用 xml 文件,但我不确定哪些选项可用/如何充分利用 xml 文件。我已经阅读了有关 oracle 的教程,并且正在使用示例 xmls 作为资源,但是您是否碰巧知道任何其他资源可以帮助我快速学习如何开发 Synth xml 文件?我真的很喜欢这种技术将 laf 与代码隔离开来的方式。 是的,它将 laf 与代码隔离开来,这就是重点,把它想象成 css。你可以看看其他一些实现。

以上是关于Java Swing 模块化配色方案的主要内容,如果未能解决你的问题,请参考以下文章