作者:野比 ([email protected]) 時間:May, 2012 封面圖片為野比原創(chuàng),請勿未經(jīng)允許私自引用 #1-1 嗯,,各位,,又是我,生物鐘顛倒的家伙,。 今天我要山寨的是大名鼎鼎的Apple,,傳說中的「被山寨之王」。 沒錯,,都被我山寨好幾次了,。 說起Apple,相信大家對他家的各種產(chǎn)品,,不管他軟還是硬,,都有相當(dāng)?shù)暮酶小?/p> 最近Apple把自家的Web瀏覽器Safari升級到了第5版,并同步推出了Windows版,,支持WinXP開始的全部Windows版本,。 不得不說,這是一個很給力的瀏覽器,,它看起來就像這樣,。
Icon
for Safari 其實我并不是蘋果控,我控紅富士要多點,。客觀的評價Safari,,這個軟件界面華麗,,速度快,但在Windows平臺上,,TopSites首頁資源消耗巨大,,操作習(xí)慣和常規(guī)Win瀏覽器有一定區(qū)別,部分網(wǎng)頁不支持或不兼容(WebKit引擎),。 不多說了,,這不是重點。重點在于它的「偏好設(shè)置(Preference)」界面,,就是這個:
看到這個,,你肯定會覺得怎么蘋果的東西會變得這么一般呢?不過就是TabControl上面增加了幾個圖標(biāo)嘛,。 嗯,,朋友,你說的似乎沒錯,。但是,,我曾經(jīng)也算中肯的評價過蘋果的東西,,拋開外觀,蘋果的特點之一就是「悶騷」,,還有「OCD」,,也就是強迫癥。 聽我這么說顯得很干癟,,那么就讓我順著導(dǎo)航標(biāo)簽,,一路點擊過去,看看會發(fā)生什么事,。
沒錯,,這窗口會自動伸縮,而且是動畫的,!這就是apple悶騷的地方,! 為了不讓他一家獨騷,為了不辜負(fù)他被山寨之王的名頭,,我只好勉為其難的山寨一番了,。
山寨前的準(zhǔn)備 山寨其實沒啥好準(zhǔn)備的,但還是需要幾樣重要的東西:
分析,,分析 山寨的靈魂在于分析,,首先把剛才拍的高清果照扯過來分解了。
所以,,我把他分解成這幾個部分:
組件設(shè)計 分析了其中的功能,,那么就要想想怎么來實現(xiàn)。 從功能來看,,這個窗口實際上是由多個子面板切換來實現(xiàn)的,,最多他加了點自動縮放。所以從本質(zhì)來說,,還是一個標(biāo)簽切換的窗口,。 我最早想到的就是大名鼎鼎卻又丑得無以復(fù)加的TabControl。
按照標(biāo)簽切換這個思想,,TabControl完全可以勝任這次的山寨需求,。但是TabControl這么丑,必須要給它整整容才行,。想不到我竟然有整容的才華,。
因為要改動的地方會很多,,所以還是完全自己來繪制標(biāo)簽好了。為了完全自定義TabControl,同時方便循環(huán)利用,,從TabControl派生一個我們自己的標(biāo)簽控件TabControlEx,。
這就是我們的TabControlEx,看起來和TabControl沒什么兩樣(那是當(dāng)然的),。 為了讓他看起來不太一樣,,在構(gòu)造函數(shù)里加上下面的代碼。
這段代碼的意思就像注釋里說的,,注意ControlStyles這個枚舉是可以按位組合的,,所以上面要用「或(|)」來進行連接,這樣系統(tǒng)就會完全忽視TabControl這個基類的界面顯示,,而使用我們自己的方式來呈現(xiàn)UI,。 現(xiàn)在TabControlEx看起來是這樣的。
啥米,??。MG,!東西哪去了,?? 嗯,,當(dāng)我第一次玩UserPaint的時候,,也被嚇了一跳。其實這就是上面我們設(shè)置的那句ControlStyles.UserPaint,,于是系統(tǒng)就不幫我們畫任何東西了,。 所以從現(xiàn)在開始,一切都要靠自己了,。下面所有的繪制都在OnPaint()方法中繪制。 為了先讓我們找到方向,,在OnPaint()方法中,,我們先把Tab的位置找到,為此我們給每個Tab的邊框都畫出來,。
TabControl.GetTabRect(int)的功能是獲得指定index的標(biāo)簽的矩形位置,。畫完后,我們的TabControlEx看起來不那么迷糊了,。
可是,,標(biāo)簽的大小還是不對,我們要的不是普通的那種長條,而是悶騷的蘋果的瘦高型,,要像這樣,。
嗯,好吧,,我們回到構(gòu)造函數(shù),,用下面的語句來設(shè)置大小。
上面設(shè)置44x55其實只是因為蘋果原版剛好是這么大,,先這么著,,后面如果不合適了,回頭再來改?,F(xiàn)在標(biāo)簽是這樣的了,。
Apple標(biāo)簽的選中狀態(tài)是帶陰影的,看起來很酷,,可是如果我用GDI+來畫的話,,什么漸變什么變換,煩都煩死了,。怎么辦呢,? 請記住,我們正在山寨,。所謂山寨的精神,,就是不問方法、不擇手段,,只要最后「看起來一樣」就行了,。所以,我決定用上摳圖大法,,把apple的背景圖摳出來,。
把這個背景保存為TabBackground.bmp文件,然后添加到項目中,,把它做成「嵌入的資源」,,就像這樣。
然后我們用一個變量來保存背景圖,。因為這張圖隨時會用到,,所以還是做成全局變量(類級別),在構(gòu)造函數(shù)里讀取圖片,。
現(xiàn)在有了圖標(biāo),,加上去看看吧。在OnPaint()里這樣寫,。
只有被選中的標(biāo)簽才會出現(xiàn)這種背景,。于是,,標(biāo)簽變成這樣了。
繪制文字 這會看著還挺單調(diào)的,,所以我們來加點料,。下面來畫文字。說起文字,,我想你應(yīng)該注意到了,,Safari的標(biāo)簽文字,都是帶有陰影的(準(zhǔn)確的說是高光),。
所以,,在繪制文字時,先用高光色繪制第一遍,,再用普通文字色(黑)繪制第二遍,。
繽紛色彩的源泉:圖標(biāo) 文字也有了,那么接下來就輪到圖標(biāo)了,。TabControl是用ImageList控件來存儲自己使用的圖標(biāo)的,,那么添加一個ImageList,然后加入圖標(biāo),。注意這里都要32x32的圖標(biāo),,所以應(yīng)該設(shè)置ImageList.ImageSize為32x32。
嗯,,現(xiàn)在我們的標(biāo)簽看起來像那么回事了,,接下來就該難看的紅線條退休了。再完善一下,,我們的標(biāo)簽就OK了,。
同步滾動演示。上面是山寨,,下面是正品,,正品打開了文字抗鋸齒,我們也可以,,在OnPaint()事件開始加入這樣的代碼,。
到此,標(biāo)簽導(dǎo)航部分已經(jīng)完成,,剩下的,,就是窗體的自動縮放功能了。
作者:野比 ([email protected]) 時間:May, 2012 #1-2
嗯,,還是我,。 現(xiàn)在繼續(xù)昨天的山寨,。昨天我們分析得到了4條需要山寨的部分,,如下,。
我們參考下圖,,
我們制作的TabControlEx是作為它所在窗體的子控件存在的,為了獲得包含TabControlEx的窗體(的引用),,可以調(diào)用TabControlEx的FindForm()方法(從Control繼承),。FindForm()可以獲取容納該控件的頂層窗體,在我們的例子里,,就是我們的山寨Safari窗體,。 為了在TabControlEx剛剛加入父控件的時候(也就是窗體初始化的時候)就能夠順利「劫持」到窗體的引用,并修改它的標(biāo)題(否則顯示Tab0的時候會發(fā)現(xiàn)窗體的標(biāo)題還未改變),,我們重寫一下TabControlEx的ParentChanged事件,。
這樣,,我們就可以在啟動時就修改父窗體標(biāo)題了。我們最終的目的是每次切換標(biāo)簽時都改變父窗體標(biāo)題,,現(xiàn)在我們拿到了窗體的引用,,只需要重寫TabControlEx的Selected事件,。
下面是完成之后的效果
自動調(diào)整窗體大小 完成了雜項工作,現(xiàn)在要進入今天的重點:自動調(diào)整大小,。在開始之前,,先來回顧一下這個悶騷的功能。
下面來好好分析一下到底發(fā)生了什么事,。 注意,,大家發(fā)現(xiàn)右下角那個問號沒有,?根據(jù)觀察,,那個問號始終是保持在窗體右下角的,這就好辦了,,直接Anchor到Right和Bottom就行,。因此下面的分析中直接無視它了。 從本質(zhì)上來看,,因為切換的標(biāo)簽內(nèi)容高度不同,,所以窗體高度也發(fā)生了改變。但不管怎么變,,窗體的底部到最下面一個控件的距離Δ沒有變化,,參考分析圖,。
所以,,動畫就是在H1-H2這段距離內(nèi)發(fā)生的,。另外,,值得注意的是,,Safari是在窗體動畫完成,調(diào)整大小到位以后,,才顯示新標(biāo)簽的控件,,這樣做可以顯得很有動感,而且留下了足夠的時間加載控件,。所以,,動畫應(yīng)該在標(biāo)簽的Selecting事件里解決,而顯示控件留到Selected事件,。 下面來分析大小調(diào)整的算法,。
山寨算法:從不追求精確還原 通過慢鏡頭分析,可以看到在相同時間差內(nèi)窗體大小的運動距離是不同的,,這說明窗體大小不是勻速改變的。
為了不讓算法影響我們的設(shè)計進度,,將算法寫在單獨的方法里(最正規(guī)的應(yīng)該是寫成委托,,直接傳遞方法,但你認(rèn)為一個山寨貨有必要嗎),。
既然這樣,,那么算法的問題我們稍后再來討論,現(xiàn)在研究怎樣讓窗體動起來,。 由于動畫過程較長,,將近1秒,那么我們實現(xiàn)的時候應(yīng)當(dāng)盡量以不影響主線程為前提,。除了動不動就多線程這種有點大炮打蚊子太2的方法外,,我們還可以用系統(tǒng)自帶的Timer。在每個Timer.Tick事件里挪一步,,合起來就成了動畫,。
現(xiàn)在我們可以填寫剛才分析的Selecting和Selected事件了。
算法嘗試
目前流行的加減速函數(shù)有很多,,最簡單的從1次函數(shù)(勻速),、2次函數(shù)(勻加速)到3,、4甚至5次函數(shù)都有人在用。這類指數(shù)型的加速函數(shù)使用簡單方便,,用得很多。下面是在Mahematica里繪制的幾種函數(shù)曲線,,從上倒下分別為:g=10的自由落體函數(shù),,y=x^2,y=x^3,,y=x^4和y=x直線(注意:為了讓大家看清函數(shù)細(xì)節(jié),,x和y軸不是1:1的)。
看起來要實現(xiàn)又加速又減速還真是麻煩,,看來只有去掉減速了,。反正山寨嘛,只要「看起來像」就行了,。沒辦法,,我們是搞山寨的,手藝當(dāng)然不行了,,所以到底用那種,,還真的不知道。山寨大法告訴我們,,不知道的東西,,「試,就對了」,。那么就選3個版本的getHeight()來試試,。
作者:野比 ([email protected]) 時間:May, 2012
(未完待續(xù)) |
|