2011/12/30

客製化 ListView 產生 ScrollRunnable 互搶的問題解法

最近用了一個朋友寫的客製化的 ListView,主要的用途是作類似 iOS 上可以作 Pull down refresh 的功能。

但遇到了一個小問題困擾我很久。

因為 pull down 的 scrolling back to first item 的工作是由一個 ScrollRunnable 搭配 Scroller 實作的,這東西基本上平常運作都沒有問題,但是如果在 Scroll Fling 到最頂端的時候就會有 scroll 亂跳的問題。

深究 ListView/AbsListView 之後,發現其實 AbsListView 內有一個 mFlingRunnable 負責控制 Fling 動作發生時的捲動動作,而當使用另外一個  ScrollRunnable 去控制捲動的時候,會跟原本的 mFlingRunnable 的捲動互搶,造成捲動亂跳的問題。

知道原因了,要解決就很簡單吧?

錯了。

AbsListView 並沒有預留讓開發者存取 mFlingRunnable 的介面,拿不到 Runnable 就無法取消它。難道我要把整個 AbsListView, ListView 搬出來嗎 XDD

還好找到了一個不太好的 workaround。但是鑑於要把整個 AbsListView 移出來的成本太大,就將就著用了。答案是當使用 smoothScrollBy 系列的 method 的時候,AbsListView 將會自己移除 mFlingRunnable 的操作:


    public void smoothScrollBy(int distance, int duration) {
        if (mFlingRunnable == null) {
            mFlingRunnable = new FlingRunnable();
        } else {
            mFlingRunnable.endFling();
        }
        mFlingRunnable.startScroll(distance, duration);
    }

所以當你要操作自己的 ScrollRunnable 的時候,先執行 smoothScrollBy(0, 0) 就行了。因為參數都是 0 所以基本上 Fling 的動作就會取消了。

如果哪位看官有更好的解法請務必跟我說 :D

特別感謝 David Wu 跟我一起看了這個問題!

2011/12/14

Android 自製元件 - PrismFlipper

這幾天公司的 app 要作訂製的 notification bar,花了一點時間做出來,把他整理出來成一個小 view widget,這個 widget 主要是一個客製化的 notification bar,主要是想模擬三角稜柱翻動的樣子。跑起來大概像下面這樣:



使用方法也很簡單,用 flipper.showNext(text, reverse) 就可以使用了,下面附上比較完整的範例


final PrismFlipper flipper = (PrismFlipper)findViewById(R.id.viewFlipper1);
final String[] texts = new String[] { "Refresh", "go to last read position", "last read post" };
flipper.setFrontText(texts[0]);
flipper.setBackground(new ColorDrawable(0xff3465a4));
flipper.setTextColor(Color.WHITE);
Button btn = (Button)findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
   
 @Override
 public void onClick(View arg0) {
  flipper.showNext(texts[mPosition], false);
  mPosition = (mPosition + 1) % texts.length;
 }
});
        
btn = (Button)findViewById(R.id.button2);
btn.setOnClickListener(new OnClickListener() {
   
 @Override
 public void onClick(View arg0) {
  flipper.showNext(texts[mPosition], true);
  mPosition = (mPosition + 1) % texts.length;
 }
});

完整的範例可以在 github 上找到,授權是 BSD license。

2011/10/17

Android 小技巧:找出未用的 Resource

記得好像有其他 Android 內建的方法可以哪些 resource 沒有被使用。一時找不到,Google 一下發現另外一套:Android Resource Tracker

使用方法也很簡單,在 project 目錄底下執行:
$ java -jar <PATH_TO_JAR>/AndroidUnusedResources1.4.jar

接著就會列出未被使用的資源:

Running in: /Users/yurenju/git/YOUR_PROJECT
242 resources found

44 unused resources were found:
array     : upload_photo_options
    /Users/yurenju/git/YOUR_PROJECT/res/values/arrays.xml
...

2011/10/11

Eclipse 小技巧 - 同時設定多個 method breakpoint

有時候正在除錯剛寫好的 class 時,會需要針對 class 底下的所有 method 設定中斷點。不過一個一個設定實在太麻煩了,Eclipse 提供一個很貼心的功能,可以直接將多個 method 設定中斷點。

打開 Eclipse 找到列出所有 Method 的 Outline,用 Ctrl/Cmd 多選 Method 後,按右鍵選取 Toggle Method Breakpoint 即可。


2011/09/22

Javascript client library 之 TDD

最近因緣際會在寫 web service 的 javascript client library,就趁著這個機會試試看 TDD (Test-Driven Development) 的方式來開發看看。

這次撰寫的 client library 因為是 javascript,所以就利用了 Javascript 常見的 event driven 的呼叫方式。原本打算用 jsunit 作為 unit test 的框架,但因為沒看到可以測試 callback 的 method,最後改用了 jquery 所使用的 qunit

qunit 在測試 callback 的方式大略如下:


test(
  'Login success test',

  function() {
    expect(1);
    stop();

    var service = new WebService();
    service.login(
      {
        username: 'user1@example.com',
        password: 'password'
      },
      function(res) {
        equal(res.response.status, "OK", 'expected login success');
        start();
      }
    );
  }
);
qunit 可以利用 stop() 停止整個 unit test 的進行,等到呼叫 start() 的時候再繼續執行。在這個測試中,qunit 會在 stop() 之後開始等待,等到呼叫到 callback method 裡面的 start() 之後才會繼續執行,這個時候就可以擷取到 equal() 所測試的結果。另外 expect() 可以指定預期會跑到的 assert 總共有幾個,在這個例子裡面只跑了一次 equal(),所以是 expect(1)。 當所有 test case 完成後,跑 qunit 的結果大略如下:


因為根本就還沒開始寫 client library,當然所有測試結果都是 failed。但在這個時候就已經得知 javascript client library 要如何使用以及有哪些回傳值了。這樣其實可以在早期的時候就可以看到整個 client library 的面貌。

而且看著 test case 一個一個的通過心中真是有莫名的快感阿。寫完之後就變成這樣:



使用 TDD 方法開發確實讓整個開發的過程踏實不少。不過這種開發方式還是比較適合實作函式庫,如果撰寫 UI 的話就沒有那麼適合了。

不過大家還是可以玩一下,蠻有收穫的 :)

2011/09/10

Jenkins 系列 (1): 在終端機下設定 Android 模擬器

命題的有點怪,不過基本上這是篇為了在 Jenkins 進行 unit test 以及 daily build 的前置動作。當在建立 Continuous Integration 的測試系統時,我希望可以在一台獨立的機器進行測試。而在遠端的伺服器不一定有 X Window 的狀況,這個時候就會需要在終端機上設定 Android Eumlator 環境。

首先是下載並且解壓縮 Android SDK

$ wget http://dl.google.com/android/android-sdk_r12-linux_x86.tgz
$ tar zxvf android-sdk_r12-linux_x86.tgz

切換到 android-sdk-linux_x86/tools,並且用 --no-ui 選項來安裝 Android 3.2/2.3 或其他平台相關的 Platform SDK


$ cd android-sdk-linux_x86/tool
$ ./android update sdk --no-ui

這個時候會從最新的平台(如 Android 3.2)開始下載安裝,一路從最新的下載到最舊的 SDK。我是在下載完 Android 2.2 的 Platform SDK 就按 Ctrl + C 終止安裝。

編輯家目錄的 ~/.bash_profile,加入執行路徑:

PATH="$PATH:~/android-sdk-linux_x86/tools:~/android-sdk-linux_x86/platform-tools

安裝完 platform SDK 之後,可以利用下面的指令看到安裝了哪些 platform SDK:

$ android list target

輸出大略如下:

id: 4 or "android-8"
     Name: Android 2.2
     Type: Platform
     API level: 8
     Revision: 3
     Skins: QVGA, WVGA854, WQVGA432, HVGA, WVGA800 (default), WQVGA400
id: 5 or "android-9"
     Name: Android 2.3.1
     Type: Platform
     API level: 9
     Revision: 2
     Skins: QVGA, WVGA854, WQVGA432, HVGA, WVGA800 (default), WQVGA400
id: 6 or "android-10"
     Name: Android 2.3.3
     Type: Platform
     API level: 10
     Revision: 2
     Skins: QVGA, WVGA854, WQVGA432, HVGA, WVGA800 (default), WQVGA400


用下面的指令就可以建立新的 Android 2.3.3 模擬器環境

$ android create avd --name android-2.3 --target android-10

這個時候就可以用不跑模擬器畫面的方式啟動 Android 模擬環境:

$ emulator-arm -avd android-2.3 -no-window
想知道運行狀況,可以利用 adb logcat 瞭解。

2011/08/27

jsmodem - 讓 jslinux 使用網路功能

前陣子有個很有趣的 project - jslinux,這個專案主要是在瀏覽器上面模擬 x86 電腦,再將 Linux 跑在瀏覽器上面。等於說這個專案簡單實作了 PC 模擬器,就如同 VMWare, VirtualBox 一樣,但是在瀏覽器上就可以跑模擬器上面的 OS 了。甚至在上面還包含了一個 C compiler,可以在上面編譯程式。


2011/08/06

Mocking Bird - node.js REST API simulation (1)

當團隊決定開發一個使用 web service 的 mobile app 時,我們遇到了一個小問題:mobile app 跟 web service 是同時開發的,當 web service 還沒實作完畢前,mobile app developer 只能暫時先從規格中 implement 跟 web service 銜接的介面。

這聽起來有點瞎子摸象。

不過 Jamie Sa 提供了個有趣的點子:如果我們可以寫個模擬的 REST API service,並且透過 YAML 定義簡單的規格的話,我們就可以利用這個模擬 REST API 比較真實的跟 web service 銜接了 :)


2011/06/25

Python and GObject-Introspection 簡報

2011/6/25 在 PycTW 2011 講了 Python and GObject-Introspection,主要講 GObject-Introspection 的原理還有簡單的介紹 python-gobject 如何實作 GObject-Introspection。投影片如下:


2011/05/31

h4addicted - 聚會主題搜集器

上次聚會的時候, Rex 講了一個小問題。通常我們在聚會之前,其實就會有一些覺得有趣的主題想要帶到 Hacking Thursday 討論。所以希望有一個地方可以搜集這些主題。不過大家都有各自用的服務如 plurk, twitter 或是 facebook,通常有不太想要只是因為要搜集主題就需要額外使用一個 service (如 friendfeed)。

於是大家就討論想要寫一個 web app 來解決這個問題。基本上的概念就是希望在發言的時候如果加了 #h4 時,就會把這個發言丟到 web app 上,條件是至少要在 twitter 與 plurk 都可以使用 (Facebook 會比較麻煩,所以就先跳過了)。然後我們就在 David 的筆電上蓋看到 addicted 這個字,所以這個專案就叫做 h4addicted 啦。

今天我研究了一下,發現這個服務很容易就可以使用 Yahoo Pipes 兜出來!基於懶惰的原則,我就沒有自己去寫程式了 :P

Yahoo Pipe 基本上是一個可以從網路上撈資料進行後處理的 web app。所以我們要做的就是搜集大家的 feed,對各個 service 做一些後處理後產生新的 RSS/JSON 就完成了。你可以到下面這個網址:

http://pipes.yahoo.com/yurenju/h4addicted

功能其實是非常簡易的,原本製作也很簡單,只要把大家的 RSS feed 拉進來就結束了。不過事情沒有這麼簡單!我們把 twitter, plurk, facebook (對,後來 David 又透過 JSON 撈到了 Facebook 的資料,但是前提是你的 post 是公開的。)都弄出來之後發現有幾個問題要解決:

  1. Plurk 的 RSS link 欄位填寫的是相對網址,需要對字串作後處理
  2. Facebook 也有相同的問題,因為是從 JSON 轉過來的,所以也沒有 link 欄位。
  3. Facebook 因為根本沒有與 link 對應的欄位,所以要從 post id 那邊分析後取得 post id,然後再跟 user id 合併後產生 link 欄位
  4. Facebook 的 message 欄位沒有 author,所以要額外把 name 加入 message 欄位
所以比想象中的複雜一點,不過還是搞定了 XD

首先看概觀


Plurk 的部分其實就是把 link 拿出來加上 http://www.plurk.com/ 的前綴後再塞回去就完成了。右邊的 facebook 我則是把它獨立成另外一個模組處理。


  1. Rename 模組負責把 JSON 的格式改成與 RSS 類似,讓訊息可以正確的的解譯出來。
  2. Loop - String Builder 的部分是為了作讓 user name 加在 message 前面,才知道是誰的發言。
  3. Loop - String Regex 的部分則是用來把 item.id 裡面類似 724235041_10150262195320042 的結構取出後者(也就是 post-id)並且儲存
  4. 取出 post-id 後再利用一次 String Builder 把 facebook 前綴、 user-id 與 post-id 連接在一起,就完成了。

Yahoo Pipes 真是非常方便的工具,利用它其實可以做到非常多事情了 :)

也歡迎 clone 拿去使用。

2011/05/30

GNOME3 延伸套件開發

這幾天到 KaLUG 講了一場『GNOME3 延伸套件開發教學』,其實也幫我自己上了一堂課。在這邊寫下一些心得讓大家參考一下。

3D 加速後的 GNOME3 桌面

在 GNOME3 裡面多了一個 gnome-shell 的軟體,主要負責提供 GNOME3 新一代的使用者體驗,包含更容易使用的 workspace,新的 application 管理機制等等。比如說下面的影片講解了 workspace 系統:



而這些絢麗的使用者體驗歸功於新的 UI Toolkit - Clutter 以及 Shell-Toolkit (ST)。Clutter 是一套利用 OpenGL/OpenGL ES 加速的 UI Toolkit,可以簡單的創作出許多絢麗的特效。比如說我們要撰寫一個小程式,當使用者點擊圖像之後,做出淡出、放大並且位移的特效。若使用 GTK+ 或其他 UI Toolkit 亦然可撰寫出來,但採用 Clutter 撰寫時就會格外簡單許多。


(因為使用桌面錄影的關係,感覺有些遲鈍,不過實際上跑的時候還蠻順暢的。)

這個 Demo 程式源碼如下:
const Clutter = imports.gi.Clutter;
const Tweener = imports.tweener.tweener;

function click(actor, ev) {
    let properties = {  time: 2.0,
                        x: texture.x+100,
                        y: texture.y+100,
                        scale_x: 1.5,
                        scale_y: 1.5,
                        opacity: 0
                    };
    Tweener.addTween(texture, properties);
    print('Clicked!');
    return true;
}

function quit(actor) {
    Clutter.main_quit();
}

Clutter.init(0, null);
let stage = new Clutter.Stage();
let texture = new Clutter.Texture({ filename: 'test.jpg',
                                    reactive: true });
texture.connect('button-press-event', click);
stage.connect('destroy', quit);
stage.add_actor(texture);
stage.show();

Clutter.main();

上面這個範例可以在 Debian 安裝 gir1.0-clutter-1.0, gjs 之後即可執行。

GNOME3 extension 採用 Javascript 撰寫

其實在 gnome-shell 的 UI 部分幾乎都是採用 Javascript 撰寫而成。而理所當然的開發 extension 時當然也是採用 Javascript 開發。然而最好的地方在於開發 extension 的時候,就像是 firefox extension 一樣,你可以任意的存取原本 gnome-shell 的所有元件。比如說,你可以存取 panel 的日曆元件,並且加入或修改這些元件。

Looking glass - 幾近作弊的 console

在 GNOME3 extension 開發的時候有個非常好用的工具叫做 Looking glass,這個東西就像是雷神之錘按下 "~" 就可以叫出來的 console (然後可以在裡面輸入作弊指令)。在 GNOME3 的環境下,按 alt + F2 並且輸入 lg 你就會看到這個 console 緩緩地從上面降下來。

這東西有什麼用途呢?最好用的工具是左上角那隻筆。它的作用是可以拿來點 GNOME3 桌面元件的任何東西,接着你就可以取得那個元件,然後做些什麼事情。


選取元件之後會跳回 looking glass,這個時候系統就會給他一個存取他的變數 r(NUM),接下來你可以在 console 使用這個代號調用它。


當你點擊這個物件的時候,他就會把這個物件的 method 全部列出來,馬上就可以知道有什麼 method 可以調用。然後!這個東西當然不是只有看的,你甚至可以在 looking glass 裡面使用 r(NUM).method() 直接在 runtime 執行特定 method。比如說我執行了 r(13).set_text("I don't care")



系統的日期馬上就任性了起來!當然你也可以做些旋轉或什麼之類的事情,而且由於 clutter 的特性,作了修改後的元件依然可以操作。



最後附上 Tutorial 簡報,請多多指教 :)

2011/05/16

Linkedin 連結

Hi 各位讀者大家好,下面是小弟的 Linkedin 連結:

http://tw.linkedin.com/in/yurenju

歡迎寄信給我索取詳細資料 :)

yurenju -AT- gmail -DOT- com

2011/04/16

[簡報] 手把手教你寫 gnome-shell 擴充套件

前幾週在 GNOME 3 Release Party 給了這個 Talk 『手把手教你寫 gnome-shell 擴充套件』,分享一下探索下一代 GNOME3 的桌面環境擴充套件,並且簡單的示範如何使用 Javascript 寫出下面這個 Screenshot 的擴充套件



GNOME3 的桌面可是充滿著絢麗動畫的 framework 呢 :)

簡報在下面,歡迎參觀。不過提醒一下 GNOME3 這次發佈並沒有提到 extension 系統的部份,想必是 API 目前也還沒有穩定。歡迎敢死隊現在就開始寫 gnome-shell extension!

2011/02/24

Python-GTK 簡報與勘誤

感謝 Study-Area 與 GNOME Taiwan 的邀請,一月份下旬給了一份 talk,投影片在此。

另外也感謝 Mosky 以及 Hychen 的指正,部分演講內容作出修改。

  • 第 35 頁的奇技淫巧其中的回傳多個變數的能力,實際上內部是透過 tuple 實做,並不是真的可以回傳多個變數。
  • __init__ 並不是建構子,而是物件的初始化 method
  • 範例裡面的 gtk-gtr2.py 使用了 except 但卻沒有指定例外種類,這也是不建議的寫法。