青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

逛奔的蝸牛

我不聰明,但我會(huì)很努力

   ::  :: 新隨筆 ::  ::  :: 管理 ::

高級(jí) Synth

有了最新的 Swing 外觀,定制 UI 不在話下

Michael Abernethy, 軟件工程師, EMC

簡(jiǎn)介: 本文將深入透視 Synth 外觀,它是 Java 5.0 中為 Swing 引入的最新內(nèi)容。通過(guò)為 Java UI 編程引入“皮膚”的概念,Synth 使開發(fā)人員可以為應(yīng)用程序創(chuàng)建和部署定制的外觀。軟件工程師 Michael Abernethy 將帶您從頭開始逐步構(gòu)建一個(gè)具有 synth 外觀的應(yīng)用程序,讓您充分了解 Synth 的概念。閱讀本文之后,您應(yīng)該可以在短時(shí)間內(nèi)創(chuàng)建具有專業(yè)外觀的 UI。

本文的標(biāo)簽:  best_practicesswing外觀應(yīng)用開發(fā)

發(fā)布日期: 2005 年 2 月 01 日 
級(jí)別: 初級(jí) 
訪問(wèn)情況 : 2692 次瀏覽 
評(píng)論: 0 (查看 | 添加評(píng)論 - 登錄)

平均分 4 星 共 10 個(gè)評(píng)分 平均分 (10個(gè)評(píng)分)
為本文評(píng)分

就在 Sun 一如既往地試圖“再次引入 Java Desktop”之際,Java UI 開發(fā)人員的抱怨之詞亦已表面化:要?jiǎng)?chuàng)建完全定制的外觀實(shí)在太難。這樣做不僅要花費(fèi)太多的時(shí)間,并且 Swing UI 代碼的編寫和文檔的編制也極為不堪,常常是亂雜一氣,缺乏規(guī)劃。為了創(chuàng)建完整的外觀,開發(fā)人員需要繼承 Metal 外觀的 39 個(gè)類,或者繼承 Basic 外觀的 60 個(gè)類。誰(shuí)想通過(guò)重寫整個(gè)包來(lái)改變應(yīng)用程序呈現(xiàn)外觀的方式呢?用 Swing 創(chuàng)建定制外觀有多難,通過(guò)下面的事實(shí)同樣可窺見一斑:在很多開發(fā)人員為開源項(xiàng)目添磚加瓦的時(shí)代,Internet 上可用的自定義 Swing 外觀幾乎是鳳毛麟角 —— 總共大約是 20 個(gè),其中少數(shù)在 SourceForge.net 上(請(qǐng)參閱 參考資料)。

美麗只是膚淺的東西

進(jìn)入 Synth,Sun 希望它能使應(yīng)用程序外觀的個(gè)性化過(guò)程變得容易。Synth 的目標(biāo)很簡(jiǎn)單 —— 讓開發(fā)人員不必編寫任何代碼就可以創(chuàng)建新的外觀。這似乎是個(gè)不錯(cuò)的解決方案。程序員一般沒有突出的藝術(shù)才華,而圖形設(shè)計(jì)人員通常也不是 Java 編程專家。Synth 把對(duì)外觀的所有描述從代碼中分離出來(lái),而將其放入外部的 XML 文件和圖像文件中,為上述問(wèn)題提供了大快人心的解決之道。這種完全在外部文件中描述的外觀被稱作 皮膚(skin)。

Sun 的皮膚概念并不是什么創(chuàng)新。例如,Winamp 有數(shù)百種皮膚,F(xiàn)irefox 也有幾十種皮膚,這些皮膚很容易創(chuàng)建,只需更改一個(gè) XML 文件即可。想像一下,僅僅修改一個(gè) XML 文件,就能快速、容易地為 Java 應(yīng)用程序創(chuàng)建一個(gè)外觀。再想想這樣一來(lái)的結(jié)果 —— 幾百個(gè)互不相同的 Swing 外觀。Java UI 開發(fā)人員當(dāng)然有理由歡呼了。

本文將深入分析 Synth 外觀,向您展示創(chuàng)建一個(gè)完整的外觀或皮膚所需知道的一切。您會(huì)看到一個(gè)帶有示例皮膚的應(yīng)用程序,這個(gè)應(yīng)用程序使用了 Synth 所有重要的概念。然后,我會(huì)逐步剖析這個(gè)皮膚,在構(gòu)建 XML 文件的過(guò)程中,一一教會(huì)您 Synth 的各個(gè)概念。

本文最后一節(jié)將盡力回答開發(fā)人員關(guān)于 Synth 性能、bug 和缺陷以及 Synth 在省時(shí)方面的表現(xiàn)等種種問(wèn)題。閱讀本文之后,您應(yīng)該會(huì)愿意擁護(hù) Synth 作為外觀解決方案,并準(zhǔn)備馬上使用它來(lái)創(chuàng)建自己的 Swing 外觀。

Synth 基礎(chǔ)

Synth 是一個(gè) 白板(tabula rasa)外觀 —— 一塊完全空白的畫布,表現(xiàn)為一個(gè)完全空白的面板(panel),只有在 XML 文件中定義了組件時(shí),它才會(huì)顯示東西。一旦定義了組件,在應(yīng)用程序上設(shè)置 Synth 外觀就再容易不過(guò)了,如清單 1 所示:


清單 1. 設(shè)置 Synth 外觀
            SynthLookAndFeel synth = new SynthLookAndFeel();
            synth.load(SynthFrame.class.getResourceAsStream("demo.xml"), SynthFrame.class);
            UIManager.setLookAndFeel(synth);

但是,對(duì)于 Synth,最重要的是要理解它是 XML 代碼,而不是 Java 代碼。雖然 Synth XML 格式一開始看上去比較嚇人,但實(shí)際上很簡(jiǎn)單。如果使用 KISS (Keep It Simple Stupid)這道符咒,您可以快速地創(chuàng)建一個(gè) XML 文件,并得到一個(gè)新的、可以運(yùn)行的外觀。

考慮到 KISS 指令,我將首先介紹 Synth XML 文件的主要構(gòu)件 —— <style>標(biāo)簽。<style>標(biāo)簽包含描述一個(gè)組件的式樣的所有信息,例如顏色、字體、圖像文件、狀態(tài),以及一些特定于組件的屬性。雖然一個(gè) <style>標(biāo)簽可以描述多個(gè)組件,但構(gòu)建 Synth 文件的最簡(jiǎn)便方法是為每個(gè) Swing 組件創(chuàng)建一個(gè)式樣。

創(chuàng)建好式樣之后,便可以將式樣鏈接到一個(gè)組件。<bind>標(biāo)簽通知 Synth 引擎將一個(gè)已定義的式樣鏈接到一個(gè)組件,如清單 2 所示。這樣的組合便完全創(chuàng)建了組件的新外觀。


清單 2. 將一種式樣鏈接到一個(gè)組件
            <style id="textfield">
            // describe colors, fonts, and states
            </style>
            <bind style="textfield" type="region" key="Textfield"/>
            <style id="button">
            // describe colors, fonts, and states
            </style>
            <bind style="button" type="region" key="Button"/>

關(guān)于 <bind>標(biāo)簽,要注意的一點(diǎn)是:<bind>標(biāo)簽中的 key屬性映射到 javax.swing.plaf.synth.Region類中的常量。Synth 引擎使用這些常量將式樣與一個(gè)實(shí)際的 Swing 組件鏈接。簡(jiǎn)單的組件,例如 JButton和 JTextField,使用一個(gè)常量。有些更復(fù)雜的組件,例如 JScrollBar和 JTabbedPane,則有多個(gè)常量,用于不同的部分。

我建議您在更熟悉 Synth 格式并且能夠設(shè)置 XML 中的繼承模型之前,使用每個(gè)組件一種式樣(one-style-per-component)的設(shè)置。這種結(jié)構(gòu)雖然沒有利用所有 XML 的分層結(jié)構(gòu)功能,但它是最容易設(shè)置、編寫代碼和調(diào)試的。

在處理 Synth XML 文件時(shí),還有一點(diǎn)很重要,并不是任何形式都是合法的。如果有輸入錯(cuò)誤,或者在 XML 中使用了不正確的屬性,這些錯(cuò)誤只有當(dāng)外觀裝載期間拋出一個(gè)運(yùn)行時(shí)異常時(shí)才能發(fā)現(xiàn)。解決方法:在將 XML 文件發(fā)布給客戶之前,對(duì)其進(jìn)行測(cè)試。

Demo 應(yīng)用程序

我將帶您構(gòu)建一個(gè)簡(jiǎn)單的登錄屏幕,用它作為例子應(yīng)用程序,向您展示 Synth XML 文件的工作原理。該屏幕提供了足夠多的組件,通過(guò)這些組件,可以看到 XML 文件的所有重要部分,如果使這些部分結(jié)合起來(lái)便可以創(chuàng)建一個(gè)完整的外觀。

通過(guò)比較圖 1 和圖 2,具有 Ocean 外觀的登錄屏幕看上去與您預(yù)期的一樣 —— 簡(jiǎn)單,直接,也令人厭煩。具有 Synth 外觀的登錄屏幕則完全不同。


圖 1. 具有 Ocean 外觀的 Demo 應(yīng)用程序 
Ocean 外觀 

圖 2. 具有 Synth 外觀的 Demo 應(yīng)用程序
Synth 外觀 

更改顏色和字體

為 demo 應(yīng)用程序創(chuàng)建外觀的第一步是設(shè)置默認(rèn)顏色和字體。您將把 white Aharoni 字體作為每個(gè)組件的默認(rèn)字體,如果沒有特殊設(shè)置組件的話,就使用這種字體。

您可以將更改字體的 XML 放在 <style>標(biāo)簽內(nèi)的任何地方。還可以將顏色嵌入到一個(gè) <state>標(biāo)簽中。在本文的后面部分,我將更詳細(xì)地討論 <state>標(biāo)簽,但現(xiàn)在只需知道,一個(gè)簡(jiǎn)單的、不帶屬性的 <state> </state>標(biāo)簽可以包含任何狀態(tài),這個(gè)標(biāo)簽正是您在這里所需要的。

color標(biāo)簽本身需要兩個(gè)屬性:

  • value 可以是 java.awt.Color常量的任何 String表示(例如 REDBLUE),或者,它可以是一種顏色的十六進(jìn)制表示,前面加上 "#" (例如 #669966)。

  • type 描述文件應(yīng)該設(shè)置哪個(gè)區(qū)域的顏色。選擇有BACKGROUND、FOREGROUNDTEXT_FOREGROUNDTEXT_BACKGROUND和 FOCUS。

font標(biāo)簽有兩個(gè)必需的屬性和一個(gè)可選屬性。這三個(gè)屬性直接映射到 java.awt.Font類中的三個(gè)參數(shù):

  • name :字體的名稱(例如,Verdana、Arial)。

  • size :字體大小,以像素為單位。

  • style :如果不使用這個(gè)可選標(biāo)簽,那么將得到常規(guī)外觀的字體。其他選項(xiàng)包括 BOLD和 ITALIC。您還可以通過(guò)在這兩個(gè)屬性之間加一個(gè)空格來(lái)指定粗體加斜體的字體:BOLD ITALIC(這種組合屬性的技術(shù)對(duì)于 Synth XML 文件中的所有屬性都適用)。

最后,通過(guò)使用 .* wildcard,將這個(gè)式樣綁定到應(yīng)用程序中的每個(gè)組件,而不是將其綁定到每個(gè) JLabel和每個(gè) JButton。這個(gè)通配符告訴 Synth 外觀為每個(gè)組件指定一個(gè)默認(rèn)的 white Aharoni 字體。清單 3 展示了用于設(shè)置組件字體和顏色的完整 XML 代碼:


清單 3. 更改多個(gè)組件的字體和顏色
            <style id="default">
            <font name="Aharoni" size="14"/>
            <state>
            <color value="#FFFFFF" type="FOREGROUND"/>
            </state>
            </style>
            <bind style="default" type="region" key=".*"/>
            

使用圖像

圖 2中的 textfield邊框不是常規(guī)外觀的單像素矩形邊框。可以使用一個(gè)圖像來(lái)創(chuàng)建這些邊框。這不是我們所熟悉的概念 —— 圖像用在 button 和 label 中已經(jīng)有些時(shí)候了 —— 但您可以想像在哪些地方會(huì)出問(wèn)題。如何知道光標(biāo)移動(dòng)到什么地方,如何顯示文本,如何創(chuàng)建不同大小的文本域?這些問(wèn)題可以通過(guò) 圖像拉伸(image stretching)的概念來(lái)解決。一個(gè)圖像文件必須描述應(yīng)用程序中文本域各個(gè)邊的長(zhǎng)度,因此需要有一種方式來(lái)告訴 XML 文件如何適當(dāng)?shù)乩靾D像,以及如何處理常規(guī)的 textfield活動(dòng)(carat 和文本控制)。

幸運(yùn)的是,從早期帶皮膚的應(yīng)用程序起,就有一個(gè)方法可用于處理這種類型的拉伸。圖像必須分成 9 個(gè)區(qū)域 —— 頂部、右上、右部、右下、底部、左下、左部、左上和中間 —— 這些區(qū)域是通過(guò) XML 文件中的一個(gè)屬性來(lái)指定的。然后呈現(xiàn)程序可以通過(guò)一定的方式拉伸圖像,以適合指定的空間。圖 3 展示了文本域圖像是如何拉伸的。


圖 3. 在 Synth 中圖像如何拉伸 
Synth 外觀 

圖 3 中綠色填充區(qū)只會(huì)垂直拉伸。也就是說(shuō),當(dāng)文本域比圖像高的時(shí)候,這些區(qū)域就會(huì)變高。當(dāng)文本域比圖像長(zhǎng)的時(shí)候,那些紅色填充區(qū)只會(huì)水平拉伸。而黃色填充區(qū)則是大小固定的。不管文本域的大小如何,這些區(qū)域都會(huì)如它們?cè)趫D像文件中那樣顯示。因?yàn)檫@些區(qū)域不會(huì)拉伸,因此它們應(yīng)該包含所有畫布、特殊底色、陰影和任何一旦拉伸就會(huì)看起來(lái)很古怪的東西。最后,中間區(qū)域是可選的。您可以選擇畫出或者忽略該區(qū)域。在我們的例子中,文本域的中間被忽略。此后,呈現(xiàn)程序使用這個(gè)區(qū)域來(lái)處理文本控制和 carat。也就是說(shuō),使用一個(gè)圖像文件完全畫出文本域。

imagePainter標(biāo)簽提供了在外觀中使用圖像所需的所有信息。它只需要幾個(gè)屬性:

  • path :所使用的圖像的路徑。

  • sourceInsets :按像素計(jì)算的 insets,表示圖 3 中綠色區(qū)域的寬度和粉紅色區(qū)域的高度。它們依次映射到頂部、左部、底部和右部。

  • method :這也許是最令人費(fèi)解的屬性。它直接映射到 javax.swing.plaf.synth.SynthPainter類中的一個(gè)函數(shù)。這個(gè)類包含大約 100 個(gè)函數(shù),所有這些函數(shù)都以 paint開始。每個(gè)函數(shù)映射到在一個(gè) Swing 組件中某個(gè)特定的繪畫任務(wù)。您只需找到一個(gè)合適的函數(shù),然后去掉 paint字符串,并使隨后的首個(gè)字母為小寫形式,便可以設(shè)置該屬性。例如,paintTextFieldBorder是 textFieldBorder的屬性。呈現(xiàn)程序(renderer)負(fù)責(zé)剩下的工作。

  • paintCenter :該屬性允許您保留或者舍棄圖像的中間區(qū)域(例如在一個(gè)按鈕中)。在這個(gè)例子中,textfield舍棄了中間區(qū)域,以便顯示文本。

使用圖像畫邊框的最后一步是加大默認(rèn)的 insets,以便處理用來(lái)畫這些 insets 的圖像。如果沒有更改 insets,那么就看不見任何圖像。您需要添加一個(gè) <insets>標(biāo)簽來(lái)增加 insets,以便在其中畫出圖像。在大多數(shù)情況下,insets 的值應(yīng)該與在圖像中使用的 insets 的值相同。

清單 4 展示了用于裝載圖像的 XML 代碼。注意 sourceInsets如何確保圖像只有適當(dāng)?shù)牟糠直焕臁?/p>
清單 4. 裝載圖像
            <style id="textfield">
            <opaque value="true"/>
            <state>
            <font name="Aharoni" size="14"/>
            <color value="#D2DFF2" type="BACKGROUND"/>
            <color value="#000000" type="TEXT_FOREGROUND"/>
            </state>
            <imagePainter method="textFieldBorder" path="images/textfield.png"
            sourceInsets="4 6 4 6" paintCenter="false"/>
            <insets top="4" left="6" bottom="4" right="6"/>
            </style>
            <bind style="textfield" type="region" key="TextField"/>

處理不同的狀態(tài)

從前面的例子可以看到,<state>標(biāo)簽是定義一個(gè)組件的焦點(diǎn)所在。在清單 3和清單 4中,color 和 font 標(biāo)簽都處在 <state>標(biāo)簽內(nèi)。現(xiàn)在我將解釋 <state>標(biāo)簽的作用。

默認(rèn)狀態(tài)是在 <state>標(biāo)簽中沒有指定屬性,這對(duì)于定義文本域和 label 中的顏色和字體已經(jīng)足夠了,因?yàn)檫@兩種組件的狀態(tài)不會(huì)改變。但是在那些狀態(tài)會(huì)改變的組件中(例如按鈕),可以為每種狀態(tài)定義完全不同的外觀。每種狀態(tài)可以有它自己的顏色、字體和圖像。您可以比較登錄屏幕中 Cancel按鈕在默認(rèn)狀態(tài)(圖 4)和 mouse-over 狀態(tài)(圖 5)下的不同。


圖 4. DEFAULT 狀態(tài)下的 Cancel 按鈕
Default 狀態(tài) 

圖 5. MOUSE_OVER 狀態(tài)下的 Cancel 按鈕
Mouse over 狀態(tài) 

<state>標(biāo)簽只需要一個(gè) value屬性,該屬性定義了實(shí)際的組件狀態(tài)。如果沒有指定 value,如清單 3 和 4 所示,那么每種狀態(tài)都使用默認(rèn)值。如果指定 value屬性,那么可以選擇ENABLEDMOUSE_OVER、PRESSED、DISABLED、FOCUSED、SELECTED和 DEFAULT。這些選擇包含 Swing 中任何組件所有可能的狀態(tài)。您還可以在不同選擇間添加 and來(lái)組合各種狀態(tài)。例如,如果您想在鼠標(biāo)位于按鈕之上以及按鈕被按下的時(shí)候改變按鈕上的字體,那么可以使用狀態(tài)值 MOUSE_OVER and PRESSED。

清單 5 展示了用于處理 demo 應(yīng)用程序狀態(tài)的 XML。注意每種狀態(tài)是如何定義不同的圖像和文本顏色的。


清單 5. 處理狀態(tài)
            <style id="button">
            <state>
            <imagePainter method="buttonBackground" path="images/button.png"
            sourceInsets="9 10 9 12" paintCenter="true" stretch="true"/>
            <insets top="9" left="10" bottom="9" right="12"/>
            <font name="Aharoni" size="16"/>
            <color type="TEXT_FOREGROUND" value="#FFFFFF"/>
            </state>
            <state value="MOUSE_OVER">
            <imagePainter method="buttonBackground" path="images/button_on.png"
            sourceInsets="9 10 9 12" paintCenter="true" stretch="true"/>
            <insets top="9" left="10" bottom="9" right="12"/>
            <color type="TEXT_FOREGROUND" value="#FFFFFF"/>
            </state>
            <state value="PRESSED">
            <imagePainter method="buttonBackground" path="images/button_press.png"
            sourceInsets="10 12 8 9" paintCenter="true" stretch="true"/>
            <insets top="10" left="12" bottom="8" right="9"/>
            <color type="TEXT_FOREGROUND" value="#FFFFFF"/>
            </state>
            <property key="Button.margin" type="insets" value="0 0 0 0"/>
            </style>
            <bind style="button" type="region" key="Button"/>

處理 <state>標(biāo)簽的一個(gè)重要方面是知道哪些組件有哪些狀態(tài)。顯然,在這個(gè)例子中,按鈕可以擁有默認(rèn)狀態(tài)、鼠標(biāo)懸停(mouse-over)狀態(tài)和被按下(pressed) 狀態(tài)。對(duì)于這個(gè)例子,還可以定義一個(gè)聚焦(focused)和禁用(disabled)狀態(tài)。但是對(duì)于一個(gè)面板,選中(selected)狀態(tài)根本不適用,當(dāng)鼠標(biāo)處于面板之上時(shí)如果改變面板的狀態(tài),那么只能招來(lái)抱怨。

處理特定于組件的屬性

定義對(duì)每種組件都通用的 XML 屬性時(shí),總是忽略了一些特定于組件的屬性。例如 list 的行高、單選鈕的圖標(biāo)和菜單的箭頭圖標(biāo),這些都是特定于組件的屬性??梢远x的特定于組件的屬性有 100 多種,但是為每個(gè)這樣的屬性定義一個(gè) XML 屬性就有些過(guò)分了。因此,Synth XML 文件允許設(shè)置特定于組件的屬性。<property>標(biāo)簽就像一個(gè) Hashtable,它定義一個(gè)鍵 / 值對(duì)來(lái)設(shè)置屬性。

登錄屏幕示例的復(fù)選框演示了如何為特定于組件的屬性編寫代碼。通過(guò)定義 imageIcon,可以設(shè)置默認(rèn)狀態(tài)和選中狀態(tài)下的CheckBox.icon。這就像是翻遍 100 個(gè)屬性找到您想要的屬性那樣簡(jiǎn)單。

清單 6 展示了為登錄屏幕中特定于組件的屬性編寫代碼的 XML。注意要首先定義 imageIcon。然后,通過(guò)使用圖像圖標(biāo)的 ID,可以為復(fù)選框的每種狀態(tài)設(shè)置一個(gè)圖標(biāo)。


清單 6. 定義特定于組件的屬性
            <style id="checkbox">
            <imageIcon id="check_off" path="images/checkbox_off.png"/>
            <imageIcon id="check_on" path="images/checkbox_on.png"/>
            <property key="CheckBox.icon" value="check_off"/>
            <state value="SELECTED">
            <property key="CheckBox.icon" value="check_on"/>
            </state>
            </style>
            <bind style="checkbox" type="region" key="Checkbox"/>

使用定制 painter

定義 圖 2中登錄屏幕例子的最后工作是用曲線繪制漸變背景。用 XML 來(lái)實(shí)現(xiàn)這種背景似乎有些別扭,坦白地說(shuō),真是這樣。但這樣我便有機(jī)會(huì)展示 Synth,不限制您在 UI 設(shè)計(jì)中只使用圖像和簡(jiǎn)單的顏色。您可以使用它來(lái)畫任何東西。

Synth 允許重寫其 paint 方法(即在 javax.swing.plaf.synth.SynthPainter類中的方法),該方法繼承自 SynthPainter,它將覆蓋那些您想要定制繪畫方式的特定函數(shù)。在這個(gè)例子中,需要定義 paintPanelBackground方法,因?yàn)檫@種設(shè)計(jì)不能以 Synth XML 格式描述。

為了使用定制的 painter,或者在 XML 中以任何方式創(chuàng)建一個(gè)類,可以使用 <object>標(biāo)簽。<object>標(biāo)簽允許創(chuàng)建和保持用于彌補(bǔ) Synth 呈現(xiàn)程序的任何 Java 類。<object>標(biāo)簽帶有兩個(gè)元素:

  • class :將創(chuàng)建的類的全名。
  • id :用于在 XML 文檔中引用這個(gè)類實(shí)例的 ID 名。

通過(guò)使用對(duì)象,不僅可以創(chuàng)建 BackgroundPainter類的實(shí)例 —— 這個(gè)類將用于繪制背景,而且還可以創(chuàng)建 ColorUIResource類的實(shí)例,在這個(gè)類中可以定義背景顏色。想一想:在 BackgroundPainter類中定義背景中使用的顏色,這與 Synth 的目標(biāo)是矛盾的,Synth 的目標(biāo)是在一個(gè)外部 XML 文件中定義一切,而不是在一個(gè) Java 文件中進(jìn)行硬編碼。

使用定制 painter 的最后一步是告訴 Synth 呈現(xiàn)引擎,是您自己而不是 SynthPainter類來(lái)提供函數(shù)。在這個(gè)例子中,首先在BackgroundPainter類中定義 paintPanelBackground函數(shù),并讓 SynthPainter類定義剩下的繪畫函數(shù)。<painter> 標(biāo)簽讓您可以覆蓋 SynthPainter 函數(shù)。它帶有兩個(gè)元素:

  • method :定制 painter 應(yīng)該覆蓋的方法。從 使用圖像一節(jié)中您已經(jīng)得知,您可以在 javax.swing.plaf.synth.SynthPainter 類中找到這些函數(shù),但是應(yīng)該刪除每個(gè)函數(shù)開始部分的 paint字符串(例如,SynthPainter中的 paintPanelBackground在 XML 文件中應(yīng)該是 panelBackground)。

  • id:對(duì)將覆蓋此方法的類的引用。

為了在定制 painter 中使用顏色,必須將顏色保存在 javax.swing.UIDefaults類中。在清單 7 和清單 8 中可以看到,將顏色保存在UIDefaults中十分簡(jiǎn)單,對(duì)于那些接觸過(guò) UI 創(chuàng)建的人來(lái)說(shuō)應(yīng)該,應(yīng)該比較熟悉這些內(nèi)容。在 XML 文件中定義的鍵將成為UIManager中的引用,在 BackgroundPainter的 Java 代碼中,可以使用 UIManager來(lái)獲得顏色。

清單 7 展示了在例子應(yīng)用程序中使用定制 painter 的 XML 代碼。注意必須首先定義顏色。


清單 7. 使用定制 painter
            <style id="panel">
            <object id="background" class="demo.synth.BackgroundPainter"/>
            <object class="javax.swing.plaf.ColorUIResource" id="startColor">
            <int>30</int>
            <int>123</int>
            <int>235</int>
            </object>
            <defaultsProperty key="Panel.startBackground" type="idref" value="startColor"/>
            <object class="javax.swing.plaf.ColorUIResource" id="endColor">
            <int>1</int>
            <int>20</int>
            <int>80</int>
            </object>
            <defaultsProperty key="Panel.endBackground" type="idref" value="endColor"/>
            <painter method="panelBackground" idref="background"/>
            </style>
            <bind style="panel" type="region" key="Panel"/>

清單 8 展示了例子應(yīng)用程序的定制繪畫類的 Java 代碼:


清單 8. 定制繪畫的 Java 代碼
            public class BackgroundPainter extends SynthPainter
            {
            public void paintPanelBackground(SynthContext context,
            Graphics g, int x, int y,
            int w, int h)
            {
            Color start = UIManager.getColor("Panel.startBackground");
            Color end = UIManager.getColor("Panel.endBackground");
            Graphics2D g2 = (Graphics2D)g;
            GradientPaint grPaint = new GradientPaint(
            (float)x, (float)y, start,
            (float)w, (float)h, end);
            g2.setPaint(grPaint);
            g2.fillRect(x, y, w, h);
            g2.setPaint(null);
            g2.setColor(new Color(255, 255, 255, 120));
            g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            CubicCurve2D.Double arc2d = new CubicCurve2D.Double(
            0, h/4, w/3, h/10, 66 * w, 1.5 * h, w, h/8);
            g2.draw(arc2d);
            g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
            }
            }

更高級(jí)的設(shè)置

本節(jié)包含兩個(gè)超出登錄屏幕例子范圍的技術(shù)。在創(chuàng)建您自己的 Synth 外觀時(shí),您可能發(fā)現(xiàn)這兩項(xiàng)技術(shù)很有用。

繪制非 Swing 組件

可以改變每個(gè) Swing 組件的外觀這一點(diǎn)雖然很棒,但是還應(yīng)該能夠改變其他組件 —— 開發(fā)人員創(chuàng)建的用于填補(bǔ) Swing 空缺的組件 —— 的外觀。在這種情況下,<bind>標(biāo)簽需要作出改變,以反映正在繪制的不是一個(gè) Swing 組件。type屬性可以有兩種值:如果映射到一個(gè) Swing 組件,則該值為 region,如果映射到非 Swing 組件,則該值為 name。因此,如果將 <bind>標(biāo)簽變?yōu)?nbsp;<bind style="mystyle" type="name" key="Custom.*"/>,則會(huì)改變每個(gè)類名以 Custom開始的組件(例如,CustomTextFieldCustomLabel),使它們使用 mystyle式樣。

式樣的分層結(jié)構(gòu)

除了在創(chuàng)建 XML 文件時(shí)使用 KISS 式樣之外,還可以構(gòu)建分層次的一些式樣,并將這些式樣應(yīng)用于組件中。清單 9 應(yīng)該可以清楚地演示這一點(diǎn)。注意,Synth 使用最后定義的屬性來(lái)顯示組件。


清單 9. 分層結(jié)構(gòu)的例子
            <style id="base">
            <color value="BLACK" type="BACKGROUND"/>
            <state>
            <font size="14"/>
            </state>
            </style>
            <bind style="base" type="region" key=".*"/>
            <style id="sublevel" clone="base">
            <color value="RED" type="BACKGROUND"/>
            </style>
            <bind style="sublevel" type="region" key="Label"/>

清單 9 中的代碼使每個(gè)組件有一個(gè)黑色的背景,字體大小為 14,但 label 組件除外,label 組件擁有紅色的背景。通過(guò)克隆sublevel中的 base式樣,清單 9 復(fù)制了整個(gè)式樣。然后,您可以覆蓋所需的任何特定屬性。

檢驗(yàn) Synth 的性能、可靠性和效率

至此,您已經(jīng)看到如何創(chuàng)建用于 Synth 的 XML 文件,以及如何通過(guò)更改字體、更改顏色和添加圖像來(lái)創(chuàng)建定制的外觀,但對(duì)于 Synth 可能還有些疑問(wèn)。如果您使用 Swing 已經(jīng)有一段時(shí)間,那么我可以肯定,您首先想到的是性能問(wèn)題。我設(shè)計(jì)了一些性能測(cè)試,這些測(cè)試表明,Synth 不會(huì)令您的 UI 慢如蝸牛。為了調(diào)查您可能看到的問(wèn)題(并討論我在使用 Synth 時(shí)已經(jīng)碰到過(guò)的一些問(wèn)題),我查看了 Java Bug Parade (請(qǐng)參閱 參考資料)。最后,我將回答最重要的問(wèn)題 —— Synth 真的可以節(jié)省您的時(shí)間嗎?

裝載那么多圖像會(huì)不會(huì)使 Synth 變得更慢?

為了回答這個(gè)問(wèn)題,我創(chuàng)建了兩個(gè)測(cè)試,并讓您更深切地體會(huì) Synth 在性能方面與其他外觀的比較。第一個(gè)測(cè)試將測(cè)試示例登錄應(yīng)用程序的裝載時(shí)間。該測(cè)試裝載 6 個(gè) Synth 圖像,并將這個(gè)裝載時(shí)間與一個(gè)開發(fā)人員可能創(chuàng)建的一般屏幕的裝載時(shí)間進(jìn)行比較。第二個(gè)測(cè)試是關(guān)于裝載時(shí)間的壓力測(cè)試 —— 一個(gè)幀中有 100 多個(gè)組件。

兩個(gè)測(cè)試都將測(cè)試 Ocean 和 Motif 外觀的裝載時(shí)間,以便進(jìn)行比較。為了公正起見,我在三種機(jī)器上運(yùn)行了這兩個(gè)測(cè)試 —— 一種是安裝 Windows XP 的手提電腦,一種是 SuSE Linux box,還有一種是 Red Hat Linux box。結(jié)果顯示在表 1 和表 2 中。


表 1. 登錄屏幕的平均裝載時(shí)間
機(jī)器配置 Ocean Motif Synth
Windows XP - 1.7GHz - 2GB RAM .32 seconds .29 seconds .57 seconds
SuSE Linux 9.0 - 3.3GHz - 2GB RAM .23 seconds .20 seconds .45 seconds
Red Hat Linux 3.0 - 1.4GHz - 512MB RAM .37 seconds .32 seconds .61 seconds



表 2. 包含 100 個(gè)組件的屏幕的平均裝載時(shí)間
機(jī)器配置 Ocean Motif Synth
Windows XP - 1.7GHz - 2GB RAM .33 seconds .32 seconds .34 seconds
SuSE Linux 9.0 - 3.3GHz - 2GB RAM .23 seconds .23 seconds .30 seconds
Red Hat Linux 3.0 - 1.4GHz - 512MB RAM .40 seconds .40 seconds .43 seconds

您可以看到,Synth 外觀的裝載時(shí)間只比 Ocean 和 Motif 慢一點(diǎn)點(diǎn)。但是請(qǐng)注意,登錄屏幕與壓力測(cè)試會(huì)比裝載更慢一些。乍一看來(lái),這似乎很奇怪,但如果仔細(xì)研究,便可以發(fā)現(xiàn)起因。壓力測(cè)試沒有裝載復(fù)選框中所使用的圖像,而登錄屏幕卻裝載了這些圖像。據(jù)此可以下結(jié)論,在 Synth 外觀中使用的每個(gè)附加圖像增加了裝載時(shí)間。與含有兩個(gè)使用兩種不同圖像的組件的應(yīng)用程序相比,使用相同圖像的 100 個(gè)組件裝載起來(lái)要更快一些。減少所使用圖像的數(shù)量可以提高 Synth 裝載時(shí)間方面的性能。

Synth 是不是像 Swing 一樣,在第一次發(fā)布時(shí)滿是 bug ?

根據(jù) Sun Java 開發(fā)者網(wǎng)站上 Bug Parade 的評(píng)判,Synth 看上去是一個(gè)比較干凈、沒有 bug 的產(chǎn)品。然而,沒有哪個(gè)軟件是完美的。Synth 曾經(jīng)有 125 個(gè) bug,這與 Synth 處理 JTabbedPane的方式不成比例。因此,如果您經(jīng)歷到一些問(wèn)題,不要感到驚訝。然而,根據(jù) Sun 的辯護(hù),這些缺陷都處于“關(guān)閉(Closed)”狀態(tài)。但通常的情況是,如果以前存在某些問(wèn)題,那么這些問(wèn)題在將來(lái)也很可能會(huì)出現(xiàn)。

雖然 bug 數(shù)據(jù)庫(kù)為 Synth 賦予了一個(gè)相對(duì)干凈的形象,我在處理登錄屏幕的時(shí)候還是碰到一些問(wèn)題。我第一次嘗試更改 JPanel背景顏色時(shí)遭到失敗。我創(chuàng)建了一個(gè)特定于 JPanel的式樣,并將其綁定到所有 JPanel,但這樣行不通。而當(dāng)我決定使用自己的定制 painter 時(shí),事情就解決了。

一個(gè)更大的問(wèn)題是當(dāng)狀態(tài)改變時(shí)對(duì)組件進(jìn)行重新繪制。在處理按鈕及其狀態(tài)時(shí),我發(fā)現(xiàn),按鈕上的文本不能正確地改變顏色。當(dāng)初始化時(shí),作為默認(rèn)顏色的白色沒有如期顯示,并且直到觸發(fā)了狀態(tài)變化之后才出現(xiàn),然后就被重新設(shè)置為默認(rèn)顏色。如果仔細(xì)研究關(guān)于 Synth 的文檔,就可以發(fā)現(xiàn)這個(gè)小花絮:“雖然可以為每種狀態(tài)提供不同的字體,但在一般情況下,當(dāng)組件的狀態(tài)變化時(shí),組件不會(huì)重新生效,所以,如果您試圖為不同狀態(tài)使用有明顯不同大小的字體時(shí),有可能會(huì)遇到字體大小的問(wèn)題”。聽起來(lái)似乎它們遇上了試圖讓 Synth 使用老的 Swing 代碼的問(wèn)題。因此,如果要在狀態(tài)改變時(shí)更改字體,那么要小心。

Synth 看上去的確很少有 bug。但如果隨處出點(diǎn)小問(wèn)題,那些本應(yīng)該行得通的代碼就會(huì)行不通,我不會(huì)對(duì)此感到驚訝。不過(guò),變通的辦法不難找到。對(duì)于在工作中碰到的每個(gè)問(wèn)題,我總能找到一個(gè)變通的辦法。

利用 Synth 可以創(chuàng)建出完全專業(yè)的外觀嗎?

回答是肯定的。Java 1.4 中發(fā)布的 GTK+ 和 Windows XP 外觀就完全是用 Synth 創(chuàng)建的。(那時(shí)它不是一個(gè)已公布的 API。) 所以這方面顯然沒有問(wèn)題。

用 Synth 創(chuàng)建一個(gè)完整的外觀比用 Java 代碼編寫這樣的外觀要快多少?

這很容易計(jì)算。這兩種方法各自都包含兩個(gè)步驟:

  1. 創(chuàng)建外觀,這通常是由圖形設(shè)計(jì)人員負(fù)責(zé)的工作。
  2. 將圖形界面轉(zhuǎn)化成代碼。

不管是用 Java 編寫代碼還是使用 Synth,圖形界面設(shè)計(jì)這部分工作所花的時(shí)間是相同的。根據(jù)我創(chuàng)建定制外觀的經(jīng)驗(yàn),我估計(jì)為一個(gè)應(yīng)用程序創(chuàng)建一個(gè)完整的外觀需要兩個(gè)圖形設(shè)計(jì)人員兩周的時(shí)間。也就是說(shuō),圖像設(shè)計(jì)工作需要 4 人一周(person-week)的人力。

通常,根據(jù)我的經(jīng)驗(yàn),通過(guò)類繼承的方式將圖形界面翻譯成立即可用的外觀需要三個(gè) Java 編程人員花大約兩個(gè)月的時(shí)間。也就是說(shuō),編寫 Java 代碼需要 6 個(gè)人一個(gè)月(person-month)的人力。加上圖形界面設(shè)計(jì)工作,通過(guò)重寫 UI 類,用 Swing 創(chuàng)建一個(gè)完全定制的外觀總共需要 7 個(gè)人一個(gè)月的工作量。這些數(shù)據(jù)有助于您明白為什么 Internet 上可供下載的定制外觀是那么少。

通過(guò)將圖形界面轉(zhuǎn)換成一個(gè) XML 文件,Synth 可以節(jié)省大量的時(shí)間。通過(guò) Java 編程創(chuàng)建外觀需要 6 個(gè)人一個(gè)月的工作量,而一個(gè)開發(fā)人員將圖形界面轉(zhuǎn)換成 Synth XML 文件只需兩個(gè)星期。用 Synth 創(chuàng)建完整外觀所需的工作量減少到僅僅 6 個(gè)人一周的工作量 —— 通過(guò)使用 Synth 節(jié)省了超過(guò) 5 個(gè)月的時(shí)間。對(duì)于一個(gè)由兩個(gè)圖形設(shè)計(jì)師和兩個(gè)程序員組成的團(tuán)隊(duì),在短短三個(gè)星期內(nèi)便可以創(chuàng)建出一個(gè)完整的 Synth 外觀。

結(jié)束語(yǔ)

Synth 將皮膚的概念引入到 Swing 中。相對(duì)于傳統(tǒng)的用 Java 代碼編寫定制外觀的方法,Synth 最大的優(yōu)勢(shì)是節(jié)省時(shí)間。一個(gè)完整的 Swing 外觀可以在不到一個(gè)月的時(shí)間里完成,這比用 Java 語(yǔ)言編程的方法要快 5 倍。對(duì)于有干勁的開發(fā)人員,在用 Java 代碼編寫一個(gè)外觀的時(shí)間里,他可以創(chuàng)建 5 個(gè) Synth 外觀。

然而,Synth 并非毫無(wú)瑕疵。通過(guò)編寫 Java 代碼覆蓋 Swing 外觀,可以同時(shí)改變應(yīng)用程序的外觀和 感覺。而 Synth 只允許改變應(yīng)用程序的外觀。這是一個(gè)很大的不同之處。外觀是指應(yīng)用程序中使用的顏色、字體和圖形。另一方面,感覺則對(duì)應(yīng)于應(yīng)用程序在交互期間展現(xiàn)出來(lái)的行為 —— 這里指單擊一下鼠標(biāo)右鍵,那里按下一個(gè)鍵。例如,如果您想改變一個(gè) JList的行為,希望通過(guò)單擊鼠標(biāo)左鍵選中條目,然后再通過(guò)單擊鼠標(biāo)右鍵來(lái)刪除條目,那么用 Synth 是無(wú)法做到這些的。您需要為新的外觀編寫 Java 代碼。Synth 實(shí)際上應(yīng)該稱為一種新的 Swing 外觀,而不是一種普通外觀。通過(guò) Synth 可以快速改變 UI 的外觀,但 UI 的感覺永遠(yuǎn)都是默認(rèn)的 Swing 感覺。

當(dāng)然,如果您想通過(guò)為應(yīng)用程序提供新的外觀來(lái)使之整潔漂亮,或者渴望看到比令人討厭的 Metal 外觀(謝天謝地,在 Java 5.0 中它已成為歷史)更好的 Swing 應(yīng)用程序外觀,那么 Synth 是很好的一個(gè)選擇。它不存在性能問(wèn)題,并且看上去 bug 也很少。Sun 已經(jīng)表示,通過(guò)發(fā)布 GTK+ 外觀,用 Synth 可以創(chuàng)建完整的外觀。

令人吃驚的是,Synth 文檔和實(shí)例現(xiàn)在還很少。閱讀本文之后,對(duì)于 Synth 的工作原理您應(yīng)該有一個(gè)更深的理解,并且能夠使用一個(gè)組件一個(gè)樣式標(biāo)簽(one-style-tag-per-one-component)的設(shè)計(jì)來(lái)生成一個(gè)完整的 Synth XML 文檔。Synth 的繼承和分層模型為創(chuàng)建 style 標(biāo)簽提供了更強(qiáng)大的方法,但沒有它們?nèi)匀豢梢詣?chuàng)建完整的外觀。理想情況是:隨著對(duì) Synth 認(rèn)識(shí)的加深,Swing UI 社區(qū)將出現(xiàn)皮膚數(shù)量的大爆炸。有了數(shù)百個(gè)可供選擇的外觀,通常那些加在 Swing 應(yīng)用程序身上的“長(zhǎng)相恐怖”、“丑陋”之類的責(zé)罵之詞也將永遠(yuǎn)消失。


下載

名字大小下載方法
synth.jar 21 KB HTTP

關(guān)于下載方法的信息


參考資料

  • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文。

  • 單擊本文頂部或底部的 Code圖標(biāo)(或查看 下載小節(jié)),以便下載本文中所使用的代碼示例。

  • 請(qǐng)務(wù)必參閱 Synth Javadoc。

  • Synth File Format文檔對(duì) Synth XML 文件中所有可能出現(xiàn)的情況提供了完整的介紹。

  • Synth Component Specific Properties文檔列出了在 XML 文件中可以設(shè)置的 100 多個(gè)特定于組件的屬性。

  • Region類文檔列出了所有 Swing 組件以及可以在 Synth XML 文件中改變的部分組件。

  • SynthPainter類文檔列出了在 imagePainter和 painter標(biāo)簽中可以描述的所有函數(shù)。

  • SourceForge 上提供的定制 Swing 外觀包括 Liquid、 Napkin、oyoahaSkinGTK+ PluggableVira和 Office外觀。

  • John Zukowski 撰寫的 Taming Tiger系列中的文章“Ocean and Synth meet Metal”(developerWorks, 2004 年 8 月)探索了 Java 5.0 中的新外觀 Ocean 和 Synth。

  • Scott Violet 所著的“The Synth Look and Feel”是 Sun 關(guān)于 Synth 的最初表述,時(shí)間要追溯到 2004 年早期 Synth 剛被引入之際。

  • 請(qǐng)參閱可搜索獲得的 Java Bug Database(即 Java Bug Parade)。

  • 請(qǐng)參閱 Michael Abernethy 關(guān)于 Swing 和 UI 開發(fā)的其他文章。“用 TableModel Free 框架簡(jiǎn)化 Swing 開發(fā)”(developerWorks, 2004 年 8 月)介紹了 TableModel Free 框架,該框架消除了結(jié)合使用 TableModels 和 Swing JTables 的需要。“Go state-of-the-art with IFrame”(developerWorks, 2004 年 3 月)展示了如何使用 IFrame 架構(gòu)將 JFrame 窗口轉(zhuǎn)化成定制的應(yīng)用程序窗口。

  • IBM alphaWorks 上的 IBM Reflexive User Interface Builder(RIB)是一個(gè)應(yīng)用程序和工具箱,它使您可以為基于描述性 XML 文檔的 Java Swing 和 Eclipse SWT 構(gòu)造和交付 GUI。“Reflexive User Interface Builder 簡(jiǎn)介”(developerWorks, 2004 年 8 月)探索了基于 Java 2 Swing 框架的一些 RIB 例子。

  • 在 developerWorks Java 技術(shù)專區(qū)可以找到關(guān)于 Java 編程的各個(gè)方面的文章。

  • 請(qǐng)參閱 Developer Bookstore,以獲得技術(shù)性書籍的完整清單,其中包括數(shù)百篇 Java 相關(guān)主題的書籍。

關(guān)于作者

Michael Abernethy

Michael Abernethy 目前是 WebSphere 系統(tǒng)管理功能性測(cè)試團(tuán)隊(duì)的負(fù)責(zé)人。之前,他在 WebSphere Services 團(tuán)隊(duì)工作了 4 年,一直在 WebSphere 上編寫、部署應(yīng)用程序。在業(yè)余時(shí)間里,他還參與了 Swing 和 UI 開發(fā)。您可以通過(guò) Michael 的郵箱 mabernet@us.ibm.com 與他聯(lián)系。

為本文評(píng)分

平均分 4 星 共 10 個(gè)評(píng)分 平均分 (10個(gè)評(píng)分)

1 星1 星
2 星2 星
3 星3 星
4 星4 星
5 星5 星

評(píng)論

請(qǐng) 登錄 或 注冊(cè) 后發(fā)表評(píng)論。

注意:評(píng)論中不支持 HTML 語(yǔ)法


剩余 1000 字符




在檢索評(píng)論時(shí)出錯(cuò),請(qǐng)稍后刷新。

標(biāo)簽

Help
使用搜索文本框在 My developerWorks 中查找包含該標(biāo)簽的所有內(nèi)容。熱門標(biāo)簽 顯示了特定專區(qū)最受歡迎的標(biāo)簽(例如 Java technology,Linux,WebSphere)。我的標(biāo)簽 顯示了特定專區(qū)您標(biāo)記的標(biāo)簽(例如 Java technology,Linux,WebSphere)。

熱門標(biāo)簽

熱門標(biāo)簽結(jié)束

我的標(biāo)簽

要訪問(wèn)我的標(biāo)簽,請(qǐng)登錄

查看熱門標(biāo)簽


我的標(biāo)簽結(jié)束

更多更少

查看方式 | 列表


From: http://www.ibm.com/developerworks/cn/java/j-synth/

posted on 2011-12-03 09:57 逛奔的蝸牛 閱讀(1153) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Java
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久九九国产精品怡红院| 欧美日韩在线精品| 中文欧美在线视频| 巨胸喷奶水www久久久免费动漫| 制服丝袜亚洲播放| 亚洲最新中文字幕| 9人人澡人人爽人人精品| 99精品免费| 亚洲欧美第一页| 亚洲综合色婷婷| 新67194成人永久网站| 欧美在线视频免费播放| 老司机久久99久久精品播放免费| 久久一区二区三区四区| 欧美大片免费久久精品三p | 亚洲一区三区在线观看| 中日韩在线视频| 亚洲女性喷水在线观看一区| 影音先锋国产精品| 欧美a级在线| 欧美日韩福利在线观看| 国产精品久久毛片a| 影音先锋成人资源站| 一区二区三区欧美激情| 欧美中文字幕精品| 欧美大片免费久久精品三p | 午夜精品久久久久久久99热浪潮| 亚洲视频在线观看三级| 欧美在线日韩在线| 美国成人直播| 亚洲精品九九| 亚洲综合日韩| 久久久久久亚洲精品不卡4k岛国| 欧美紧缚bdsm在线视频| 国产女人水真多18毛片18精品视频| 在线日韩av片| 午夜精品剧场| 久久综合成人精品亚洲另类欧美 | 欧美在线看片a免费观看| 亚洲第一中文字幕| 亚洲天堂激情| 久久精品国产91精品亚洲| 欧美激情二区三区| 性欧美办公室18xxxxhd| 欧美日韩免费区域视频在线观看| 国产综合久久久久久鬼色| 黄色亚洲在线| 亚洲欧美久久| 亚洲国产合集| 久久精品国产一区二区三| 亚洲国产欧美一区| 老司机成人网| 国产女人精品视频| 欧美一区二区三区婷婷月色| 欧美国产一区二区三区激情无套| 久久成人18免费网站| 欧美日韩大陆在线| 日韩午夜精品| 久久色在线播放| 亚洲一区二区三区免费视频| 美女黄色成人网| 国产一区二区三区不卡在线观看| 日韩亚洲欧美一区二区三区| 亚洲国产高清自拍| 欧美有码视频| 伊人春色精品| 欧美亚洲日本国产| 亚洲一区二区在线看| 欧美日韩直播| 亚洲一区日韩在线| 亚洲精品无人区| 国产精品久久久久久户外露出| 亚洲精品永久免费| 久久躁日日躁aaaaxxxx| 久久精品国产清自在天天线| 国产欧美一二三区| 久久se精品一区精品二区| 亚洲伊人一本大道中文字幕| 欧美日韩国产亚洲一区| 亚洲激情在线观看| 亚洲欧美影院| 久久久久久久综合色一本| 狠狠色综合一区二区| 免费欧美视频| 久久久久五月天| 亚洲精品在线三区| 亚洲国产成人av| 欧美香蕉视频| 亚洲图片在线观看| 午夜久久久久久| 中文在线资源观看网站视频免费不卡 | 欧美专区福利在线| 亚洲国产高清在线| 亚洲国产精品久久久久秋霞蜜臀| 欧美精品一区三区| 亚洲精品视频免费在线观看| 中文精品视频| 国产欧美一区二区三区视频| 另类av一区二区| 免费不卡亚洲欧美| 欧美影院久久久| 久久成人国产| 欧美亚韩一区| 欧美777四色影视在线| 亚洲男女毛片无遮挡| 在线观看不卡| 最新国产乱人伦偷精品免费网站| 国产亚洲成av人片在线观看桃| 久久久久久高潮国产精品视| 欧美精品v日韩精品v国产精品| 亚洲午夜精品一区二区| 久久久精品日韩| 亚洲国产你懂的| 亚洲一区二区三区免费观看| 尤物99国产成人精品视频| 亚洲在线观看视频网站| 亚洲大胆在线| 新狼窝色av性久久久久久| 亚洲国产日韩欧美在线99| 欧美在线一二三四区| 99综合在线| 欧美激情中文字幕乱码免费| 久久久99国产精品免费| 欧美激情在线有限公司| 国产一区二区三区四区在线观看 | 狠狠色狠狠色综合系列| 亚洲国产精品一区| 狠狠色丁香久久婷婷综合_中| 欧美国产第二页| 国产色视频一区| 亚洲国产欧美一区二区三区丁香婷| 国产揄拍国内精品对白| 亚洲精品久久久蜜桃| 在线成人h网| 亚洲一区在线直播| 亚洲欧美中文另类| 美乳少妇欧美精品| 免费亚洲一区二区| 开心色5月久久精品| 国产精品igao视频网网址不卡日韩| 亚洲激情成人网| 在线成人免费观看| 久久乐国产精品| 午夜精品短视频| 国内不卡一区二区三区| 一本色道**综合亚洲精品蜜桃冫| 欧美第一黄色网| 免费日韩精品中文字幕视频在线| 国产自产高清不卡| 亚洲永久精品大片| 久久精品一区四区| 国产精品一区二区三区久久久| 亚洲小说欧美另类婷婷| 99re8这里有精品热视频免费| 欧美日韩国产精品自在自线| 欧美韩日一区二区三区| 99精品欧美一区二区三区| 蜜桃av综合| 亚洲毛片在线观看.| 日韩网站在线| 国产精品一区毛片| 午夜精品久久久久久久99黑人 | 免费人成精品欧美精品| 亚洲区国产区| 欧美成人午夜| 亚洲午夜久久久久久久久电影网| 日韩午夜电影| 国产精品自在欧美一区| 亚洲午夜免费福利视频| 久久久久久久久蜜桃| 国产亚洲精品一区二区| 久久综合色8888| 欧美激情一区二区三区在线| 亚洲无线一线二线三线区别av| 国产精品v欧美精品v日韩精品| 午夜精品国产精品大乳美女| 久久久久久91香蕉国产| 在线视频亚洲欧美| 国产精品视频网站| 免费观看不卡av| 日韩视频一区二区| 久久久久久夜精品精品免费| 一区二区在线不卡| 欧美肉体xxxx裸体137大胆| 亚洲制服av| 欧美激情视频网站| 亚洲天堂成人| 亚洲精品午夜精品| 国产精品日韩欧美一区二区三区 | 亚洲午夜精品视频| 伊人久久亚洲影院| 欧美成人一区二区三区片免费 | 欧美二区在线看| 欧美成人精品在线观看| 午夜精品久久久久久| 国产精品乱码一区二三区小蝌蚪| 欧美成人a视频| 亚洲国产精品va在线看黑人动漫 | 亚洲免费观看视频| 亚洲午夜久久久久久久久电影院 |