摘要: 很久沒有回來這里寫技術(shù)BLOG了,這里的氛圍還行,大家都對(duì)一個(gè)問題積極的思考(至少之前這里給我的感覺是這樣的),2年里面自己也忙著做些事情,沒有寫,最近有空也就寫寫,偶爾會(huì)去oschine.net看看新聞,然后就在那里看到了一個(gè)人提出的問題很有意思,就是怎么表達(dá)式求解,例如(1 + 2) / 3 - 1 * 2 + 5 / (3 + 2)這樣的字符串輸入,怎么樣解析之后輸出結(jié)果。說來也好笑,對(duì)于我...
閱讀全文
posted @
2011-11-09 10:36 ruislan 閱讀(1761) |
評(píng)論 (6) |
編輯 收藏
摘要: 很久沒來了,不是一位朋友給我發(fā)郵件問我關(guān)于swing的問題,才想起,然后翻看了之前的代碼,發(fā)現(xiàn)當(dāng)年還實(shí)現(xiàn)了一個(gè)Vista風(fēng)格的按鈕沒有放出來,現(xiàn)在補(bǔ)上,也許現(xiàn)在人們的swing水平對(duì)我這代碼不屑一顧,不過還是依然拋磚引玉,給未知的人們一個(gè)啟發(fā)。還是老慣例,上效果圖:
正常情況下:(和vista一樣具有焦點(diǎn)的按鈕的顏色漸深漸淺的循環(huán)變化)
鼠標(biāo)在區(qū)域內(nèi)時(shí):
鼠標(biāo)按下去:
然后是代...
閱讀全文
posted @
2009-09-12 12:54 ruislan 閱讀(2486) |
評(píng)論 (3) |
編輯 收藏
認(rèn)為自己是達(dá)人的就不用看了。只是一點(diǎn)小技巧,不敢班門弄斧,做個(gè)總結(jié),為那些還不知道的解解惑,隨便告訴大家我還活著。
最近客戶提了個(gè)小改動(dòng),客戶網(wǎng)站上圖片存放的目錄需要改動(dòng)一下。例如在網(wǎng)上訪問是www.tkk7.com/images/*.*,在服務(wù)器上的目錄是D:/<webroot>/images/*.*,客戶想把這個(gè)images目錄下的資源全部移動(dòng)到E:/data/里面去,但是在網(wǎng)上www.tkk7.com/images/*.*還是同樣可以訪問得到,我剛開始犯了形式主義的錯(cuò)誤,老是想用程序解決,一會(huì)filter,一會(huì)servlet/action,后來我配置程序的時(shí)候突然看到了server.xml,于是我想到了選擇用映射的方式。正好,server.xml中的<Context>就是做這個(gè)事情的。于是乎我們?cè)?lt;Host></Host>中增加了一個(gè)<Context docBase="E:/data/images" path="/images">,OK,重啟之后,所有檢索www.tkk7.com/images路徑下的資源實(shí)際上都由E:/data/images下的資源提供了。
posted @
2008-02-15 16:41 ruislan 閱讀(1272) |
評(píng)論 (3) |
編輯 收藏
在寫多線程程序的時(shí)候,你就像個(gè)經(jīng)理,手下有那么或多或少的職員,你負(fù)責(zé)協(xié)調(diào)職員之間的工作,如果你稍不留神,職員之間就陷入了相互等待的尷尬狀態(tài)。還好,大多數(shù)時(shí)候多線程都還在我們掌控之內(nèi),即便是遇到這樣的deadlock情況,我們也能夠去修正,但是有的時(shí)候生活就是那么不盡人意,特別是NIO這種你不能掌控的時(shí)候,且看下面的代碼:
/**
* @(#)DeadLock.java v0.1.0 2007-12-13
*/
package ruislan.rswing.test;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executors;
/**
* NIO DeadLock
*
* @author ruislan <a href="mailto:z17520@126.com"/>
* @version 0.1.0
*/
public class DeadLock {
public static void main(String[] args) throws Exception {
Service service = new Service();
Executors.newSingleThreadExecutor().execute(service);
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("http://www.tkk7.com", 80));
service.addChannel(channel);
}
static class Service implements Runnable {
Selector selector;
public Service() {
}
public void run() {
try {
selector = Selector.open();
while (true) {
selector.select();
System.out.println(selector.selectedKeys().size());
}
} catch (Exception e) {
}
}
public void addChannel(SocketChannel channel) {
try {
channel.register(selector, SelectionKey.OP_CONNECT
| SelectionKey.OP_READ);
System.out.println("can reach here?when pigs fly!");
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
}
}
乍看之下,我們的代碼沒有問題,但是運(yùn)行之后你會(huì)發(fā)現(xiàn),這句System.out.println("can reach here?when pigs fly!");永遠(yuǎn)無法執(zhí)行,也就是說register()方法被阻塞了!Oh god bless,讓我們看看JavaDoc是怎么說的:
...
可在任意時(shí)間調(diào)用此方法。如果調(diào)用此方法的同時(shí)正在進(jìn)行另一個(gè)此方法或 configureBlocking 方法的調(diào)用,則在另一個(gè)操作完成前將首先阻塞該調(diào)用。然后此方法將在選擇器的鍵集上實(shí)現(xiàn)同步,因此如果調(diào)用此方法時(shí)并發(fā)地調(diào)用了涉及同一選擇器的另一個(gè)注冊(cè)或選擇操作,則可能阻塞此方法的調(diào)用。
...
看這句“可在任意時(shí)間調(diào)用此方法。”,也就是說我們調(diào)用的時(shí)間沒有任何限制,而阻塞的情況只會(huì)出現(xiàn)在“如果調(diào)用此方法的同時(shí)正在進(jìn)行另一個(gè)此方法或 configureBlocking 方法的調(diào)用”的情況下,即便是阻塞了,我相信“正在進(jìn)行另一個(gè)此方法或configureBlocking”也不會(huì)花掉太多的時(shí)間,況且這里沒有上面這樣的情況出現(xiàn)。那register()是被誰(shuí)擋住了?或者是BUG?
我們來分析一下程序,程序有兩個(gè)線程主線程和Service線程,主線程啟動(dòng)后啟動(dòng)了Service線程,Service線程啟動(dòng)Selector然后Service線程陷入select()的阻塞中,同時(shí),主線程調(diào)用Service的addChannel()方法來添加一個(gè)SocketChannel,嗯,兩個(gè)線程之間唯一的聯(lián)系就是selector,看來要從selector尋找線索,很可惜,selector的實(shí)現(xiàn)沒有源代碼可查,不過可以肯定是channel的register()會(huì)調(diào)用selector的register(),雖然此時(shí)持有selector的Service線程被select()方法所阻塞,但是并不影響其他線程對(duì)其操作吧?那么,剩下的解釋就是Selector的select()方法和register()方法公用了一個(gè)鎖,select()方法阻塞住了,所以register()拿不到這個(gè)鎖了,那么這樣一來我們就只能保證讓select()或者register()不能同時(shí)調(diào)用或者register()調(diào)用的時(shí)候select()不持有這個(gè)鎖,也就是說我們要用Service線程自己來執(zhí)行addChannel()方法,所以改進(jìn)如下:
/**
* @(#)DeadLock.java v0.1.0 2007-12-13
*/
package ruislan.rswing.test;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* NIO DeadLock
*
* @author ruislan <a href="mailto:z17520@126.com"/>
* @version 0.1.0
*/
public class DeadLock {
public static void main(String[] args) {
Service service = new Service();
new Thread(service).start();
for (int i = 0; i < 5; i++) {
new Thread(new ChannelAdder(service)).start();
}
}
static class ChannelAdder implements Runnable {
private Service service;
public ChannelAdder(Service service) {
this.service = service;
}
@Override
public void run() {
try {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(
"http://www.tkk7.com", 80));
service.addChannel(channel);
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class Service implements Runnable {
private Selector selector;
private Queue<SocketChannel> pendingRegisters;
public Service() {
pendingRegisters = new LinkedBlockingQueue<SocketChannel>();
}
public void run() {
try {
selector = Selector.open();
while (true) {
selector.select();
System.out.println(selector.selectedKeys().size());
handlePendingRegisters();
}
} catch (Exception e) {
}
}
public void handlePendingRegisters() {
while (!pendingRegisters.isEmpty()) {
SocketChannel channel = pendingRegisters.poll();
try {
channel.register(selector, SelectionKey.OP_CONNECT);
System.out.println("can reach here?yeah!");
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
}
public void addChannel(SocketChannel channel) {
pendingRegisters.offer(channel);
selector.wakeup();
}
}
}
新的代碼,我們?cè)赟ervice的線程提供了一個(gè)待處理Channel隊(duì)列,然后在添加一個(gè)SocketChannel到隊(duì)列中時(shí)喚醒這個(gè)selector,取消阻塞,然后在Service的循環(huán)中處理這個(gè)pendingChannel,這樣就避免這個(gè)Deadlock的發(fā)生了。當(dāng)然我們亦可以在那個(gè)代碼上將select的超時(shí)時(shí)間設(shè)置非常的短,然后讓兩個(gè)線程去競(jìng)爭(zhēng),這樣做有太多的不可控性,不推薦了。
posted @
2007-12-13 18:31 ruislan 閱讀(1349) |
評(píng)論 (3) |
編輯 收藏
UI作為用戶與電腦的交互界面,如何更好的服務(wù)于人,讓人們用起來方便、簡(jiǎn)單、快捷一直是UI開發(fā)者應(yīng)該有的覺悟,作為開發(fā)人員的我們來說,不應(yīng)該只是把UI推給電腦平面設(shè)計(jì)人員,更不應(yīng)該一手包辦了(如果你不是一個(gè)人的話)。我們開發(fā)人員常常在開發(fā)UI的時(shí)候避重就輕,基本上都在強(qiáng)調(diào)code的美學(xué),模式的應(yīng)用而忽略了真實(shí)用戶的感受。我們常常得意于自己技術(shù)的美麗,而將一些比自己水平低的應(yīng)用嗤之以鼻。但是用戶卻從來不關(guān)心代碼是如何寫的,他們關(guān)心這個(gè)應(yīng)用是否對(duì)他們有用,順手乎?聰明乎?所以如果我們只是美麗于自己的設(shè)計(jì),太關(guān)注軟件的本身而忽略了用戶的感受,就跟某些象牙塔里拿著錢做些無用的研究的人沒什么兩樣,或許有個(gè)美麗的名詞,為了科學(xué)。
那么如何才能算是好的UI人性化的設(shè)計(jì)呢?這個(gè)得看針對(duì)的用戶主要是哪些。我們熟知的操作系統(tǒng)Windows XP,Windows Vista,Vista是微軟最新的操作系統(tǒng),包含了很多開發(fā)人員辛苦的結(jié)晶,但是在我身邊的很多人都不愿意裝它,也包括一些新聞的調(diào)查也說Vista不如當(dāng)年XP出世那般火爆,他們大多數(shù)不愿裝的都說了同樣的話,XP都還有很多不懂,怕Vista更搞不懂,說實(shí)話我用過Vista,就我這么一個(gè)算是業(yè)內(nèi)人士用起來當(dāng)然駕輕就熟,再加上我們都有勇于創(chuàng)新的精神,所以常常去用新的東西,而普通客戶就不這么想了,我問了幾個(gè)不懂電腦才安裝了Vista的用戶的感受,“開始菜單的‘開始’兩個(gè)字沒有了,我還以為換了位置”,“界面比XP漂亮啊,但是我的機(jī)器好像有點(diǎn)慢,是不是要設(shè)置個(gè)什么啊”……再來我們熟知的AJAX,我已經(jīng)接到過很多次不同的人給我的電話,說“為什么網(wǎng)頁(yè)打開的時(shí)候突然好卡了,以前不這樣啊?”,“網(wǎng)頁(yè)瀏覽不了,老說請(qǐng)稍候,數(shù)據(jù)加載中,等了很久,就是不出現(xiàn)”……面對(duì)這些電話或許我們會(huì)說,你們?cè)趺茨敲幢堪。ㄊ且驗(yàn)樵谙聳|西,在執(zhí)行JS,寫JS的人太垃圾,浪費(fèi)了資源,不出現(xiàn)就刷新啊,不要瀏覽那個(gè)不專業(yè)的網(wǎng)站了,等等就OK了等等回答,其實(shí)很多時(shí)候我們可以避免用戶的問題出現(xiàn),例如你的AJAX的JS太大的時(shí)候,可以先提示用戶說,數(shù)據(jù)量較大,請(qǐng)稍后,如果長(zhǎng)時(shí)間無反應(yīng),請(qǐng)按瀏覽器的刷新按鈕,或者嘗試按下F5鍵。
我還見過許多軟件鼓吹自己的功能如何強(qiáng)大,如何厲害,多么的人性化,但是我打開他們的軟件,居然發(fā)現(xiàn)只能用鼠標(biāo)操作!!這是多么大的UI設(shè)計(jì)失敗!在舉一個(gè)例子,MSN和QQ兩個(gè)IM,如果你用MSN,在聯(lián)系人框里按上下的話,MSN會(huì)很聰明的明白你是要選擇上一位或者下一位聯(lián)系人,而QQ會(huì)很聰明的明白你是要拖動(dòng)滑動(dòng)條!@#$,還有很多軟件記憶力太差,不管我如何操作,它就是記不住,關(guān)閉軟件重新啟動(dòng)后又回到了最初的模樣,還有的軟件自信心不足,一再問我“你確定嗎?”,“真的要這樣做嗎?”,“或許您不小心點(diǎn)了?”而我只是在點(diǎn)關(guān)閉這個(gè)娛樂性質(zhì)的軟件而已,而有些軟件又特膽肥,做了一個(gè)不可恢復(fù)的操作盡然連提示都沒有,還有的軟件文化太差,常常把一個(gè)按鈕或者圖標(biāo)該表達(dá)的含義弄得模棱兩可,以至于常常讓我們會(huì)錯(cuò)意,做錯(cuò)操作,或者把一些高風(fēng)險(xiǎn)的操作放在常用操作的旁邊,很容易點(diǎn)錯(cuò),還有的把不常用的操作也放到常用操作區(qū),還不告訴用戶怎么去掉,這樣的例子不勝枚舉。
出現(xiàn)這些問題的原因在于我們與用戶之間的思維方式有著很大的不同。例如在寫文章之前我才將老爸從我的電腦椅上請(qǐng)下來,請(qǐng)下來之前他正在看我吃飯前的網(wǎng)頁(yè)——“界面九宮格”,我說您能看得懂嘛,他說“不懂,不過這軟件的界面不都這樣嘛?再說了,一張紙就8個(gè)方位,加上中間正好九個(gè),你的東西不擺這里擺哪里啊?”,我正要解釋一下這與軟件設(shè)計(jì)的關(guān)系,但是突然一想,是啊,有道理啊,要是我給他老人家再解釋一下可以放在上面和下面,那不就是3D的了。再比如我一直都很不屑一顧的網(wǎng)絡(luò)實(shí)名,但是當(dāng)它被我在很多人的機(jī)器里面消滅之后,很多人都打電話問我,怎么在地址欄里面輸入漢字,跳出搜索界面了,不是那個(gè)漢字的網(wǎng)站了,以前是有的,原來我只看到了它流氓的一面,忽略了普通用戶是根本記不住網(wǎng)址在哪里,甚至有些用戶不懂英文,你怎么讓他記得住全是英文的網(wǎng)址呢?不過過了幾天,他們都說不用了,有一個(gè)網(wǎng)站導(dǎo)航網(wǎng)址做了他們的主頁(yè),他們平日想去的網(wǎng)站都在上面列著的,我后來才知道,就是被我以前同寢室的刪除了半天的hao123。所以我們必須充分考慮我們的應(yīng)用是針對(duì)哪些用戶,他們是哪一類人,習(xí)慣是什么,當(dāng)然還有就是UI設(shè)計(jì)的一些基本的東西,例如鼠標(biāo)能夠完成的動(dòng)作,同樣鍵盤也能完成等等。
posted @
2007-11-11 13:36 ruislan 閱讀(1427) |
評(píng)論 (5) |
編輯 收藏
Swing作為一個(gè)完整的UI解決方案,包含了一個(gè)GUI程序所擁有的方方面面,當(dāng)然包括作為普通程序也好,作為GUI程序也好,作為Web程序等等程序都共有的線程概念。
Swing中的線程有三種:初始線程,事件線程,工作線程
這三種線程基本上包括了讓一個(gè)GUI完美工作的方方面面,首先,初始線程被用來創(chuàng)建GUI組件、資源加載和啟動(dòng)GUI組件,眾所周知,Swing是事件驅(qū)動(dòng)的,所以當(dāng)UI出現(xiàn)了之后,初始線程就完成了它的使命,并將接力棒交給了事件線程,Event Dispatch Thread,這個(gè)時(shí)候所有組件的事件行為都交給了這個(gè)線程去處理,當(dāng)然我們自己也要需要用線程來運(yùn)行許多任務(wù),優(yōu)秀的GUI程序是絕不能讓界面被卡死不動(dòng)的,那會(huì)讓用戶崩潰,所以這個(gè)時(shí)候就需要工作線程了,也可以說是在背后運(yùn)行的線程,這種線程是勞動(dòng)階級(jí),任勞任怨的執(zhí)行者長(zhǎng)時(shí)間的工作。
初始線程的寫法很簡(jiǎn)單,這樣就可以了:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
initGUI();
showGUI();
}
}
但是Applet中,你可能需要調(diào)用SwingUtilities.invokeAndWait這個(gè)方法,要是init方法返回了,瀏覽器開始展現(xiàn)Applet,但是GUI的創(chuàng)建還在thread中,出錯(cuò)也是可想而知的。
至于invokeLater和invokeAndWait這兩個(gè)線程的簡(jiǎn)單點(diǎn)的區(qū)別就是invokeLater是異步的,你不知道它什么時(shí)候會(huì)開始執(zhí)行,invokeAndWait則是同步的,它會(huì)等到動(dòng)作執(zhí)行完成之后才返回。
Event Dispatch Thread不是線程安全的,所以要用線程來與它打交道要注意了,同步問題總是讓人頭痛。
在1.5之前應(yīng)該說工作線程都是由開發(fā)人員自己去定義的,但是現(xiàn)在Swing推薦了SwingWorker這個(gè)類,包括Swing最新的符合JSR標(biāo)準(zhǔn)的Swing AppFramework也使用了SwingWorker這個(gè)類來處理所有在GUI背后做的事情。
了解了Swing中的線程定義,能夠讓我們更好的寫出優(yōu)美的基于Swing的GUI程序。
posted @
2007-11-04 12:40 ruislan 閱讀(1269) |
評(píng)論 (2) |
編輯 收藏
很多人都說我們這行的人是偏執(zhí)狂,我也覺得我是有一點(diǎn)倔脾氣,就像看連續(xù)劇,從第一集開始一直到最后一集才會(huì)關(guān)上電腦,一旦一個(gè)研究開始,就一定要有一個(gè)結(jié)果或者令自己滿意的結(jié)果才結(jié)束,但是當(dāng)我看到這位“偏執(zhí)狂”之后,我覺得我只是稍微有一點(diǎn)偏執(zhí)而已。
研究UI繪制的時(shí)候很容易陷入另外一個(gè)領(lǐng)域,圖像領(lǐng)域,或者是游戲領(lǐng)域,我不喜歡做游戲是因?yàn)槲覑弁嬗螒颍绻婧凸ぷ鹘壴谝黄鹆说脑挘敲垂ぷ髦蟮男蓍e就也是工作了。
好了,說了點(diǎn)廢話,下面是對(duì)FengGUI的介紹.
FengGUI是一個(gè)建立在OpenGL上的GUI的API,F(xiàn)engGUI提供了很多標(biāo)準(zhǔn)的UI組件,像Button,TextField,Panel之類的,下面先看看截圖:

GridLayout的截圖

可分割的面板
要說到最大的特色,莫過于FengGUI基于OpenGL,并且可以在組件里面直接使用OpenGL,可以輕松的集成jME(java Monkey Engine,一個(gè)非常棒的Java 3D游戲引擎),jogl(Java OpenGL API),lwjgl(輕量級(jí)Java游戲庫(kù)),jPCT(同樣非常棒的Java 3D游戲引擎),但是在跑它的demo時(shí)我也感受到了CPU 80% 工作率的壓力,所以就目前的我膚淺的了解,用它來做普通的GUI程序估計(jì)還為時(shí)有點(diǎn)早,但是如果是游戲中的組件的話確實(shí)是與上述引擎和API的非常好的補(bǔ)充。
posted @
2007-10-27 20:34 ruislan 閱讀(1707) |
評(píng)論 (2) |
編輯 收藏
以前或許大家對(duì)一個(gè)UI組件是否透明沒有那么關(guān)心,但是自從Vista的毛玻璃出現(xiàn)后,UI透明就成了大家非常關(guān)注的一個(gè)話題,于是Java陣營(yíng)開始了鋪天蓋地的討論如何實(shí)現(xiàn)透明的效果,但是很不幸的是無論組件如何透明和變換,但是能夠放置于屏幕任何位置的Window一族就是沒法透明和變形,原生代碼的問題還是交給原生代碼來解決吧。
自己寫原生代碼是可怕的,特別是對(duì)我這種只知道Java的平凡程序員,所以我們得借助一個(gè)非常方便的跨平臺(tái)的調(diào)用OS function方便的Lib,JNA便是最佳選擇(那個(gè)誰(shuí),這里不是討論JRuby&JPython的)。
so, all robots, transform!
下面我們要做一個(gè)界面是圓角四邊形的,中間有一個(gè)滑動(dòng)條來滑動(dòng)調(diào)節(jié)透明度。先看看做好的截圖。

注意圖中的JFrame邊角已經(jīng)變成了圓弧,滑動(dòng)滑塊,JFrame開始透明,桌面的圖標(biāo)顯現(xiàn)出來,下面是代碼。
/**
* @(#)TransparentFrame.java v0.1.0 2007-10-21
*/
package ruislan.rswing.test;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.sun.jna.examples.WindowUtils;
/**
* Transparent JFrame use JNA
*
* @author ruislan <a href="mailto:z17520@126.com"/>
* @version 0.1.0
*/
public class TransparentFrame {
private JFrame frame;
private JPanel container;
private JSlider slider;
private JPanel titleBar;
private JLabel titleLabel;
private JButton closeButton;
public static void main(String[] args) {
new TransparentFrame().launch();
}
private void launch() {
createUI();
launchUI();
}
protected void launchUI() {
frame.setVisible(true);
}
protected void createUI() {
System.setProperty("sun.java2d.noddraw", "true");
frame = new JFrame();
frame.setSize(200, 150);
frame.setAlwaysOnTop(true);
frame.setUndecorated(true);
container = new JPanel();
frame.setContentPane(container);
container.setLayout(new BorderLayout());
container.add(new JLabel("Ubunto vs Vista, I like both."),
BorderLayout.CENTER);
container.setBorder(new LineBorder(Color.BLACK));
titleBar = new JPanel();
titleBar.setLayout(new BorderLayout());
titleLabel = new JLabel("JNA is great!");
titleBar.add(titleLabel, BorderLayout.CENTER);
titleBar.setBorder(new LineBorder(Color.GRAY));
closeButton = new JButton("X");
closeButton.setFocusPainted(false);
closeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
titleBar.add(closeButton, BorderLayout.EAST);
container.add(titleBar, BorderLayout.NORTH);
slider = new JSlider(0, 100);
slider.setValue(100);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
float value = slider.getValue();
WindowUtils.setWindowAlpha(frame, value * 0.01f);
}
});
container.add(slider, BorderLayout.SOUTH);
RoundRectangle2D.Float mask = new RoundRectangle2D.Float(0, 0, frame
.getWidth(), frame.getHeight(), 20, 20);
WindowUtils.setWindowMask(frame, mask);
centerWindow(frame);
}
public static void centerWindow(Container window) {
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int w = window.getSize().width;
int h = window.getSize().height;
int x = (dim.width - w) / 2;
int y = (dim.height - h) / 2;
window.setLocation(x, y);
}
}
利用JNA來制作透明效果非常簡(jiǎn)單,只需要調(diào)用
WindowUtils.setWindowAlpha(window, alpha)就可以了。當(dāng)然這是JNA特別為UI寫的工具代碼。如果要改變形狀,用WindowUtils.setWindowMask(window, shape)或者WindowUtils.setWindowMask(window, icon)就可以了,但是要注意一點(diǎn)必須設(shè)置System.setProperty("sun.java2d.noddraw", "true"),否則JNA會(huì)告訴你這個(gè)程序不支持透明。當(dāng)然要運(yùn)行起來,還得需要兩個(gè)Jar,jna.jar 和 examples.jar ,都不是很大,只有200多K。
雖然這篇文章只是介紹了一下JNA關(guān)于Swing的簡(jiǎn)單用法,但是有了這個(gè)我最先到的便是可以做類似于Yahoo Widget和Google Widget之類的東西了,還可以做好看的fishEye,SideBar,JNA的JAR兩個(gè)合起來不過400K,卻能讓這么多復(fù)雜的事情簡(jiǎn)單化,真是精湛的藝術(shù)啊,嗯。
posted @
2007-10-21 13:43 ruislan 閱讀(4385) |
評(píng)論 (12) |
編輯 收藏
很久沒有上來更新了,因?yàn)橐恍┰颉5牵矣只貋砹耍子肕ASK的話,小子們,想我嗎?
回來了先報(bào)告一個(gè)Blogjava的BUG,就是用Opera9瀏覽器寫文章時(shí)不能用鼠標(biāo)點(diǎn)擊編輯區(qū),還好用TAB可以切換過來,然后就是用Google輸入法的話,標(biāo)點(diǎn)符號(hào)和數(shù)字輸入一次,編輯區(qū)會(huì)出來兩個(gè),最后就是編輯完之后好像內(nèi)容也保存不了,所以我又換回FF來編輯了。
回來了卻不知從何開始了,組件的改造也算拋磚引玉了(當(dāng)然如果你們喜歡,我還可以繼續(xù)改造),冰封的程序要繼續(xù)改造的話就要開一個(gè)工程了大家集體參與了,所以那就還是先從上次哪位仁兄提到的組件透明的問題,從那個(gè)開始吧!show time。
posted @
2007-10-20 16:30 ruislan 閱讀(433) |
評(píng)論 (1) |
編輯 收藏
昨天我們改進(jìn)了選擇區(qū),今天我們來繼續(xù)為選擇區(qū)選定之后添加一個(gè)操作框
下面是改進(jìn)后的截圖:

正常的情況,右下角出現(xiàn)了一個(gè)操作框

在三個(gè)(左、右、底)邊際的情況,我們重新計(jì)算了位置。
三個(gè)按鈕還沒有功能,也不是圖片,但是我們離QQ的截屏程序又進(jìn)一步了,嗯!
posted @
2007-09-14 18:48 ruislan 閱讀(1160) |
評(píng)論 (10) |
編輯 收藏
“千里冰封” 兄弟的截屏程序酷斃了,但是好像9月4日之后就沒有繼續(xù)更新了,我們來繼續(xù)為他的程序改進(jìn),順便也把我們這幾天都在講的2D繪制用進(jìn)來,我們的目標(biāo)是讓冰封的截屏程序成為截屏程序里的王!
今天先改進(jìn)一下截圖時(shí)候的選框,還是先放上截圖的截圖(*o*):

這是原來的圖片,下面是改進(jìn)后的

和改進(jìn)的代碼部分:
這部分代碼插入 Temp類的paintComponent方法中的 if (showTip) 這句的前面
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3F));
g2d.setColor(Color.RED.brighter().brighter());
int sX = Math.min(startX, endX);
int sY = Math.min(endY, startY);
g2d.fillRect(sX, sY, Math.abs(endX - startX), Math.abs(endY
- startY));
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 1F));
boolean drawCTip = endX - startX != 0 && endY - startY != 0;
if (drawCTip) {
String cTip = String.format("%dX%d", Math.abs(endX - startX),
Math.abs(endY - startY));
int cTipH = 20;
Font cTipFont = new Font("system", Font.BOLD, 16);
g2d.setFont(cTipFont);
int cTipW = SwingUtilities.computeStringWidth(
getFontMetrics(cTipFont), cTip);
g2d.setPaint(Color.BLACK);
int cStartY = sY - cTipH > 0 ? sY - cTipH : sY;
g2d.fillRect(sX, cStartY, cTipW, cTipH);
g2d.setPaint(Color.WHITE);
g2d.drawString(cTip, sX, cStartY == sY ? sY + cTipH - 3
: sY - 3);
}
g2d.dispose();
怎么樣,比起QQ的截圖程序,我們又近一步了,嗯。
posted @
2007-09-13 13:28 ruislan 閱讀(1488) |
評(píng)論 (10) |
編輯 收藏
摘要: 嗯,BeanSoft的話很有道理,令我敬佩,也許是昨天在下對(duì)那個(gè)“更好的”三個(gè)字感到一時(shí)憤慨,所以看到UI就自己擴(kuò)大了問題,想到迎合LookAndFeel上面去了,在此說句對(duì)不起了。你的回帖里面偏重于從整個(gè)組件的設(shè)計(jì)和重用性上,我的文章主要講的是如何將2D繪制和組件的繪制結(jié)合起來,看客如果既了解了如何繪制自己想要的組件,又能設(shè)計(jì)得體,重用性高的話也算是對(duì)我拋磚引玉的欣慰了。...
閱讀全文
posted @
2007-09-12 13:36 ruislan 閱讀(2440) |
評(píng)論 (11) |
編輯 收藏
看來我們的JTextField之旅也到了一個(gè)階段,已經(jīng)很不錯(cuò)了,現(xiàn)在我們來改造JButton,讓那個(gè)呆板的Swing看起來舒服一些。
還是先放上完成后的效果圖:

普通的狀態(tài)

鼠標(biāo)滑過

鼠標(biāo)按下
和代碼:
1 /**
2 * @(#)RJButton.java 0.1.0 2007-9-11
3 */
4 package ruislan.rswing;
5
6 import java.awt.AlphaComposite;
7 import java.awt.Color;
8 import java.awt.Font;
9 import java.awt.GradientPaint;
10 import java.awt.Graphics;
11 import java.awt.Graphics2D;
12 import java.awt.RenderingHints;
13 import java.awt.Shape;
14 import java.awt.event.MouseAdapter;
15 import java.awt.event.MouseEvent;
16 import java.awt.geom.RoundRectangle2D;
17
18 import javax.swing.JButton;
19
20 /**
21 * Custom JButton
22 *
23 * @version 0.1.0
24 * @author ruislan <a href="mailto:z17520@126.com"/>
25 */
26 public class RButton extends JButton {
27 private static final long serialVersionUID = 39082560987930759L;
28 public static final Color BUTTON_COLOR1 = new Color(205, 255, 205);
29 public static final Color BUTTON_COLOR2 = new Color(51, 154, 47);
30 // public static final Color BUTTON_COLOR1 = new Color(125, 161, 237);
31 // public static final Color BUTTON_COLOR2 = new Color(91, 118, 173);
32 public static final Color BUTTON_FOREGROUND_COLOR = Color.WHITE;
33 private boolean hover;
34
35 public RButton() {
36 setFont(new Font("system", Font.PLAIN, 12));
37 setBorderPainted(false);
38 setForeground(BUTTON_COLOR2);
39 setFocusPainted(false);
40 setContentAreaFilled(false);
41 addMouseListener(new MouseAdapter() {
42 @Override
43 public void mouseEntered(MouseEvent e) {
44 setForeground(BUTTON_FOREGROUND_COLOR);
45 hover = true;
46 repaint();
47 }
48
49 @Override
50 public void mouseExited(MouseEvent e) {
51 setForeground(BUTTON_COLOR2);
52 hover = false;
53 repaint();
54 }
55 });
56 }
57
58 @Override
59 protected void paintComponent(Graphics g) {
60 Graphics2D g2d = (Graphics2D) g.create();
61 int h = getHeight();
62 int w = getWidth();
63 float tran = 1F;
64 if (!hover) {
65 tran = 0.3F;
66 }
67
68 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
69 RenderingHints.VALUE_ANTIALIAS_ON);
70 GradientPaint p1;
71 GradientPaint p2;
72 if (getModel().isPressed()) {
73 p1 = new GradientPaint(0, 0, new Color(0, 0, 0), 0, h - 1,
74 new Color(100, 100, 100));
75 p2 = new GradientPaint(0, 1, new Color(0, 0, 0, 50), 0, h - 3,
76 new Color(255, 255, 255, 100));
77 } else {
78 p1 = new GradientPaint(0, 0, new Color(100, 100, 100), 0, h - 1,
79 new Color(0, 0, 0));
80 p2 = new GradientPaint(0, 1, new Color(255, 255, 255, 100), 0,
81 h - 3, new Color(0, 0, 0, 50));
82 }
83 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
84 tran));
85 RoundRectangle2D.Float r2d = new RoundRectangle2D.Float(0, 0, w - 1,
86 h - 1, 20, 20);
87 Shape clip = g2d.getClip();
88 g2d.clip(r2d);
89 GradientPaint gp = new GradientPaint(0.0F, 0.0F, BUTTON_COLOR1, 0.0F,
90 h, BUTTON_COLOR2, true);
91 g2d.setPaint(gp);
92 g2d.fillRect(0, 0, w, h);
93 g2d.setClip(clip);
94 g2d.setPaint(p1);
95 g2d.drawRoundRect(0, 0, w - 1, h - 1, 20, 20);
96 g2d.setPaint(p2);
97 g2d.drawRoundRect(1, 1, w - 3, h - 3, 18, 18);
98 g2d.dispose();
99 super.paintComponent(g);
100 }
101 }
102
注意代碼中的幾個(gè)部分:
首先是paintComponent方法中最后一行,我們調(diào)用了父類的paintComponent方法,這是因?yàn)槲覀円扛割悂砝L制字符,但是父類的這個(gè)方法除了繪制字符之外還會(huì)繪制其他的,所以我們需要關(guān)閉掉其他的(當(dāng)然我們也可以自己來繪制字符,但是JButton提供了方法為什么不用呢),所以我們?cè)跇?gòu)造方法那里調(diào)用了:
setBorderPainted(false);
setFocusPainted(false);
setContentAreaFilled(false);
告訴父類不用繪制邊框,不用繪制焦點(diǎn),不用繪制內(nèi)容部分,這部分我們自己來搞*o*。
然后就是這一句了g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)告訴繪制API我們需要平滑一點(diǎn),否則繪制出來會(huì)有很多鋸齒喲。
接下來的這一句g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,tran))告訴繪圖API我們需要繪制一個(gè)有透明度的,tran就是透明度(0-1)。
然后就是將邊框的邊角變直角為圓角,我們繪制一個(gè)RoundRectangle2D,這個(gè)就是邊角都為圓角的方形,然后我們根據(jù)這個(gè)方形來clip我們的方形,這樣方形就被RoundRectangle2D的圓角方形包裹,從而變成了圓角方形。
最后就是繪制外邊線和內(nèi)邊線,通過改變內(nèi)邊線和外邊線的色變從而造成陷入或者突出效果。
整個(gè)JButton改造完畢,如果你能夠活用clip的話,你也可以做一個(gè)五角星的JButton喲。
整個(gè)源代碼我連同Eclipse工程文件已經(jīng)打包成zip放在我的文件里面,下載鏈接如下:
http://www.tkk7.com/Files/ruislan/rswing-0.1.0.zip
posted @
2007-09-11 12:24 ruislan 閱讀(5553) |
評(píng)論 (20) |
編輯 收藏