参考:
https://blog.csdn.net/sureyonder/article/details/5695420?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-14&spm=1001.2101.3001.4242
https://www.cnblogs.com/zhchoutai/p/8726824.html
Swing/LookAndFeel 优美的 MVC 模式会给你一个良好的编程思想。
Swing 是什么?
Swing 是 Java 官方推出的,绝大部分控件都由 Graphics2D 绘制的一种轻量级 GUI 方案。其全部的轻量级控件都继承自 JComponent 类。
须要注意的是,Swing 中依旧有三个重量级控件:JFrame,JDialog,JWindow。只是它们都是窗口,他们都继承自 Window 类。
而不管是 JComponent 还是 Window 它们都继承自 Container 类,这事实上也就意味着:全部的 Swing 控件,都能够做控件容器。
Swing 是怎样通过 Graphics2D 绘制这些控件的呢?
答案就是 LookAndFeel 机制。
通俗的说,这就是皮肤;
从功能上说。这是一种批量管理 Swing 控件外观的机制;
从根源来说,这是 Swing 的核心。
官方在 Java 7 中正式推出的 NimbusLookAndFeel 则要美观的多。
你问怎样换 LookAndFeel?
在启动你的程序前:
UIManager.setLookAndFeel(…);
就可以
假设是在程序已经启动之后再换 LookAndFeel,那在上面那句之后,建议再加上:
SwingUtilities.updateComponentTreeUI(…);
所谓 MVC 就是:模型(Model)、视图(View)、控制器(Controller)这种一种结构。
Swing 中,差点儿全部的控件都能够清晰的分解成这三大部分。
就拿 JButton 来举例,我们能够这样分解:
JButton——控制器;
ButtonModel——模型。其最常见的详细实现类是:DefaultButtonModel。
ButtonUI——视图,其最常见的详细实现类是:MetalButtonUI;
JButton 负责控制,ButtonModel 提供模型,而 ButtonUI 实现展示。
也就是说,基本上全部的 Swing 控件都是由一个 Control 类、一个 Model 类、一个 UI 类组成的。
部分过于简单和数据无关的控件无 Model 类,比如 JPanel……
Swing 是怎样通过 UI 类来绘制控件的呢?
Swing 控件在 UI 线程中的绘制过程:
会产生一个 PaintEvent 然后排入 UI 线程的事件处理序列;
而 UI 线程在处理 Swing 控件的 PaintEvent 时,终于都会调用到控件的 paint 方法。
通过源码分析,总结如下:
一个控件要绘制,就必定调用到它的 paint 方法;
而默认的 paint 方法中会调用到 paintComponent 方法;
paintComponent 方法中又会调用 UI 类的 update 方法;
也就是说,一个控件的绘制,和其 UI 类中的 update 方法是息息相关的。
看,UI 类成功的和控件绘制关联上了。
各个控件的 UI 类又是怎么被设置到 Control 类中的呢?
这里要提到一个非常重要的类——UIManager。
在 LookAndFeel 机制中,会有大量的键值对存放在一个 UIDefaults(事实上就是个 HashTable)中。
这些键值对记录了控件的边框、各种部分的颜色、字体等等,当中也包含了这个控件相应的 UI 类的类名。
而 UIManager 就是方便我们调用或替换这些键值对的一个管理工具类。
通过源码分析,原来是通过控件类中的 getUIClassID 返回的“键”。来获得 UI 类的类名在 UIDefaults 中的“值”。然后反射生成 UI 类的对象。
总结如下:
在控件构造时,都会去调用 updateUI 方法;
在控件的 updateUI 方法中,会通过 UIManager.getUI 去获取 ui 对象;
而 getUIClass(target.getUIClassID(), uiClassLoader),是通过控件的 uiClassID 这个“键”去获得 UIDefaults 中的相应的“值”;
而最后依据返回的类名,反射生成一个 UI 类的对象(PanelUI),返回给 updateUI 方法;
再通过 setUI 方法赋值给 ui 成员变量。
假设你打算自己写一套 LookAndFeel,当你写了一个 UI 类之后应该怎么和控件相应上呢?
答案就是改写 LookAndFeel 类中的 initClassDefaults 方法,设置相应的组件对。
例如源码:MetalLookAndFeel 类,initClassDefaults 方法:
protected void initClassDefaults(UIDefaults table) { super.initClassDefaults(table); final String metalPackageName = "javax.swing.plaf.metal."; Object[] uiDefaults = { "ButtonUI", metalPackageName+ "MetalButtonUI", "CheckBoxUI", metalPackageName+ "MetalCheckBoxUI", "ComboBoxUI", metalPackageName + "MetalComboBoxUI", "DesktopIconUI", metalPackageName + "MetalDesktopIconUI", "FileChooserUI", metalPackageName + "MetalFileChooserUI", "InternalFrameUI", metalPackageName + "MetalInternalFrameUI", "LabelUI", metalPackageName + "MetalLabelUI", "PopupMenuSeparatorUI", metalPackageName + "MetalPopupMenuSeparatorUI", "ProgressBarUI", metalPackageName + "MetalProgressBarUI", "RadioButtonUI", metalPackageName + "MetalRadioButtonUI", "ScrollBarUI", metalPackageName + "MetalScrollBarUI", "ScrollPaneUI", metalPackageName + "MetalScrollPaneUI", "SeparatorUI", metalPackageName + "MetalSeparatorUI", "SliderUI", metalPackageName + "MetalSliderUI", "SplitPaneUI", metalPackageName + "MetalSplitPaneUI", "TabbedPaneUI", metalPackageName + "MetalTabbedPaneUI", "TextFieldUI", metalPackageName + "MetalTextFieldUI", "ToggleButtonUI", metalPackageName + "MetalToggleButtonUI", "ToolBarUI", metalPackageName + "MetalToolBarUI", "ToolTipUI", metalPackageName + "MetalToolTipUI", "TreeUI", metalPackageName + "MetalTreeUI", "RootPaneUI", metalPackageName + "MetalRootPaneUI", }; table.putDefaults(uiDefaults); }