Swing第三刀:做套ERP,要配得上我的登錄界面!
在《Swing第一刀》和《Swing第二刀》中提到了一個我正在折騰的一個ERP界面小框架,不少童鞋表示灰常感興趣,這里繼續和大家分享一個新的小進展:一個全新的登錄界面。也許可以改變一些你對Swing和UI設計的看法。
為啥又是登錄界面
登錄界面是一個軟件系統中最簡單的一個界面,也是最重要的一個界面。為什么?因為它是用戶看到的第一個界面;因為它是用戶每天都要看的界面。要想讓用戶愛上你的軟件,一個美觀、耐看、友好的登錄界面是一個重要的前提。下功夫做一個好的登錄界面,你的系統就成功了一半。這就是為什么很多美工初學者總是喜歡從模仿、創作登錄界面開始。
不信咱仔細瞅瞅。瞧,這是你做的登錄界面:
抓圖來自網絡,版權歸作者所有
瞧,這是我的登錄界面(來自本文案例):
美工設計效果圖
程序實際運行圖
哪個更討人喜歡?你是鳳姐,我是志玲姐;你是芙蓉姐姐,我是Lady Gaga姐,不大好比哦。嗯,正如你所說,是“各有所長”吧!不過,除非你有嚴重的自虐傾向,否則你的選擇應當跟我一樣。
估計有人已經在叫了:“你這是Swing做的?”“用JNI了?”“這透明圓角,這透明文字,這陰影…咋弄的?”“快扔源碼!”“Flex做這個太容易了”“花里胡哨有P用?”…你看,程序員的“老毛病”又來了。
不錯,這個界面是Swing做的;也沒有JNI(從來也沒整過JNI);代碼也極其簡單,就是窗戶紙一捅就破;也談不上多么高深技術;也無意和Flex、.NET之類的技術相比較。我只是想讓大家更多的了解Java和Swing的能力,以及我們對待技術和UI的態度。如果急吼吼的看一下代碼run一下demo “哦!”一下,繼續扭頭堆代碼,那我就算是白費這勁了。
Swing變冷了,還是變熱了
JavaEye上最近關于Swing的帖子絡繹不絕,不少人驚呼:Swing似乎難道又要熱了?面對這樣的疑問,國內著名Java專家、《程序員》雜志特約撰稿人、美國密蘇里州立大學計算機科學與技術學院客座教授、JavaEye創始人Robbin卻表現的很坦然,他告訴我說“對于我們Swing程序員來說,這是很正常的事情。”在Robbin看來,這是再正常不過的現象了。“Swing本來就很強大, 而最近頻頻出現的熱帖也不是因為Swing發展加快了,只是媒體關注變多了。我使用Swing已經30多年了,在我上大學讀書開始就接觸Swing,”Robbin說,“毫無疑問,我相信Swing會取得成功”。“美洲大陸已經存在上億年了,只是哥倫布發現了它而已。所以,不必驚呼新大陸很美很新鮮,只能怪我們很傻很無知。”
(注:以上訪談純屬自編自導個人杜撰,如有雷同,純屬巧合。)
Swing做界面,是快刀還是鈍刀
用Swing做一個上面的登錄界面,需要多少時間?答絕不可能是幾分鐘。而要做到“技驚四座、一鳴驚人”,用“鬼斧神工”搬的精致來徹底討好和吸引用戶,就必須下足功夫。這往往不是程序員一個人在戰斗!美工師、程序員、熟悉業務的系統工程師…需要一起雕章琢句、廢寢忘食。然而,好的設計帶來的回報也會遠遠超過你的想象。所以,討論所謂**技術更“快”和更“慢”毫無意義,當初快如閃電的PB、Delphi如今也難覓蹤跡。所以,程序的價值和回報不在于速度,更多的在于我們的態度:精雕細琢成大器,還是粗制濫造堆垃圾呢?
程序員:我們并不在意程序是否好看
真的嗎?你真的這樣看嗎?真是很可怕的一件事情。當然,在Java領域里,只要一提到J2EE、企業應用,大家就會立刻想到業務、重構、集群、云計算、松耦合、SOA、線程池、EJB、JPA、Hibernate、Spring、JBPM、SSH、JBoss、Lucene、Seam數之不盡的buzzword和framework。很多程序員浸淫在各種框架之中樂此不疲、無法自拔,甚至從來沒有寫下過例如“public class MyClass extends *** implements ***”親手create過一個自己的class,實在是挺可怕。軟件說到底是拿來“用”的,不是用來“學”的。再多的框架和技術,最終還是要為用戶服務,和用戶交互就要有好的UI。UI無非就是User Interface,是一切between在用戶和機器之間的東西。忽視UI技術和UI設計,確實對一個程序員的成長是不利的。
程序員:我們需要左腦還是右腦
美國的斯佩里和日本角田等人的研究表明,左腦支配右半身的神經和感覺,是理解語言的中樞,主要完成語言、邏輯、分析、代數的思考認識和行為,它是進行有條不紊的條理化思維,即邏輯思維,是程序員的典型活躍區域,也是我們的“國家級重點保護區”。喜歡踢足球的程序員們注意了:盡量不要頭球;用也要盡量用右腦;用左腦最好戴個摩托頭盔先,別把吃飯的家什整壞了。右腦支配左半身的神經和感覺,是沒有語言中樞的啞腦,但有接受音樂的中樞,主要負責可視的、綜合的、幾何的、繪畫的思考認識和行為,也就是負責鑒賞繪畫、觀賞自然風光、欣賞音樂,憑直覺觀察事物,縱觀全局,把握整體。右腦具有類別認識能力、圖形認識、空間認識、繪畫認識、形象認識能力,是形象思維。看上去右腦似乎也恰恰是咱們程序員的軟肋。但這能很好的解釋為什么程序員彈吉他蓬蓬作響卻像彈棉花,拉二胡一身汗還是像驢叫,跟大媽吵架被罵的插不上嘴,這不能怨我們自己,原來是有生理原因的。
但是從事邏輯思維的程序員恰恰還需要有很強的創造力。而右腦在創造性工作中具有不可替代的作用。美國科學家在《思維的藝術》一書中,將創造過程分為四個階段,即準備階段、醞釀階段、閃光階段和驗證階段。這其中,直覺和頓悟是創造的泉源,但是它必須經過語言的描述和邏輯的檢驗才具有價值。左右腦的這種協同關系是創造力的真正基礎和源泉。如果我們一味的抱怨右腦發育不良,甚至自認右腦天生腦殘,那就只能一生一世做一個不折不扣的coder編碼機器了。
勤奮和聰明,哪個更重要
這個話題有點大。作為程序員,不管缺了哪樣,都是致命的,還不如早點放棄這條路的好。這論調似乎很沒新意,如果非要選擇一個的話,我選擇勤奮。不聰明,哪怕做不了程序員還可以干其他的嘛,比如當個官員、干個城管、踢個足球啥的(僅限國足,男隊)。但要是不勤奮,恐怕就一事無成了,想做貪官都難(不勤奮你以為就能貪的到啊)。勤奮又聰明,再加上那么一點點激情,你就厲害了!
細節是魔鬼:登錄還是登陸?
這真是一個問題。Google一下看,很多人都在爭論和分析是“登陸”還是“登錄”,這似乎不僅僅是一個軟件問題,而變成了一個語言學術研究問題。《北京晚報》還在2007年5月專門發表解青的署名文章《“登陸網站”還是“登錄網站”》進行深入的分析。經過一系列復雜的公式推導邏輯運算,作者最終得出的結論是應當使用“登陸網站”。我們程序員語言能力一向很差,雖然“登陸網站”還說得過去,但是軟件的login界面如果用“登陸”那我們就會感覺有點點怪了,總讓人想起彪炳史冊的偉大的“諾曼底”這個地方。毫無疑問,“登錄”是更正確的選擇。別說程序員不用這樣較真,別說這個跟我程序員無關。如果哪個大型軟件的login界面用“登陸”兩個字,請你立刻鄙視它、嘲笑它、諷刺它、挖苦它、打擊它、永遠別買它、永遠離開它,離啊離開它,離~開~它!不服氣現在就重啟系統看看Windows界面用的什么,去Google一下主流軟件抓圖用的是什么。如果你做的軟件真的由此而不能賣出去,此事也與你無關嗎?細節決定成敗,細節就是魔鬼,這不是口號。你不提防它,它就吃掉你。
Swing與不規則窗體的“哥德巴赫猜想”
不管你信不信,“如何用Swing做不規則窗體”是近10年來Swing被最常問到的幾個問題之一。Google上搜索“Swing 不規則 窗體”有將近10萬個中文結果,而且從1999年就有人在致力于這一問題的解決方法,至今甚至已經出現多種不同“流派”的解決方案。
第一招:抓屏法
最早出現的比較靠譜的一個解決方法出現在《Swing Hacks》一書中。《Swing Hacks》一書是著名的圖書出版商O’Reilly在2007年出版的Swing技巧圖書,作者是Swing開發組成員Joshua Marinacci,他也是目前JavaFX開發組成員。在這本書的41章中介紹了一種制作透明窗體和不規則窗體的方法。由于當時的JDK功能所限,Java本身并未提供任何直接的API來制作半透明或不規則窗體,所以這一技巧是利用Robot來截取屏幕原有內容生成內存圖片,然后將圖片顯示在Swing的窗體中作為背景,“欺騙”大家的眼睛誤以為是窗口“透”過去了。這一招確實體現了程序員的聰明才智。
不過這一方法的缺陷是,當窗口被移動一下后就會露餡,所謂“透明”區域的抓圖不知道位置變化,也不會隨之改變。所以在該書的例子程序中,作者又將JFrame的setUndecorated設置為true去掉了標題欄,讓你無法移動窗口;再啟動了一個線程,每250毫秒就重新抓屏一次,更新Swing窗口的圖片背景。不過由于Swing窗口顯示出來后,它本身又遮擋了屏幕后面的物體,作者只好先frame.hide()把窗口隱藏一下,然后馬上抓圖,然后再frame.show恢復窗口顯示。透明效果是勉強出來了,但是程序在那里有事沒事的一直忽隱忽現,真是夠怪異的,效率、實時性也都慘不忍睹。
以下是相關代碼:



2

3



4

5



6

7

8

9

10

11

12

13



14

15



16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39



40

41

42

43

44

45

46

47

48

《Swing Hacks》中介紹的方法代碼片段
第二招:AWTUtilities.setWindowShape法
隨著Sun公司對JavaFX技術的猛醒和大力持續投入,JDK從6就開始從底層為JavaFX的未來做好準備,提供更多底層功能支撐。作為“酷”、“炫”的UI技術的先鋒,窗口透明、不規則窗口自然是將來JavaFX不可缺少的元素和特性。所以,Sun在JDK6中提供了幾個新的函數,用來支持窗口透明度、窗口任意形狀:
1
void
setWindowOpacity(Window window,
float
opacity)
//
設置窗口透明度
2
void
setWindowShape(Window window, Shape shape)
//
設置窗口形狀

2

這里有官方的具體介紹:
http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/
setWindowOpacity方法提供了官方的、徹底的方法來生成不規則形狀窗體。不過依舊有以下幾個問題:
1, AWTUtilities并非JDK公開類,將來可能會發生變化。當然除了編譯時的一個不爽的警告外,也不用過度擔心,即使將來API發生變化,相信Sun和Oracle也會妥善處理好。
2, 用Shape形狀定義的窗口邊緣粗糙,顯示效果差。使用setWindowShape函數對窗口設定形狀后,其窗口切割的邊緣并未做抗鋸齒(anti-alias)處理,也沒有相應的函數或參數進行控制,導致顯示效果粗糙。看看Sun自己做出來的例子:
http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/9.jpg
,一個簡單的橢圓Shape,其邊緣就已經粗糙不堪。更不用說更復雜的透明圖片邊緣了。本人經過好幾天的反復嘗試,發現其效果始終不甚理想(如圖),無論對圖片的透明邊緣如何精細處理,甚至直接new Shape,都完全達不到美工設計出來的效果圖。
3, 透明PNG圖片的邊緣Shape不好獲取。如果我們的窗體不是一個規則的、可定義的幾何形狀Shape,而是一個任意透明PNG圖片,該如何獲取圖片的透明邊緣Shape,進而設置window的不規則形狀呢?這確實是一個難題。在網上有人專門討論這一算法,基本上是讀取PNG圖片的每一個像素,獲得像素透明邊界點,對邊界點進行不斷的合并與逼近,最后形成一個最終Shape。TWaver的TWaverUtil工具類中就有一個getImageShape方法用來獲得任意圖片的邊緣shape。經反復測試驗證,就是采用了這種算法。不過這種算法的缺點很明顯:邊緣必須是連續的,甚至必須是“外凸”的;如果png圖片中間有一個透明的“洞”,甚至邊緣有一個凹陷透明區域,生成的Shape都無法準確反映出來。
第三招:終極解決之道
經過反復的研究探索,終于獲得了一個完美的解決方法:不用shape、不用抓圖、不用workaround,真正的、徹底的、完全的、隨意的在桌面上任意繪圖、涂鴉、撒野,真正的屬于程序員的Freedom!下面就來一起揭開這層窗戶紙吧!
在程序中依次設置以下幾個參數:
設置窗口完全透明:AWTUtilities.setWindowOpaque(frame, false);
設置窗口無邊緣:frame.setUndecorated(true);
設置窗口的ContentPane為要顯示的Pane:frame.setContentPane(myPane);
在myPane中放置具體要顯示的內容,也可以重載paint方法進行Java2D繪制。這些paint會直接發生在桌面背景上。
接下來,就是見證奇跡的時刻!
(不好意思,暴露我的桌面了)
通過上面方法,可以做一個任意大小、任意位置的window,在相應的桌面位置上隨意顯示Swing組件,或做任意Java2D畫圖。比如下面小例子可以在屏幕上直接畫一個紅色的立體矩形,而沒有顯示窗口:

2

3

4

5

6

7

8

9

10

11

12

13



14

15

16

17



18

19

20

21

22

23

24

25

26

27

28

29

30

31



32

33

34

35

36

37



38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

運行效果如下圖:

窗口的拖拽移動
窗口不再規則,窗口標題欄不再出現,如何移動窗口?按照其他類似軟件的習慣做法,應當允許用鼠標直接拖拽窗體任意位置進行窗口移動。做一個鼠標監聽器對窗口中的元素進行拖動監聽,對窗口進行相應拖動距離的移動:



2

3

4

5

6

7

8

9

10

11



12

13

14

15

16

17

18

19

20



21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

對窗體上的組件安裝這一listener,就可以對窗口中任意元素進行拖拽,直接拖動窗體四處晃悠了。
圖片的切割
要做好的界面,需要一個耐心、有創意的美工大力協助,例如圖片的切割就很重要。下圖展示了如何從效果圖進行具體切割素材:
制作漸變背景Panel
仔細觀察中間的輸入區域部分,其背景是有漸變設計的。其制作方法也很簡單:首先讓美工幫助制作一個一個像素寬、整個panel高度的小圖片作為素材;然后用這個圖片創建紋理Paint;最后用這個紋理對真個panel進行fill。



2

3

4

5

6

7

8

9

10

11

12

13



14

15

16

17

18

19

20

21

22

23

24

25

26

肆虐你的桌面:六月飄雪!
既然窗戶紙捅破了,在桌面上就隨意折騰吧。這幾天窗外酷熱難耐,咱們就來個桌面飄雪,也許可以望梅止渴,帶來絲絲清涼吧!
先準備一個雪花的png透明圖片,然后在桌面上隨機生成50個雪花坐標,每次paint讓每個雪花的左右略微抖一下(snowX[i] += TWaverUtil.getRandomInt(5) – 3),垂直距離下墜5像素(snowY[i] += 5),再旋轉個小角度。然后,用一個線程不停的repaint窗口。
雪花png圖片:



2

3

4



5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20



21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36



37

38

39

40

41

42



43

44

45

46

47

48



49

50

51

52

53

54

55

56



57

58

59

60

61

62

63

64



65

66

67

68

69

70

71

72

73

74

75

76

77

78



79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120



121

122

123

124

125

126



127

128



129

130



131

132

133

134



135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

快快運行代碼,讓雪花飄起來吧!
如果愿意折騰,還可以修改代碼中的:
private int count = 50,調整雪花的數量;
修改angles[i] += i / 5,調整雪花翻滾的速度;
修改snowY[i] += 5,調整雪花下墜的速度;
修改snowX[i] += TWaverUtil.getRandomInt(5) – 3,調整雪花左右擺動的速度;
別說你不知道怎么結束程序啊,不會Alt+F4的話,你這個程序員肯定不合格了。
秘密背后的秘密
當把透明窗口Frame設置特別大以后(例如10000*10000),你會發現不但界面變得極其緩慢,而且還會內存溢出。Sun的秘密不言自明了:還是使用了BufferedImage。否則,鼠標點擊你畫的橢圓或桌面的圖標,它如何知道是點擊了窗體,還是操作了桌面?只能生成內存圖片,在里面進行像素判斷了。要挖掘再深入的秘密,我也不清楚了,自己繼續探索吧!
代碼大家接著到我之前發表的TWaver中文社區下載吧。