高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Python簡介

What is Python?

Python的老爸是,Guido van Rossum,聽說是他在1989年的時候的某個聖誕節無聊,然後就設計出這套程式語言,1991年放出來給大家用,到目前已經有將近20年的歷史了。

目前主流的版本是2.x跟3.x系列,最新的版本目前分別是2.7.2版及3.2.2版,但2.x跟3.x有不少地方是不相容的,各位在使用時還請多留意。

設計哲學

the Zen of Python裡提到了Python的設計哲學:

There should be one-- and preferably only one --obvious way to do it.

這與其它程式語言例如Perl的"There is more than one way to do it"是完全不同的。

What can Python do?

Python是一種物件導向的語言,雖然它被歸類到scripting language一族,但它能做的事情跟一般的程式語言一樣,從數學運算、系統管理到網站開發,它都做得來的,而且都還做得不錯。

有人會覺得嫌Python很慢,老實說,我是不覺得慢到哪裡去。當然拿它跟編譯型的程式語言比當然不公平,不過也就是因為它不用編譯,而且語法很好學好寫,開發速度相對的會比編譯型的語言要來得高。

Who is using Python?

  • Google
  • Youtube
  • NASA
  • ..and ME!

Why Python?

每種程式語言都有它的優缺點,以下是我個人覺得我喜歡Python的一些特點:

程式碼強制縮排

在Python寫程式,程式碼強制一定要縮排,長得像這樣:

say hello
1
2
3
def say_hello():
  print "Hello Python"
  print "Hello again!"

像這樣,不管你是要用tab或空白(space)都可以,要縮2格或4格都ok,只要對齊就行了。只要程式碼沒有對齊,在執行的時候就會直接出現IndentationError的錯誤訊息而無法繼續執行。

也許有些人不喜歡這種被強制規定的感覺,不過我個人還滿喜歡的。在別的程式語言,有的人喜歡把大括號放在最後面,有人喜歡把大括號放下一行,像這樣:

大括號的位置
1
2
3
4
5
6
7
8
function type1(){
  // 把大括號放最後面
}

function type2()
{
  // 把大括號放下一行
}

這沒有誰好誰壞的問題,不過這個在Python就比較不會有這樣的困擾,也因為如此,即使你是接手別人的案子,基本上程式碼的長相也不會差太多。

而且我真的遇過程式碼不縮排或亂縮排的工程師..

支援多個平台

目前幾乎所有比較主流的平台上面都有支援了,也就是說你在Linux底下寫的程式,搬到Windows上也能執行。(當然前提是沒有用到一些系統特定的模組或method)

免費取得

不僅不用錢,而且連Python的原始碼都可以讓你帶回家研究。

開發工具易取得

其實寫Python不用什麼特別的開發工具,基本上只要是一般的文字編輯器就可以了。也有一些像PyDev這種為Python量身打造的IDE(Integrated Development Environment)也不錯用,不過我個人偏好純文字編輯器,例如Vi,至少開啟速度快,而且搭配一些好用的外掛,該有的功能都差不多該有了。

其它

電腦程式語言幾乎都是外國人的世界,你有想過用中文可以寫程式嗎? 有個叫做周蟒的專案做到了。這是用中文寫出來的Python程式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env zhpy
# 檔名: if.py
數字 = 23
猜測 = 整數(輸入('輸入一個數字: '))
如果 猜測 == 數字:
    印出 '恭喜, 你猜對了.' # 一個新區塊的開始
    印出 '(但沒有獎品喔!)' # 新區塊的結束
假使 猜測 < 數字:
    印出 '錯了, 數字再大一點.' # 另一個區塊
    # 你可以在區塊中做任何想做的事 ...
否則:
    印出 '錯了, 數字再小一點.'
    # 只有在猜測 > 數字 的情況下才會跑到這個區塊來
印出 '結束'
# 最後一行語句和"如果..假使..否則"語句是無關的,
# 因為最後的'印出'這行在主區塊中出現,所以這行永遠會被執行.

好寫嗎? 這見人見智了,不過挺有趣的,有興趣的朋友可以玩看看 :)

How to Install Octopress on Heroku

為什麼使用Octopress

這個我想xdite的Why Octopress?寫得很清楚了,就不多做說明了。

安裝

請到Octopress的官網官網找下載點,或是到github上也找得到Octopress on github,如果懶得找,請打開你的終端機視窗,然後跟我這樣做:

> cd /tmp
> git clone git://github.com/imathis/octopress.git

第一步切換到/tmp只是個人喜好,你可以換成別的。進到下載的資料夾裡,安裝需要的gem:

> cd octopress
> bundle install

沒問題的話..

> rake install
## Copying classic theme into ./source and ./sass

再來..

> rake preview

就會在你本機開一個port 4000的web server起來,開瀏覽器起來輸入http://127.0.0.1:4000/可以看到畫面如下:

image

不過你會看到一些像是Blog title都是預設值,這些都是可以修改的,基本上修改都是在_config.yml這個檔案裡,相關設定請參考這裡

不難吧! 你已經完成一半的工作囉,接著我們來寫第一篇文章。

管理介面?

你是說像wordpress那種有個帳號密碼登入的管理介面嗎? 沒這種東西啦! Octopress的文章都是由markdown檔案轉成靜態html,寫完就丟上去,就這麼單純而已。

寫文章

在Octopress,每篇文章就是一個markdown檔,檔案需要放在source/_posts底下,並依照YYYY-MM-DD-post-title.markdown的格式來命名,例如我這篇的檔名就是2011-10-11-how-to-install-octopress.markdown,這樣待會在轉成靜態html的時候才會找得到。

以上這個動作你可以手動自己做,不過我會建議是透過rake指令幫你做:

> rake new_post[how-to-install-octopress]
Creating new post: source/_posts/2011-10-11-how-to-install-octopress.markdown

因為用rake幫你建立的markdown檔,會幫你加一些文章的表頭相關設定。不管你是手動還是rake自動產生的markdown檔,剩下的就是動手寫文章了。

另外,如果你的shell是用zsh的話,上面這個指令可能會讓你出現這樣的狀況:

>   zsh: no matches found: new_post[how-to-install-octopress]

如果遇到這個狀況的話,你可改一下你的.zshrc,加上這行:

alias rake="noglob rake"

或是改一下原來的rake指令:

> rake "new_post[how-to-install-octopress]"

怎麼寫?

就跟你平常寫文章一樣啦! 也許你會好奇,用markdown語法來寫文章真的方便嗎? 對我這種喜歡敲鍵盤而且又常需要在文章裡放程式碼的人來說還挺不錯用的。我是覺得只要習慣的話是還滿快的,markdown的語法要記的也沒多少,寫幾次大概就背起來了。事實上你正在看的這篇文章就是用markdown編寫的。

有推薦的編輯器嗎? 我覺得Mou挺不錯用的!

不會寫? 這裡有小抄可以參考。

寫好了,然後..?

markdown寫好之後,再來就是要把markdown轉成靜態html檔案了:

> rake generate
## Generating Site with Jekyll
unchanged sass/screen.scss
Configuration from /private/tmp/octopress/_config.yml
Building site: source -> public
Successfully generated site: source -> public

執行這個rake generate要注意的是,你可能會在你的文章裡會插入一些圖檔,請記得你的圖檔是放在source/images這個資料夾裡,而不是public/images,因為在做rake generate的時候會以source資料夾為主,並複製一份到public的相對應資料夾裡,如果你是直接把圖檔放在public裡,在比對的過程中就會被自動砍掉了。

在上傳之前

預設.gitignore會把public資料夾給忽略掉而不上傳,這個在一般的環境還ok,因為public資料夾裡的東西如果沒有的話會再自動重生,但因為我們準備把東西傳到Heroku,Heroku的檔案系統是唯讀的,不會讓你做這個動作,所以如果你也是要上傳到Heroku的,請記得修改這個檔案,把public給拿掉,不然你的轉好的內容都會傳不上去而出現Sorry, I cannot find /的錯誤訊息!

當初沒先看文件就亂玩,結果在這邊卡關卡超久..後來是翻heroku logs才發現原因,其實文件根本早就有寫了..orz

上傳到Heroku

因為待會我們要把東西丟上Heroku,所以請先上Heroku申請個帳號,Heroku的Getting Started with Heroku請先翻一下,相關gem也別忘了一起裝起來。

有帳號之後,用heroku來開個專案:

> heroku create happy-octopress
Creating happy-octopress... done, stack is bamboo-mri-1.9.2
http://happy-octopress.heroku.com/ | git@heroku.com:happy-octopress.git
Git remote heroku added

這個happy-octopress請改成你自己取的名字。再來就是把剛剛剛好的東西用git丟上去:

> git add .
> git commit -m 'first commit'
> git push heroku master
Counting objects: 3400, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (1218/1218), done.
Writing objects: 100% (3400/3400), 844.84 KiB, done.
Total 3400 (delta 1949), reused 3352 (delta 1916)

-----> Heroku receiving push
-----> Ruby/Sinatra app detected
-----> Gemfile detected, running Bundler version 1.0.7
       Unresolved dependencies detected; Installing...
       Using --without development:test
       Fetching source index for http://rubygems.org/
       Installing rack (1.3.2) 
       Installing tilt (1.3.2) 
       Installing sinatra (1.2.6) 
       Using bundler (1.0.7) 
       Your bundle is complete! It was installed into ./.bundle/gems/
-----> Compiled slug size is 608K
-----> Launching... done, v4
       http://happy-octopress.heroku.com deployed to Heroku

To git@heroku.com:happy-octopress.git
 * [new branch]      master -> master

就這樣,你可以自己打開瀏覽器,輸入網址http://happy-octopress.heroku.com,或是叫heroku幫你開:

> heroku open

沒意外的話,應該就可以看到架好的Octopress,以及你剛剛寫的文章了。

大概就是這樣,更多的說明請參考Octopress官網。若有任何問題或是有哪邊寫錯了,歡迎一起來討論囉 :)

Zoë – 從SWF匯出SpriteSheet的方便工具

前一篇簡單的介紹了一下EaselJS,其中有個類別叫做SpriteSheet,其實它就是把一些連續動作的圖片排排站的集合拼成一張圖,把檔案及相關的frame data包成SpriteSheet之後,再讓這些連續圖檔連續播放,感覺就會像動畫了。在之後的文章應該還會有更多關於SpriteSheet的說明,如果你還是沒辦法想像Spritesheet是怎麼回事,你可以回想一下設計師在Flash做動畫的時候,會在時間軸上安插一堆的關鍵影格(Key Frame),然後讓每個關鍵影格的動作都稍微有些變化,大概就是這樣的概念,只是HTML5並沒有時間軸可以用。

一個跑步的角色的SpriteSheet圖片可能會長這樣:

image

感謝Maso哥提供!

若你之前曾經用Flash做過些小遊戲,有些動態效果你已經做成MovieClip了,你當然可以打開繪圖軟體來一塊一塊的拼貼成SpriteSheet圖檔,然後還得自己寫frame data,這樣實在有些辛苦。有鑑於此,Grant Skinner大神用Adobe AIR寫了個叫做Zoë小工具,可以讓你很容易的把SWF裡的東西轉成SpriteSheet圖檔,而且還順便幫你產生一個給EaselJS用的frame data。(GS大神請受我一拜!)

安裝

請到Zoë的下載頁面下載相關檔案,再來解壓縮、安裝,應該沒什麼太大的難度。(Zoë是用Adobe AIR寫的,所以你的電腦也需要安裝AIR的runtime)

使用

打開安裝好的Zoë,然後把SWF拖進去,它會自動開始幫你算邊界(bound),或是你也可以自己切到Bounds功能按下Calculate按鈕:

image

再切到Export功能,勾選EaselJS,按下Export按鈕:

image

然後,你會得到一張拼好的png圖檔像這樣:

image

同時你還會得到一個同名的js檔,內容如下:

1
2
3
4
5
6
var frameData = {
  run:[0,4]
};
var img = new Image()
img.src = "hero.png"
var spriteSheet = new SpriteSheet(img,48,68,frameData);

如果你發現你的frameData裡面是空的,可能是因為你少了label關係,這是我在Flash IDE裡的樣子:

image

老實說這軟體沒什麼好介紹的,就是執行、匯出..就這樣。

用這樣轉檔有什麼好處? 首先,如果你之前有做好的動態,至少不用再手工拼貼連續圖。就算是從頭開始做,對設計師來說,在Flash使用關鍵影格來做動態也是很方便的(這是Flash的強項啊)。

動手寫code了

接下來,來試著讓這個SpriteSheet動起來!

1
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
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <title>SpriteSheet Demo</title>
  <script type="text/javascript" src="easel.js"></script>
  <style type="text/css" media="screen">
    #my_canvas{
      background-color: gray;
    }
  </style>
  <script type="text/javascript">
    var stage;

    function init()
    {
      var canvas = document.getElementById("my_canvas");
      stage = new Stage(canvas);

      var frameData = {
        run:[0,4]
      };
      var img = new Image()
      img.src = "hero.png";
      var spriteSheet = new SpriteSheet(img,48,68,frameData);

      var hero = new BitmapSequence(spriteSheet);
      hero.gotoAndPlay("run");

      stage.addChild(hero);
      stage.update();

      Ticker.setFPS(24);
      Ticker.addListener(this);
    }

    function tick()
    {
      stage.update();
    }
  </script>
</head>
<body onload="javascript:init();">
<canvas width="200" height="80" id="my_canvas"></canvas>
</body>
</html>

效果如下:

或按這裡檢視。

程式碼跟上一個範例沒有太大的差別,中間那段SpriteSheet的內容是從剛剛產出的同名js剪過來的。但SpriteSheet本身並不是DisplayObject的一種,所以需要來個BitmapSequence來把它包起來,然後執行閃客們都很熟悉的gotoAndPlay():

1
2
var hero = new BitmapSequence(spriteSheet);
hero.gotoAndPlay("run");

看吧,不用幾行程式碼就能讓帥氣的廖添丁動起來了!!

最後,要再感謝一下Maso哥提供的原始檔。至於會不會在不久的將來就會看到HTML5版的廖添丁呢.. 也許大家可以期待一下囉。

EaselJS新手上路

前言

我一直都沒有忘記我是個閃客(Flasher)的身份,不過近年來因為貪圖紅寶石(Ruby)的漂亮,所以同時也把它一併收到工具箱裡,當做自己謀生的工具之一。

也許你會好奇,Flash/AS寫得好好的,幹嘛學人家用什麼HTML5+JavaScript? 問的好! 簡單的答案是:因為我高興;複雜一點的答案是:我本來就喜歡一些冷門的玩具,而且AS的部份我覺得目前學的能力暫時夠混飯吃了,想來試點別的。雖然HTML5的規格都還沒標準化,瀏覽器的支援程度也都不太一樣,但如果要等到它真的紅的時候再進場,大概也輪不到我了,所以趁專案的空檔來研究一下,若學有所成的話,之後也可以試著引進到自己或客戶的專案裡。

Canvas是HTML5新進的東西,如果以Flash/AS的角度來看,它有點像是Bitmap或是BitmapData,不過操作起來可不像在AS裡那麼簡單。感謝我心目中的偶像 – Grant Skinner大神開發了一個叫做EaselJS的framework,可以讓Canvas的操作變得簡單許多,而且有Flash-like的API可以用,讓Flash/AS Developer可以比較無痛的上手。衝著這點以及GS大神的名號,於是選擇了它來當做我的第一個入門framework。

首先就要來面臨一個最現實的問題了:瀏覽器的支援程度(或該說是客戶電腦的瀏覽器是不是支援?)。這在Flash Player上幾乎沒這個困擾,但在HTML5就不是這麼回事了。目前比較新版本的瀏覽器,例如Firefox、Safari、Chrome以及Opera幾個大廠牌的支援都還算OK。不過我想那些不是重點,你我關注的還是那老兵不死的IE!! 版本新一點的IE也許還OK,但至於舊一點的.. 這個嘛,我想這部份的細節就讓大家自行體會了,對於這個問題,我只能說「I DON’T CARE」。

安裝

基本上不用什麼特別的安裝,到EaselJS的github repo上把壓縮檔下載回來,解壓縮後把lib資料夾裡的easel.js拿出來,放到適當的目錄就能用了。EaselJS是open source的,而且還是MIT授權,所以你可以放心的使用。在這個當下取得的release版本是0.3.2。

上路之前

在開始之前,建議最好對HTML以及JavaScript有些基礎的認識,不然可能容易迷路。接下來,在EaselJS裡有幾個主要的類別你也會需要花點時間了解一下:

DisplayObject體系

DisplayObject

寫過AS3的朋友應該不陌生這個名詞(如果覺得陌生的話,不要跟別人說你會寫AS3喔),它是所有可視物件(例如Bitmap、Shape、Text等)的最上層的父類別,定義一些基本屬性及方法,例如X、Y座標、放大縮小、旋轉、透明度等,更多細節請參閱文件DisplayObject章節

Stage

閃客們,有回家的感覺了嗎? 這個在AS3就是整個場景的最上層(root),但如果你從上往下看的話,也可以說是最下層的物件。Stage把Canvas包起來,然後在呼叫Stage的tick()update()的時候會render被包起來的Canvas。不過跟AS3不同的是,在AS3裡Stage只會有一個,在EaselJS可以有一個以上。細節請參閱文件Stage章節,或是看看底下的程式範例。

Container

這個在AS3是DisplayObjectContainer,在這邊名字變短了,但功能是差不多的,就是可以把一些DisplayObject包起來,以方便操作;當然你想要像在Flash裡面的一樣的一層包一層的巢狀結構也是可以的。在AS3裡常用的addChildaddChildAtremoveChildgetNumChildren等方法,以及碰撞偵測hitTest也都是在這個類別定義的。細節請見Container章節

Bitmap

拿來裝圖片跟影片用的,請參閱文件Bitmap章節

BitmapSequence

這個可能會比較陌生一些了,你可以暫時先把它想像成跟Flash的時間軸差不多的東西,這個類別定義了在Flash裡很常用的gotoAndPlay()gotoAndStop(),也許之後透過一些實作會更清楚它是幹嘛的。細節請參閱文件BitmapSequence章節

Shape

底下會提到的Graphics類別其實是沒辦法把向量圖形直接畫到Canvas上的,得透過Shape類別的包裝,並放到Stage上才行。細節請見文件Shape章節,或見底下的範例。

Text

看名字就知道它是用來處理文字的類別,請參閱文件Text章節

附上一個我自己畫的DisplayObject類別關係圖:

image

其它類別

Graphics

提供了方便的API,讓你可以畫一些向量圖形用的,這個類別還滿重要的,詳細內容請參閱文件Graphics章節

SpriteSheet

在Flash裡如果我們要做一個角色在跑步的MovieClip是很容易的,大概就是勞請設計師幫忙畫在跑步中的各個動作圖,然後使用關鍵影格,把各張圖片包在MovieClip就行了。這邊GS利用SpriteSheet來達到類似的功能,透過這個類別,你可以把一連串的圖片包成一個SpriteSheet來連續播放,看起來就有類似的動畫效果。細節請參閱文件SpriteSheet章節

Ticker

有點類似在Flash/AS裡ENTER_FRAME的東西,讓你可以在每個frame/interval做一些事情,細節請參閱Ticker章節

整體來看,EaselJS的類別不到20個,建議花點時間看一下文件或原始碼,如果你曾經或現在是個Flash Developer、自認AS3還算ok,也許EaselJS對你來說也不會太難上手的。 來寫code吧

講再多沒來個範例一定沒感覺的,來做個會旋轉的正方形好了(我知道這範例還滿無聊的,沒比Hello World強到哪裡去,but just for demo purpose):

1
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
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <title>EaselJS Demo 001</title>
  <script type="text/javascript" src="easel.js"></script>
  <style type="text/css" media="screen">
    #my_canvas{
      background-color: gray;
    }
  </style>
  <script type="text/javascript">
    var stage;
    var box;
    var box_width = 50;

    function init()
    {
      var canvas = document.getElementById("my_canvas");
      stage = new Stage(canvas);

      var graphics = new Graphics();
      graphics.setStrokeStyle(3);
      graphics.beginStroke(Graphics.getRGB(255,255,255,0.8));
      graphics.drawRect(0, 0, box_width, box_width);

      box = new Shape(graphics);
      box.regX = box.regY = box_width / 2;
      box.x = canvas.width / 2;
      box.y = canvas.height / 2;

      stage.addChild(box);
      stage.update();

      Ticker.setFPS(30);
      Ticker.addListener(this);
    }

    function tick()
    {
      box.rotation += 4;
      stage.update();
    }
  </script>
</head>
<body onload="javascript:init();">
<canvas width="300" height="300" id="my_canvas"></canvas>
</body>
</html>

效果如下:

或按這裡檢視效果。

HTML的部份就不多解釋,JavaScript的部份則是把剛才下載回來的easel.js給引進來,然後在body的onload的地方呼叫自己寫的init()。不一定要這樣做,甚至你想把jQuery也引進來用也沒問題,要注意的是,因為這裡僅是做個示範,所以這個簡單的範例沒有特別檢查該瀏覽器是否支援Canvas。

上面這個範例需要說明的大概就是init()裡的那幾行程式碼了:

1
2
var canvas = document.getElementById("my_canvas");
stage = new Stage(canvas);

取得canvas,並把canvas用stage包起來。

1
2
3
4
var graphics = new Graphics();
graphics.setStrokeStyle(3);
graphics.beginStroke(Graphics.getRGB(255,255,255,0.8));
graphics.drawRect(0, 0, box_width, box_width);

利用Graphics類別把方塊畫出來,Graphics的method幾乎都是會回傳Graphics的instance回來,也就是說上面幾行例如setStrokeStyle()或是drawRect()等方法是可以直接串起來寫的。

1
2
3
4
box = new Shape(graphics);
box.regX = box.regY = box_width / 2;
box.x = canvas.width / 2;
box.y = canvas.height / 2;

因為Graphics畫完即使加到stage上,也還是看不到(因為它不是DisplayObject,請見上面的類別圖),所以你需要再用個Shape類別把它包起來,待會要把它放到stage裡。這裡的regX跟regY指的是這個shape的註冊點。如果你不知道註冊點是什麼,你可以試著把設定註冊點那行程式碼拿掉,再看看旋轉效果如何就知道了。

1
2
stage.addChild(box);
stage.update();

呼叫addChild()把剛剛建立的box加到stage上,再呼叫update()來把上面的可視物件給render到canvas上。只有在stage叫呼update()或tick()方法的時候,canvas才會重畫。事實上,當stage呼叫update()的時候,會呼叫所有被這個stage包起來的instance的tick()方法。

1
2
Ticker.setFPS(30);
Ticker.addListener(this);

利用Ticker來設定FPS(每秒30格),並加上listener。

1
2
3
4
5
function tick()
{
  box.rotation += 4;
  stage.update();
}

在每個interval把box轉動4度,然後重畫canvas。這個function一定要取名做tick()嗎? 別的名字不行嗎? 我們在前面請Ticker去監聽,Ticker在每個intereval就會去呼叫被監聽的物件上的tick()方法,所以你如果取做別的名字是沒用的。

如果你是AS3的閃客,應該不會覺得太難上手,如果不是,我想上面這程式也沒多難懂才是。

結論

  1. 目前EaselJS仍是alpha版的,即使是GS大神非常神,但還是不敢保證在使用過程中不會遇到些神奇的狀況。我個人覺得以入門來說,這套用起來算是不會太難,況且GS大神自己也用這套framework幫微軟做了款守城遊戲(Pirates Love Daisies),而且當初用的還只是0.1版的EaselJS。(我真的覺得像這樣可以吃自己做的狗食超棒!)
  2. 我沒有評估過或使用過市面上所有類似的framework,不確定在效能或功能上EaselJS是不是最佳選擇,不過目前看起來效能不至於到非常差,而且入門門檻對閃客來說也不算高。如果各位有能力,也可以直接到EaselJS的github repo去fork一份原始碼來研究看看,說不定也能幫忙貢獻一些心力。
  3. 目前網路上找得到的相關資料似乎不太多,所以官方文件將會是你最好的朋友,最好也跟原始碼做一下朋友。

之後應該還會再實做幾個例子,順便再來幾篇文章,來分享一些更進階的心得。

參考文件: