<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Java桌面技術(shù)

    Java Desktop Technology

    常用鏈接

    統(tǒng)計(jì)

    友情連接

    最新評(píng)論

    通過(guò)xml配置文件定義及布局組件

    如果你使用過(guò)netBeans的GUI構(gòu)件器Matisse,在感受到其拖拽帶來(lái)的方便的同時(shí),也會(huì)發(fā)現(xiàn)自動(dòng)生成的代碼“慘不忍睹”。例如Matisse默認(rèn)的布局為GroupLayout,隨便拖拽3個(gè)組件生成的代碼如下

     // <editor-fold defaultstate="collapsed" desc="Generated Code">
        private void initComponents() {

            jButton1 = new javax.swing.JButton();
            jButton2 = new javax.swing.JButton();
            jButton3 = new javax.swing.JButton();

            jButton1.setText("jButton1");

            jButton2.setText("jButton2");

            jButton3.setText("jButton3");

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
            this.setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                            .addGap(60, 60, 60)
                            .addComponent(jButton1))
                        .addGroup(layout.createSequentialGroup()
                            .addGap(160, 160, 160)
                            .addComponent(jButton2)))
                    .addContainerGap(165, Short.MAX_VALUE))
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                    .addContainerGap(191, Short.MAX_VALUE)
                    .addComponent(jButton3)
                    .addGap(134, 134, 134))
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addGap(45, 45, 45)
                    .addComponent(jButton1)
                    .addGap(18, 18, 18)
                    .addComponent(jButton2)
                    .addGap(46, 46, 46)
                    .addComponent(jButton3)
                    .addContainerGap(122, Short.MAX_VALUE))
            );
        }// </editor-fold>


    無(wú)論Matisse發(fā)展得如何強(qiáng)大,但是其本質(zhì)只是用來(lái)生成Java代碼而已,當(dāng)你修改這代碼后,再逆向恢復(fù)成UI設(shè)

    計(jì)器時(shí)Matisse卻出于自己的一套安全考慮不允許你這樣做,所以最終不得迫使開(kāi)發(fā)人員放棄拖曳方式設(shè)計(jì)UI,

    而統(tǒng)統(tǒng)采用面向代碼的方式。就我看來(lái)拖拽作業(yè)方式能解決80%的簡(jiǎn)單布局,剩下的20%的專(zhuān)業(yè)細(xì)化GUI設(shè)計(jì)器是

    做不到的。《netBeans6.0咸魚(yú)翻身與Swing稱(chēng)霸桌面應(yīng)用》一文有人在評(píng)論中說(shuō)到“VB,Delphi,C++Bulider的

    開(kāi)發(fā)人員早已習(xí)慣了用拖曳的方式來(lái)畫(huà)頁(yè)面,界面都是保存成一個(gè)資配置源文件”,那么Java為什么不能模仿

    這一點(diǎn)呢?下面就詳細(xì)介紹利用外部配置文件實(shí)現(xiàn)組件的布局。

     

    第三部分:利用外部配置文件實(shí)現(xiàn)組件的布局

    在學(xué)習(xí)本章之前,需要對(duì)前文介紹的2種自定義布局有所了解,分別是、《自定義布局管理器-FormLayout》《自定義布局管理器-CenterLayout》本文介紹的配置文件均是以這兩種布局為基礎(chǔ)的。
    開(kāi)門(mén)見(jiàn)山,程序運(yùn)行結(jié)果如下圖。



    有4個(gè)組件,JButton、JScrollPane(內(nèi)嵌JTree)、自定義組件ImageButton、一個(gè)JTextField。布局原則是JButton左邊界距離容器左邊界5像素、右邊界距離容器左邊界130像素、所以長(zhǎng)度為130-5=125固定不變,JButton上邊界距離容器上邊界10像素,下邊界距離容器下邊界35像素,所以高度為35-10=25固定不變;JScrollPane位于容器的中央,其中左右兩邊距離容器兩邊均是20像素,所以JScrollPane的寬度隨著容器寬度的變化而變化,JScrollPane上下兩邊距離容器中心高度均是50像素,所以整體高度是100像素;ImageButton的上邊界距離容器的底部50像素,左邊界距離容器右邊界100像素,由于ImageButton會(huì)根據(jù)背景尺寸產(chǎn)生PreferredSize,所以右邊界、下邊界不用設(shè)置。剩下的JTextField不是通過(guò)配置產(chǎn)生,具體見(jiàn)后面介紹。

    下面來(lái)看看xml配置,如下。
    <ui-container>
    (1)    <layout-manager class="org.swingframework.layout.FormLayout" />
        <components>
    (2)       <component id="1101" class="javax.swing.JButton">
    (3)            <form-data>
                    <left percentage="0.0" offset="5" />
                    <right percentage="0.0" offset="130" />
                    <top percentage="0.0" offset="10" />
                    <bottom percentage="0.0" offset="35" />
                </form-data>
            </component>

            <component id="1102" class="javax.swing.JScrollPane">
                <form-data>
                    <left percentage="0.0" offset="20" />
                    <top percentage="0.5" offset="-50" />
                    <right percentage="1.0" offset="-20" />
                    <bottom percentage="0.5" offset="50" />
                </form-data>
            </component>

            <component id="1103" class="org.swingframework.component.ImageButton">
                <form-data>
                    <left percentage="1.0" offset="-100" />
                    <top percentage="1.0" offset="-50" />
                </form-data>
            </component>
        </components>
    </ui-container>

    (1)指定容器的布局類(lèi),目前僅支持FormLayout、CenterLayout兩種。
    (2)定義一個(gè)組件,指定唯一的id和完整類(lèi)名。
    (3)為組件指定布局約束。
    由于xml文檔結(jié)構(gòu)是固定的,因此xml解析采用XPath。XPath已在JDK1.5中被集成,而且相比DOM更加簡(jiǎn)單,關(guān)于XPath的更多內(nèi)容參考其他資料。

    定義布局注入LayoutInjection類(lèi),目的就是解釋一個(gè)給定的xml文件,然后去給一個(gè)容器生成內(nèi)部組件并布局這些組件。

    public class LayoutInjection {

    private Container injectTarget;
     private InputStream layoutSource;
     private LayoutManager layoutManager;
     private Map<String, ComponentEntry> entryMap;

     public LayoutInjection(Container injectTarget, InputStream layoutSource) {
      this.injectTarget = injectTarget;
      this.layoutSource = layoutSource;
     }
    ......

    private class ComponentEntry {
      private Component component;
      private FormData formData;

      ComponentEntry(Component component, FormData formData) {
       this.component = component;
       this.formData = formData;
      }

      public Component getComponent() {
       return component;
      }

      public FormData getFormData() {
       return formData;
      }
     }
    }
    單獨(dú)定義一個(gè)ComponentEntry內(nèi)部類(lèi)是為了將組件-布局約束關(guān)聯(lián)。定義一個(gè)injectLayout方法來(lái)完成布局,在injectLayout方法內(nèi)部,首先讀取外部配置文件并將組件與布局約束保存到entryMap,然后為容器設(shè)置根據(jù)配置得到的布局管理器,下一步插入自定義屬性,包括非配置產(chǎn)生的組件,與組件修飾、組件事件監(jiān)聽(tīng)器等。最后一步是遍歷entryMap根據(jù)每個(gè)組件與其布局約束完成組件創(chuàng)建與布局。
    public void injectLayout(){
      if (!load()) {
                Logger.getLogger(LayoutInjection.class.getName()).log(Level.WARNING, "load components failed");
                return;
            }
            EventQueue.invokeLater(new Runnable() {

                @Override
                public void run() {
                    injectTarget.setLayout(layoutManager);
                    /* Notifies all listeners that have registered interest for notification. */
                    Object[] listeners = listenerList.getListenerList();
                    // Process the listeners last to first, notifying those that are ICustomer type.
                    for (int i = listeners.length - 2; i >= 0; i -= 2) {
                        if (listeners[i] == ICustomer.class) {
                            try {
                                ((ICustomer) listeners[i + 1]).customProperties(LayoutInjection.this);
                            } catch (Throwable ex) {
                                Logger.getLogger(LayoutInjection.class.getName()).log(Level.WARNING, ex.getLocalizedMessage(), ex);
                            }
                        }
                    }
                    synchronized (injectTarget.getTreeLock()) {
                        Set<String> idSet = entryMap.keySet();
                        for (String id : idSet) {
                            Component component = getComponentById(id);
                            FormData formData = getFormDataById(id);
                            try {
                                injectTarget.add((Component) component, formData);
                            } catch (Throwable ex) {
                                Logger.getLogger(LayoutInjection.class.getName()).log(Level.WARNING, ex.getLocalizedMessage(), ex);
                            }
                        }
                        injectTarget.doLayout();
                    }
                }
            });
     }
    customProperties方法是空實(shí)現(xiàn),需要自己去實(shí)現(xiàn)邏輯。

    protected void customProperties() {

     }
    injectLayout其他部分的實(shí)現(xiàn)參見(jiàn)完整源碼。
    最后給一個(gè)Test測(cè)試這個(gè)類(lèi)。

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.io.FileInputStream;

    import javax.swing.BorderFactory;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextField;
    import javax.swing.JTree;
    import javax.swing.WindowConstants;

    import org.swingframework.component.ImageButton;
    import org.swingframework.layout.FormAttachment;
    import org.swingframework.layout.FormData;
    import org.swingframework.layout.LayoutInjection;

    public class Test {

     public static void main(String[] args) throws Exception {

            JFrame frm = new JFrame();
            frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            final JPanel panel = new JPanel();
            panel.setPreferredSize(new Dimension(350, 250));
            frm.getContentPane().add(panel, BorderLayout.CENTER);
            FileInputStream input = new FileInputStream("C:/layout.xml");
            LayoutInjection injection = new LayoutInjection(panel, input);
            injection.addCustomer(new ICustomer() {
                public void customProperties(LayoutInjection source) {
                    JButton button = (JButton) source.getComponentById("1101");
                    button.setText("this is a JButton");
                    JScrollPane jsp = (JScrollPane) source.getComponentById("1102");
                    jsp.setBorder(BorderFactory.createLineBorder(new Color(128, 128, 128), 2));
                    jsp.getViewport().add(new JTree());
                    ImageButton imageButton = (ImageButton) source.getComponentById("1103");
                    imageButton.setBackgroundImage(new ImageIcon("button_up.png"));
                    imageButton.setRolloverBackgroundImage(new ImageIcon("button_over.png"));
                    imageButton.setPressedBackgroundImage(new ImageIcon("button_down.png"));
                    // add extend component
                    JTextField jtf = new JTextField();
                    jtf.setBorder(BorderFactory.createLineBorder(new Color(128, 128, 128), 2));
                    FormData jtfFormData = new FormData();
                    jtfFormData.top = new FormAttachment(0.8f, 0);
                    jtfFormData.left = new FormAttachment(0.2f, 0);
                    jtfFormData.right = new FormAttachment(0.2f, 100);
                    jtfFormData.bottom = new FormAttachment(0.8f, 25);
                    panel.add(jtf, jtfFormData);
                }
            });
            injection.injectLayout();
            input.close();
            frm.pack();
            frm.setVisible(true);
     }
    }
    加紅的需要解釋以下,xml數(shù)據(jù)源是java.io.InputStream的實(shí)例,當(dāng)然不局限與文件讀取;一般地,都要實(shí)現(xiàn)customProperties完成自定義邏輯,本例是修飾了組件外觀,另外以非配置方式加載了JTextField組件,以非配置方式添加組件是很必要的,因?yàn)閷?shí)際應(yīng)用的時(shí)候,你添加的更多的是已有的組件實(shí)例而不是xml文件中寫(xiě)死的。
    至此,這一節(jié)內(nèi)容介紹完畢。下一節(jié)我想做個(gè)有關(guān)布局管理器的總結(jié),并穿插我在開(kāi)發(fā)當(dāng)中的一些感悟。
    源代碼這里下載


    posted on 2007-12-31 00:49 sun_java_studio@yahoo.com.cn(電玩) 閱讀(9407) 評(píng)論(4)  編輯  收藏 所屬分類(lèi): NetBeansGUI Design

    評(píng)論

    # re: 通過(guò)xml配置文件定義及布局組件 2008-01-01 10:51 zr

    呵呵,前些日子有個(gè)公司來(lái)我公司做項(xiàng)目,.net的,所有的界面都是從XML文件解析,然后生成的,挺有意思的  回復(fù)  更多評(píng)論   

    # re: 通過(guò)xml配置文件定義及布局組件 2008-01-01 10:55 sitinspring

    解析XML生成界面,這個(gè)技術(shù)不難的,關(guān)鍵是有實(shí)用價(jià)值.  回復(fù)  更多評(píng)論   

    # re: 通過(guò)xml配置文件定義及布局組件[未登錄](méi) 2009-08-17 18:20 sharper

    一種復(fù)雜度轉(zhuǎn)化成另外一種復(fù)雜度  回復(fù)  更多評(píng)論   

    # re: 通過(guò)xml配置文件定義及布局組件 2011-05-23 12:13 Visco

    感謝偉大的樓主,最近正在為這個(gè)發(fā)愁呢!  回復(fù)  更多評(píng)論   

    TWaver中文社區(qū)
    主站蜘蛛池模板: 热99re久久精品精品免费| 亚洲另类无码专区丝袜| 亚洲AV中文无码乱人伦在线视色| 84pao国产成视频免费播放| 一区二区免费在线观看| 亚洲性色AV日韩在线观看| 78成人精品电影在线播放日韩精品电影一区亚洲 | 亚洲另类无码专区首页| 亚洲综合小说久久另类区| 久久精品国产69国产精品亚洲| 国产又粗又猛又爽又黄的免费视频| 免费黄色福利视频| 精品国产麻豆免费人成网站| 一级毛片aa高清免费观看| 国产亚洲女在线线精品| 亚洲欧美精品午睡沙发| 亚洲娇小性xxxx| 亚洲精品中文字幕无乱码| 亚洲福利视频一区| 亚洲AV人无码综合在线观看| 亚洲免费人成在线视频观看| 亚洲色一色噜一噜噜噜| 亚洲狠狠爱综合影院婷婷| 免费国产小视频在线观看| 国产美女a做受大片免费| 岛国片在线免费观看| 国产美女在线精品免费观看| 精品香蕉在线观看免费| 1000部拍拍拍18勿入免费视频软件| 一级毛片免费不卡在线| 久久午夜夜伦鲁鲁片免费无码| 国产永久免费高清在线| 国产免费一区二区视频| 久久免费高清视频| 人妻无码一区二区三区免费| 久久久久久一品道精品免费看| 久久国产精品一区免费下载| 24小时免费看片| 免费无码A片一区二三区| 成人毛片18女人毛片免费| 国产性生交xxxxx免费|