2014年10月11日 星期六

Brackets : a IDE for JavaScript, HTML and CSS



  今天在掃Github Trend時發現了Brackets,由Adobe提供的Open Source Project,相關推薦文在這。

對開發者而言有幾點很棒的好處讓我也想使用它:

1. Auto Refresh : 裡面叫做Live Preview,也就是以後不用狂按Save-Reload,還可以安裝Static Preview的套件,就可以迅速架設一個Local的網站供其他人測試

2. 完整的文件提示 : 隨時按Cmd+K就可以看Html及Css屬性的文件

3. Color Picker 

4. Image Preview
...

看來最近如果想玩Frontend的東西都會用它了吧!




Canvas coordinate system

    最近加新功能的時,碰到一個在Canvas上座標系統認知上有所不同的地方;一般我們會用 Canvas.drawBitmap(Bitmap, 10, 10, Paint)把一張圖畫在(0,0)的位置,當然這個(0,0)不預期是整個device的(0,0),而是根據View最後被擺放的位置,例如某View被Layout在(100, 100),而Canvas.drawBitmap(Bitmap, 10, 10, Paint)則會畫在螢幕(110,110)的地方。

到目前為止都沒有錯誤,但如果translation的方式是用Matrix,而你的code可能會變成

Matrix matrix = new Matrix();
matrix.postTranslate(10, 10);
canvas.setMatrix(matrix);
Canvas.drawBitmap(Bitmap, matrix, Paint);

此時畫出來的結果就還是會在(10, 10)。原本猜想是忘記apply原本canvas的matrix所致,所以改成

Matrix matrix = new Matrix(canvas.getMatrix());
matrix.postTranslate(10, 10);
canvas.setMatrix(matrix);
Canvas.drawBitmap(Bitmap, matrix, Paint);

結果還是(10,10)!結果不小心喵到有Method叫Canvas.concat()。後來改成:

Matrix matrix = new Matrix(canvas.getMatrix());
matrix.postTranslate(10, 10);
canvas.concat(matrix);
Canvas.drawBitmap(Bitmap, matrix, Paint);

最後就成功的畫在(110,110)的位置。百思不得其解的是:

Matrix matrix = new Matrix(canvas.getMatrix()) 應該會等於 canvas.concat(matrix)

因為都會Apply原本Canvas裡面的Matrix參數,於是乎就去看了一下底層的source code

 public void concat(Matrix matrix) {
     native_concat(, matrix.native_instance);
 }

 public void setMatrix(Matrix matrix) {
          
matrix == null ? 0 : matrix.native_instance);
 }

看來還要追到NDK那層,不過看起來兩種設定Matrix的方式有些出入,就等未來有空再往底層看了。





2014年9月27日 星期六

StreamTokenizer

    最近在實作藍牙時,碰到處理接收資料的問題,原本是用InputStream.read(byte[]),但會碰到字串切割的問題,假設原本想傳送110給App,就有可能會收到1跟10的兩個數字;

    因無法判斷何時需要斷點,直覺想到可以自定義分割符號(例如:冒號,100:110:120),如果要自己實作buffer的話又嫌太麻煩。

    最後找到Java有StringTokenizer以及StreamTokenizer就可以自動幫我分割好字串(生活變得更加美麗),有興趣的話可以參考範例

參考鏈結:
StringTokenizer  http://developer.android.com/reference/java/util/StringTokenizer.html
StreamTokenizer  http://developer.android.com/reference/java/io/StreamTokenizer.html

2014年7月16日 星期三

How to handle binary file by Parse's Cloud Code


   最近在開發Side Project的時候,因為不預期有Server Team的support,所以就來玩玩Parse的Cloud Code,這個東西大概在一兩年前就有碰過一次,那個時候是上傳一些感測器的資料,如果超過某個Threshold就要寄送notification到device。

   這次的則是用在交易管理,最棒的是Stripe已經是Cloud Code的其中一個module,所以整合的過程不算太複雜;比較麻煩的事,我們需要上傳一包binary到Parse的Storage,如果由Client直接Create Data Object的話,就可以用ParseFile的物件直接搞定,該死的事繞過Cloud Code,它的Input預期是個JSON,這時候就有幾種解法:

1. 把Binary用Base64 Encode後放進JSON,但超過30MB的JSON會不會有其他悲劇發生?!
2. 直接Client端用ParseFile存檔,JSON裡面放上傳成功的URL。因為Binary沒有跟任何一筆Data Object關連,所以從後台Clean Cache的時候(可能)就會清掉。
3. 同上的Idea,但把資料放到S3。一樣很難清掉沒有意義的資料,因為存檔的動作都是由Client進行。
4. 用Pointer!Create 另外一個Table(Class),Client先Create好一筆Data後,把Object Id傳給Server做關聯。

   最後我們採用第四種做法,兼具consistance以及簡化的流程。但看了Parse的Free Plan,20GB的儲存空間,以我們的使用情境,大概1000筆訂單後就要開始付錢了:<

2014年7月7日 星期一

Retrofit : A type-safe REST client for Android and Java



    Square 雖然是做支付的公司,但也open了許多好用的library,像是OkHttp、Picasso跟Otto等等;目前公司產品內有使用的是Otto,它解決了Activity跟Fragment,或者Fragment跟Fragment之間的雙向溝通(關於Otto改天再寫別篇文章來描述使用情境),而今天要介紹的Retrofit能簡化Client跟Server間的Code。

    舉例說明,如果你要對Restful Server拿資料,首先你要開HttpClient,然後把Api的參數(POST/GET/PUT/DELETE以及Parameters)塞進去;因為Android不允許Http Connection跑在UI Thread,所以你必須在包一個AsyncTask;接著要Parsing Response Body,如果是JSON可以用內建的JOSN Library去一層一層get出來,聰明一點就會用Gson Library。對了你別忘記了Http還有很多複雜的情境,像是Response Code : 301/302的Redirect、Cache的功能、Retry的機制等等等等。

完整的程式片段可能會像這樣(別忘了,它還沒辦法handle 301/302、Cache跟Retry的機制):


    Retrofit能讓這一切簡單一些,讓您不再畏懼/厭煩對接所有Restful API,關於使用細節就直接參考官方文件。把同樣的程式用Retrofit再寫一次的結果如下:


如果有任何問題,歡迎各位一起討論。




2014年6月1日 星期日

Dart : The New Platform for Scalable Web App Engineering

Google的風格就是,當某種語言/工具無法滿足需求時,就會自己創造一個全新的,Dart也是其中一個產物。
https://www.dartlang.org/

Dart的目的是要取代Javascript,因為它實在是太多問題了(Firefox : 哪會),但目前除了Chrome以外,沒人願意把DartVM綁在自己的Browser裡,不過目前Dart Code還是可以轉成Javascript(轉換後不預期你做任何修改),執行在其他的瀏覽器上面(據說Performance也比原本的Javascript Native來的好),如此創新的作法還真的讓人躍躍欲試,但是,想到許多原本用的很開心的Javascript library都要等別人包成Dart的Plugin才能使用。

基於最後的結論,我還是緩緩先。


Reference:

Wiki : http://zh.wikipedia.org/wiki/Dart

2014年5月6日 星期二

Pull-to-Refresh on Android

    Pull-to-Refresh(PTR)的功能在iOS是原本就support的功能,而在Android上面既然要靠第三方Library(現在則否)來實現,由此可見Andorid生態系的UI原件多麼貧乏;自然而然就會有開發者去寫一個類似iOS的功能:

Android-PullToRefresh : 我們產品原本也用這個,但是碰到難以延伸的問題之後就改用別的(後面會提到),難以延伸的問題來自原件直接繼承,如果你要用在GridView,那你就必須用PullToRefreshGridView,這就會碰到如果你的GridView是客製化的(可能你會用GridViewHeader,或StickyGridHeaders,這個問題來自另外一個需求,Android的GridView沒有辦法加Header跟Footer),那這個Library就會讓你傷透腦筋,有了這個設計瓶頸,使我們不得不嘗試使用ActionBar-PullToRefresh。

ActionBar-PullToRefresh : Android為了更像Android,連PTR的行為也改得跟iOS不同,也就是Refresh的時候在ActionBar上會有一條跑馬燈在奔馳著,這套Library就是採用這種實作,自然而然程式的架構也不會跟UI元件直接耦合在一起。不過使用它必須要注意的是API的版本跟ActionBar的實作方式,它推出了三種Library:

1. actionbar-pulltorefresh:library : api 14+
2. extra-actionbarcompat-pulltorefresh : api7+. 使用support-library實作ActionBar
3. extra-actionbarshelock-pulltorefresh : api7+. 使用ActionBarshelock

這套Library採用Delegate的方式與元件互動,有興趣的話可以直接看他們的example code

mPullToRefreshLayout = (PullToRefreshLayout) view.findViewById(R.id.ptr_layout);
ActionBarPullToRefresh.from(getActivity())
                    .allChildrenArePullable()
                    .listener(this)
                    .setup(mPullToRefreshLayout);

但可惜的是該套Library已經不在維護,因為Google官方的support-library(v19.02)已經默默推出了SwipeRefreshLayout;再者它跑在Amazon Kindle機器上也會有Bug,感覺是padding算錯了,所以沒有辦法跟ActionBar貼在一起,所以最後我們會採用Google官方的版本。

SwipeRefreshLayout(API 7+):他跟ActionBar-PTR不同的地方在於Animation,上者是從左到右的跑馬燈,而它從中間往外擴的跑馬燈,而且在拖拉到一半的時候不會有提示訊息擋在ActionBar上,API使用的方式也蠻像ActionBar-PRT,但目前是第一版所以有些問題需要自己修改。

1. 無法ListView/GridView無法ScrollUp : 這個問題會發生在你包Layout的時候,如果第一個元件不是List/GridView的話就會發生。
2. 拖拉時,該頁面"一定"會彈跳 : 有些Design不預期有這種行為,但現在的API架構也無法讓你取消它。


寫到最後,才發現如果把細節寫下去可能會長到太過誇張,所以這篇文章先定位成選用不同PTR Solution的Outline,而實作及設計細節就留給其他文章當作"救援投手"了。