Swing Synth L&F

来源:互联网 发布:baidu软件中心 编辑:程序博客网 时间:2024/06/02 17:26
Synth 是Java Swing 中使用XML文件对Swing的外观进行定义的一种方式, 不必了解过于细节的UI的实现方式,只要学会编写Synth的XML格式文件, 就可以实现自己的Swing漂亮的皮肤.
public class MainFrame extends JFrame {    public static void main(String[] args) throws Exception {        SwingUtilities.invokeLater(new Runnable() {            @Override            public void run() {                try {                    createAndShowUI();                } catch (Exception e) {                    e.printStackTrace();                }            }        });    }    private static void createAndShowUI() throws Exception {        SynthLookAndFeel lookAndFeel = new SynthLookAndFeel();        lookAndFeel.load(MainFrame.class.getResourceAsStream("/laf.xml"), MainFrame.class);        UIManager.setLookAndFeel(lookAndFeel);        MainFrame mainFrame = new MainFrame();        mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);        mainFrame.setSize(800, 900);        mainFrame.getContentPane().add(new ComponentSet());        mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);        mainFrame.setBackground(new Color(43, 43, 43));        mainFrame.setVisible(true);    }}

import javax.swing.*;import java.awt.*;/** * Created by GuoLiang on 2016/1/9. */public class ComponentSet extends JPanel {    public ComponentSet() {        this.setLayout(new FlowLayout());        this.setBorder(BorderFactory.createEmptyBorder(30, /*top*/ 30, /*left*/ 10, /*bottom*/ 30) /*right*/);        JTextField jTextField = new JTextField("我是输入框 hello", 100);        jTextField.setSelectionColor(new Color(90, 117, 171));        this.add(jTextField);        JTextField jTextField1 = new JTextField("我是输入框 hello", 100);        jTextField1.setSelectionColor(new Color(90, 117, 171));        this.add(jTextField1);    }}

/** * Created by GuoLiang on 2016/1/9. */public class GradientPainter extends SynthPainter {    public void paintTextFieldBackground(SynthContext context, Graphics g, int x, int y, int w, int h) {        // For simplicity this always recreates the GradientPaint. In a        // real app you should cache this to avoid garbage.//        Graphics2D g2 = (Graphics2D) g;//        g2.setColor(Color.decode("#cccccc"));//        g2.setStroke(new BasicStroke(1.5f));//        g2.drawRoundRect(x+1, y+1, w-2, h-2, 5, 5);        Graphics2D g2 = (Graphics2D) g;        g2.fillRect(x, y, w, h);        g2.setStroke(new BasicStroke(1f));        g2.setColor(new Color(69, 73, 74));        g2.fillRoundRect(x + 2, y + 2, w - 3, h - 3, 10, 10);        g2.setColor(new Color(100, 100, 100));        g2.drawRoundRect(x + 1, y + 1, w - 2, h - 2, 10, 10);    }}
laf.xml文件:
<!--<?xml version="1.0" encoding="UTF-8"?>--><!--<!DOCTYPE synth PUBLIC "-//oracle.com//DTD swign synth configuration 1.0//EN" "https://docs.oracle.com/javase/8/docs/api/javax/swing/plaf/synth/doc-files/synth.dtd">--><synth>    <!-- Style that all regions will use -->    <style id="backingStyle">        <!-- Make all the regions opaque-->        <opaque value="TRUE"/>        <font name="微软雅黑" size="12"/>        <state>            <!-- Provide default colors -->            <color value="#2B2B2B" type="BACKGROUND"/>            <color value="#ffffff" type="FOREGROUND"/>        </state>    </style>    <bind style="backingStyle" type="REGION" key=".*"/>    <style id="buttonStyle">        <!-- Shift the text one pixel when pressed -->        <property key="Button.textShiftOffset" type="integer" value="0"/>        <insets top="10" left="10" right="10" bottom="10"/>        <state>            <imagePainter method="buttonBackground" path="images/button.png" sourceInsets="10 10 10 10"/>        </state>        <state value="PRESSED">            <imagePainter method="buttonBackground" path="images/button2.png" sourceInsets="10 10 10 10"/>        </state>    </style>    <!-- Bind buttonStyle to all JButtons -->    <bind style="buttonStyle" type="REGION" key="button"/>    <style id="textfield">        <opaque value="TRUE" />        <object id="gradient" class="com.lang.swing.GradientPainter"/>        <state>            <painter method="textFieldBackground" idref="gradient"/>        </state>        <state value="SELECTED">        </state>        <insets top="4" left="4" right="4" bottom="4"/>    </style>    <bind style="textfield" type="REGION" key="textfield"/></synth>


<synth>  <style id="basicStyle">    <font name="Verdana" size="16"/>    <state>      <color value="WHITE" type="BACKGROUND"/>      <color value="BLACK" type="FOREGROUND"/>    </state>  </style>  <bind style="basicStyle" type="region" key=".*"/></synth>说明:style标签,可以表述多个region,一般用一个style表现一个元素; style标签有个id属性,这个id属性是用在下面的bind标签中.style下的font 设置了字体.state标签下面会继续讨论.regionstate可以有一个或多个.如果有有元素没有设置对应的type,那么将使用全局的state,bind标签,将上面声明的style样式,应用到了.*所有的元素上.------------------------------------------------------------------------------------------------------------------------bind标签:    style标签定义完成之后,如果想使其生效,必须将其绑定到componentsregions.    bind标签必须含有三个属性:    1). style必须有id,并且id是唯一的    2). type: name/region.如果为name,对应的是Component.getName()的返回值,如果为region,则使用javax.swing.plaf.synth.Region中的常量.    3). key: key为一个正则表达式,用于将样式绑定到匹配的components/regions.KeyRegion是用标识一个component或者component的一部分,对应的是Region中的常量中去掉下划线的部分.比如:SPLIT_PANE -> SPLITPANE, splitpane 或者 SplitPane (大小写不敏感).下面有个简答的例子:<style id="styleOne">   <!-- styleOne definition goes here --></style><style id="styleTwo">   <!-- styleTwo definition goes here --></style><bind style="styleOne" type="region" key="Button"/><bind style="styleOne" type="region" key="RadioButton"/><bind style="styleOne" type="region" key="ArrowButton"/><bind style="styleTwo" type="region" key="ArrowButton"/>可以绑定到一个单独的/已命名的component; 如果有两个按钮 "OK" "Cancel",但你想将这两个按钮与其它的按钮区别对待,首先你可以给OkCancel取个名字,使用 component.setName() 方法, 然后定义三个样式,第一个为普通的按钮(region = "Button"), 第一个是为 OK 按钮 (name = "OK"), 第三个是为 Cancel 按钮 (name = "Cancel").就像下面的这样:<bind style="styleButton" type="region" key="Button"><bind style="styleOK" type="name" key="OK"><bind style="styleCancel" type="name" key="Cancel">"OK"按钮有两个样式 "styleButton" "styleOK,"  "Cancel" 按钮也有两个样式 "styleButton" "styleCancel."当一个component/region有多个样式的时候,会进行合并Note:在样式进行合并时,文件后面声明的样式优先.------------------------------------------------------------------------------------------------------------------------state标签:    用于定义元素的状态,如按钮的"PRESSED","ENABLED"不同的状态.ENABLED, MOUSE_OVER, PRESSED, DISABLED, FOCUSED, SELECTED, DEFAULT可以使用 and 一次指定多个状态,: ENABLED and FOCUSED. 如果没有指定value,那么这个state将会作为默认的state, 并且会应用到所有的state上面.As an example, here is a style that specifies painters per state. All buttons are painted a certain way, unless the state is "PRESSED," in which case they are painted differently:<style id="buttonStyle">  <property key="Button.textShiftOffset" type="integer" value="1"/>  <insets top="10" left="10" right="10" bottom="10"/>  <state> // 注意:这个state没有value,会作为默认的state,按钮的所有状态都会有这个样式    <imagePainter method="buttonBackground" path="images/button.png"                         sourceInsets="10 10 10 10"/>  </state>  <state value="PRESSED"> // 注意:只有在按钮被按下后,才会有这个state声明的样式    <color value="#9BC3B1" type="BACKGROUND"/>    <imagePainter method="buttonBackground" path="images/button2.png"                        sourceInsets="10 10 10 10"/>  </state></style><bind style="buttonStyle" type="region" key="Button"/>region会选择匹配最接近的state,匹配时会根据value中的的个数进行匹配. 如果和声明的state没有匹配,则使用没有valuestate<state id="zero">  <color value="RED" type="BACKGROUND"/></state><state value="SELECTED and PRESSED" id="one">  <color value="RED" type="BACKGROUND"/></state><state value="SELECTED" id="two">  <color value="BLUE" type="BACKGROUND"/></state>如果regionstate保研 SELECTED and PRESSED, 则选择 state one. state含有 SELECTED, 但不含 PRESSED, 则选择state two . SELECTED nor PRESSED 一个都没有, 则选择 state zero.如果region的当前state满足多个statevalue, 那么会选择第一个state, 比如按钮的MOUSE_OVER PRESSED, 谁在前就选谁.<state value="PRESSED">   <imagePainter method="buttonBackground" path="images/button_press.png"                          sourceInsets="9 10 9 10" />   <color type="TEXT_FOREGROUND" value="#FFFFFF"/></state><state value="MOUSE_OVER">   <imagePainter method="buttonBackground" path="images/button_on.png"                          sourceInsets="10 10 10 10" />   <color type="TEXT_FOREGROUND" value="#FFFFFF"/></state>------------------------------------------------------------------------------------------------------------------------颜色和字体:<color>的属性:value:java.awt.Color中的常量,:RED, WHITE, BLACK, BLUE,也可以是16进制表示的颜色,:#FF00FF or #326A3B.type:降颜色应用给谁,: BACKGROUND, FOREGROUND, FOCUS, TEXT_BACKGROUND, OR TEXT_FOREGROUND.:  <style id="basicStyle">    <state>      <color value="WHITE" type="BACKGROUND"/>      <color value="BLACK" type="FOREGROUND"/>    </state>  </style><font> 的属性:name: 字体的名称,:Arial or Verdana.size: 字体的像素大小.style[可选]: : BOLD, ITALIC, BOLD ITALIC. 默认为normal.:  <style id="basicStyle">    <font name="Verdana" size="16"/>  </style><color>,<font>还有另外一种使用方式,加一个id,可以重用,在另一个<color/font>中使用idref进行引用.<color id="backColor" value="WHITE" type="BACKGROUND"/><font id="textFont" name="Verdana" size="16"/><color idref="backColor"/><font idref="textFont"/>------------------------------------------------------------------------------------------------------------------------内边距:Insets : <insets top="15" left="20" right="20" bottom="15"/>------------------------------------------------------------------------------------------------------------------------使用图片:Synth可以使用图片. Synthimage painter 将图片切分为9个部分: top, top right, right, bottom right, bottom, bottom left, left, top left, and center.top, left, bottom, and right edges are 平铺/拉伸的, 四个角的图片保持原样 (原图:sourceInsets).Note:<insets>sourceInsets无关,是相互独立的.中间的区域可以使用paintCenter属性,指定是否要画上去.如果希望图片的四个角的不会被拉伸,可以设置sourceInsets 10.<style id="buttonStyle">   <insets top="15" left="20" right="20" bottom="15"/>   <state>      <imagePainter method="buttonBackground" path="images/button.png"        sourceInsets="10 10 10 10"/>   </state></style><bind style="buttonStyle" type="region" key="button"/><imagePainter>的属性:method: 使用javax.swing.plaf.synth.SynthPainter 中的方法进行绘制,100种方法,paintButtonBackground -> buttonBackground (去掉paint,剩下的首字母小写即可)path: 图片的路径,相对于SynthLookAndFeel.load方法中指定的Class路径sourceInsets: 指定使用原图,不尽兴拉伸绘制的大小,顺序如下:上、左、下、右.paintCenter[可选]: 是否绘制center区域,(如果是文本框,可以为false).The listing below shows the XML code for loading different images depending on the <state> of the button  <style id="buttonStyle">    <property key="Button.textShiftOffset" type="integer" value="1"/>    <insets top="15" left="20" right="20" bottom="15"/>    <state>      <imagePainter method="buttonBackground" path="images/button.png"                    sourceInsets="10 10 10 10"/>    </state>    <state value="PRESSED">      <imagePainter method="buttonBackground" path="images/button2.png"                    sourceInsets="10 10 10 10"/>    </state>  </style>  <bind style="buttonStyle" type="region" key="button"/>下面这行的作用,是在按钮按下时,文字向右移动一个像素:<property key="Button.textShiftOffset" type="integer" value="1"/>------------------------------------------------------------------------------------------------------------------------<property>的属性:使用键值对给元素设置外观:key—属性的名称type—属性的类型value—属性的值下面的地址可以查看每个component可以使用那些属性:https://docs.oracle.com/javase/8/docs/api/javax/swing/plaf/synth/doc-files/componentProperties.html--- 一个简单的例子---------------------------------------------------------------------------------------------------------<!-- Synth skin that includes an image for buttons --><synth>  <!-- Style that all regions will use -->  <style id="backingStyle">    <!-- Make all the regions that use this skin opaque-->    <opaque value="TRUE"/>    <font name="Dialog" size="12"/>    <state>      <!-- Provide default colors -->      <color value="#9BC3B1" type="BACKGROUND"/>      <color value="RED" type="FOREGROUND"/>    </state>  </style>  <bind style="backingStyle" type="region" key=".*"/>  <style id="buttonStyle">    <!-- Shift the text one pixel when pressed -->    <property key="Button.textShiftOffset" type="integer" value="1"/>    <insets top="15" left="20" right="20" bottom="15"/>    <state>      <imagePainter method="buttonBackground" path="images/button.png"                    sourceInsets="10 10 10 10"/>    </state>    <state value="PRESSED">      <imagePainter method="buttonBackground" path="images/button2.png"                    sourceInsets="10 10 10 10"/>    </state>  </style>  <!-- Bind buttonStyle to all JButtons -->  <bind style="buttonStyle" type="region" key="button"/></synth>------------------------------------------------------------------------------------------------------------------------使用图标:单选框和浮选,可以使用固定大小的图标:<style id="radioButton">   <imageIcon id="radio_off" path="images/radio_button_off.png"/>   <imageIcon id="radio_on" path="images/radio_button_on.png"/>   <property key="RadioButton.icon" value="radio_off"/>   <state value="SELECTED">      <property key="RadioButton.icon" value="radio_on"/>   </state></style><bind style="radioButton" type="region" key="RadioButton"/>------------------------------------------------------------------------------------------------------------------------特殊情况下比较有用:自定义画笔: 不使用图片,而是使用自定义的颜色(渐变的颜色等)进行定义样式:<synth>  <object id="gradient" class="GradientPainter"/>  <style id="textfield">    <painter method="textFieldBackground" idref="gradient"/>  </style>  <bind style="textfield" type="region" key="textfield"/></synth>GradientPainter 类的定义如下:public class GradientPainter extends SynthPainter {   public void paintTextFieldBackground(SynthContext context,                                        Graphics g, int x, int y,                                        int w, int h) {      // For simplicity this always recreates the GradientPaint. In a      // real app you should cache this to avoid garbage.      Graphics2D g2 = (Graphics2D)g;      g2.setPaint(new GradientPaint((float)x, (float)y, Color.WHITE,                 (float)(x + w), (float)(y + h), Color.RED));      g2.fillRect(x, y, w, h);      g2.setPaint(null);   }}------------------------------------------------------------------------------------------------------------------------
1 0
原创粉丝点击