2011年5月8日 星期日

[POC][筆記][Rhino on Android] - 新的onClickListener

上次的作法:

var button1 = findview(R.id('button1'));

button1.setOnClickListener(function(view, methodName)
{
     if (methodName == "onClick") {
         log("MyScript", "clicked");
} });

 

當然不是很滿意, "if(methodName == "onClick")"這樣的寫法太醜了啦!

所以這次的目標是:

button1.onclick = function(view) {

     log("MyScript", "clicked");

     alert('Hi there!');

};

這樣比較像印象中javascript的寫法

為了達到這目的, 必須在View中加入onclick這個property, 但原本View class(Java)並沒這東西, 如何將它包裝出來?

Rhino對於Javascript使用原生的java objects並不是直接使用, 還是有透過一層包裝, 因此這就是要做出這個property的路了, 包裝的點在於WrapFactory, 而原生的Java object會被包裝長NativeJavaObject 

而Rhino允許我們用自己的WrapFactory取代原本的, 像是這樣:

jsContext.setWrapFactory(new AndroidWrapFactory());

因此我們會需要自己實作這一個AndroidWrapFactory, 把原本的View改用不同的包裝, 而非原本的NativeJavaObject, 基本上只需要實作wrapAsJavaObject即可:

 

這邊用了一個新實作的ViewWrapper來取代NativeJavaObject

本來我的打算是繼承自NativeJavaObject, 然後再加入自己的東西, 不過後來發現這樣的作法總會在"put"時出差錯, 原因是NativeJavaObject是沒辦法加入新的member了(其實這說法有點問題, 應該是只要設prototype給它的話還是可以), 加上一些比較實用的function是定義在ScriptableObject的裡面, 因此, 我最後採用ScriptableObject來包裝

但要如何在ScriptableObject裡面實現像NativeJavaObject那樣可以直接叫用原本的Java class裡面的member呢?這部份不用自己實作, 只要把原本的NativeJavaObject當做這新的ViewWrapper的prototype就好了, 所以這邊constructor呼叫的super contructor是:

public ScriptableObject(Scriptable scope, Scriptable prototype)

===> super(scope, new NativeJavaObject(scope, javaObject, staticType));

defineProperty這包裝是從ScritableObject那邊改寫其中一個過來的, 主要是用來讓我們可以把這些"onlick","onlongclick"包裝成property, 在ViewWrapper可以實作getter和setter, 為了避免名稱上的混淆, 所以把getter的prefix定義成"jsget"而setter是"jsset"

在onlick的setter中, 除了將function object存起來外, 還直接對原本包裝的View設一個OnClickListener, 動作則是執行這個Function object

最後, 前面的範例裡面有一個"alert('Hi! there')", 因此為了"alert()"多實作一個Alert.java出來(跟上面的無關啦), 這是source:

 

接下來的目標, 

  • 包裝一個實際真的可以拿來開發的package, release 0.1 alpha
  • 實作XmlHttpRequest
  • Adapter & AdapterView, Service & Provider