Corona SDK 入門 part 1

Corona 是一個跨手機平台的SDK, 有免費版及付費版. 若你打算上架App Store, 就一定要用付費版. 官網

理論上可以用在商業程式或遊戲, 看過API後我認為比較偏遊戲開發.

Corona用的是Lua language, 是一種直譯語言, 在遊戲界應該尚算流行. 弱型別語言, 可以用類似物件導向的寫法, 但我覺得還是偏非物件導向, 要封裝及傳遞變數並不是很直覺.

開發環境我是用LDT, 設定上不算複雜, 但也繼承了一些Eclipse的毛病, 總是會遇到問題. 設定方法如下

1) Add a new interpreter for corona simulator in the Lua/interpreter preference page by selecting "add" and selecting the Corona Simulator 
executable as Interpreter executable, adding "-debug" to "Interpreter arguments" and uncheck the option "Accept -e as argument". 
2013-12-16_0004392013-12-16_000453 
2) You should still add the following code at the begging of your project: 
if (system.getInfo("environment")=="simulator") then
require("debugger")()
end 
2013-12-16_000630
3) Copy the debugger.lua file by clicking on the top menu
"Run/Debug Configuration" and then create a "Lua Attach to application"
launch configuration and click on the link "Lua Debbuger Client" and
select "Corona SDK/Ressources" folder. 
2013-12-16_000803
4) Then go to top menu "Run/Debug Configuration" and create a "Lua Application" launch configuration which will use the created Corona interpreter 
as runtime interpreter and launch it in debug mode.
2013-12-16_000837

來源

啟動LDT時記得用管理員權限, 不然你做上面4 steps時會有問題

UPDATE : Corona Editor 1.0 已推出, 自動完成功能 樂勝

每一個程式都要有main.lua, 裡面的variable 雖然用local宣告, 但我看文件覺得那其實是global. 因為Lua不需要main function, 所以main.lua裡的local, 其實整個main都可以用得到.

至於其他檔案看不看得到?要怎樣定義類別及分檔案?這就是我覺得lua很不物件導向的原因, part 2再續…

Android 開發Part4

.R

Android project裡面的values, layout等xml定義資源, 都會自動編譯成.R . 但Eclipse的ADK並沒有很好的處理, 在你引用.R時, Eclipse的自動建議會幫你import錯誤的Android.R. 但其實應該是你目前專案的才對, 這時候要自己輸入import my.package.R. my.package填你建立專案時的包裝名稱

Clean Project

也是跟資源相關係問題, 有時候就算你xml定義好, 也正確的import, 還是會有找不到資源的問題, 這時候可以做兩件事. 先檢查你的xml是否有語法問題. 確認都正確後, 利用Eclipse選單的Project>Clean..去重新編譯資源檔

Empty Adapter

當你想清除原本已綁定(inflate)的view物件, 例如spinner, 網路上找到的view.item.clear()跟view.setAdapter(null)都是錯誤的, 我不知道為甚麼他們要這樣寫, android從2.2.3到4.3.1的API都沒有這兩個定義. 後來我的做法是產生一個沒有內容的adapter, 例如沒有字串的字串陣列, 然後setAdapter(empty)

Localization

若你想支援Localization, 最好是從第一行程式開始就把所有字串都利用values裡的strings.xml參考, 不然之後會找字串找得很痛苦. 包括menu裡的字串也要從strings.xml讀取

GetView(row)

Adapter裡的一個方法, google提供的範例沒有說明原來方法的第二個參數其實是view object的cache (convertView), 檢查這個若不是null, 就直接回傳這個, 可以減少重覆inflate, 有較好的performance

Code Version

每一次想要上傳至Google Play都要改, 簡單的處理方法就是+1, 不然會上傳失敗

Export key

上傳Google Play一定要用的key, 產生時記得年期選100年或以上, 不然會上傳失敗. 雖然我不知道100年跟永久有甚麼分別, Google也不見得可以維持100年…

2013 boring life summary

上一次更新已經是2012 sept. 了! 其實我早就放棄這個blog, 但回來看一下原來我有在寫的時候真的有人在看!
沒有寫的原因, 其中一個是, 技術方面我已經比較少在研究了, 生活也一成不變, 所以…

不過偶而寫一篇記錄一下也無傷大雅, 整理一下2013的軌跡
九月, 我拆牙套了
今年開始比較認真的學結他, 買了我的PRS EG, 我還蠻喜歡的
XBOX360我賣了, 剩下遊戲片以及一支大搖!!
還在新竹, 同一家公司, 做著相同的事, very f23king boring.
跟肥貓豆豆生活一段日子了, 有貓貓很不錯
強仔結婚了, 難得聚在一起, 在台北玩, 還去宜蘭沖浪
伍健做了真正的爸爸

開發軟體方面, 少用了資料庫跟網頁, 多做了很多網路, WPF等東西
最近玩了一下coroana sdk, 還不錯, 還有學了一些javascript event的東西, 有空再寫

2014, 我希望不會再浪費時間, 集中精神在自己真正常做的事上面, 還希望可以多陪家人. 我已經浪費太多時間了

Android 開發Part3

前一篇Part2

Part3的內容 – asynchronous task, view inflation, file I/O.

AsyncTask

為了不影響介面反應, 長時間執行事件需要在背景執行, 利用AsyncTask是不錯的方法。

先看一下官方文件, 這裡最重要的就是泛型宣告, 三個泛型分別用在執行, 進度, 完成三個不同的事件處理器. 這裡要先了解一下AsyncTask的生命週期. 在AsyncTask裡啟動後, 首先會執行doInBackground, 這就是背景方法, 長時間事項要在這個handler裡面進行, 系統會安排背景執行緒去處理. 過程中onProgressUpdate會被呼叫, 用來通知UI事件還在處理中, 這個方法可以不實作. 最後在doInBackground完成後, onPostExecute會被呼叫, 這方法通常是用來把結果顯示於介面. onProgressUpdate和onPostExecute都是介面執行緒負責, 所以可以用來更新介面.

有這個了解之後, 就可以回到泛型, 第一個是doInBackground的參數陣列物件的型別, 第二個是onProgressUpdate的, 第三個是doInBackground傳給onPostExecute的運作結果, 所以doInBackground的return type會跟onPostExecute的參數相同.

這就是AsyncTask的主要功能, 接下來看幾段sample code

MainActivity.cs 產生並啟動AsyncTask

MyAsyncTask task = new MyAsyncTask(this);

task.execute(“Hello”, “World”);

MyAsyncTask.cs定義MyAsyncTask

Public class MyAsyncTask extends AsyncTask<String, Void, String>

{

//skip constructor def

@Override

protected String doInBackground(String… params)

{

String hello = params[0];

String world = params[1];

return hello + “ “ + world;

}

@Override

protected void onPostExecute(String result)

{

TextView view = mainActivity.findViewById(R.Id.mytextview);

view.SetText(result);

}

}

這兩段程式, 首先mainActivity會建立並呼叫MyAsyncTask, 在constructor把自己傳給MyAsyncTask, 讓task可以更新介面, 並給兩個字串參數. MyAsyncTask把兩個參數組合並把結果顯示在一個TextView.

資料綁定Inflation

Android把資料綁定稱為inflation, 資料綁定就是把資料利用指定的介面模版, 是應用程式常見的概念. Android把模版看成是氣球, 把資料吹進去成型, 是很貼切的稱呼.下面是一個最精簡的sample

MyActivity.cs

public class MyActivity extends Activity

{

ArrayAdapter<String> adapter = new ArrayAdapter<String> (this, android.R.layout.simple_spinner_item, new String[] {“water”,“coffee”, “tea”});

Spinner s = findViewById(R.Id.spinner1);

s.setAdapter(adapter);

}

這個程式碼會把三個字串送進一個spinner, spinner是類似下拉式選單的元件.

Inflation其實需要三種物件互相配合, View, Data, Adapter, View負責顯示, Data記錄內容, Adapter就是負責把Data轉成View可以使用的介接器. 這個例子使用Android內建的ArrayAdapter介接, 配合R.layout.simple_spinner_item為Spinner的每一個row的模版. 下一次我會介紹如何使用自訂layout和自建Adaptor.

File I/O

Android有很多種資料儲存方式, 例如SQLite和Preference, 這裡先介紹File I/O.

寫入檔案

FileOutputStream fs = myActivity.openFileOutput(filename, Content.MODE_PRIVATE);

OutputStreamWriter fwrite = new OutputStreamWriter(fs);

fwrite.write(“hello world”);

fs.close();

利用openFileOutput新增一個名為filename的檔案, 並且把Access Control設定為PRIVATE, 內建還有好幾種Access權限, 按需求而選. 寫入字串後關閉.

讀取檔案, 基本上跟寫入相反就可以

InputStream fs = openFileInput(filename);

InputStreamReader fread = new InputStreamReader(fs);

BufferedReader freadBuffered = new BufferedReader(fread);

StringBuffer bffr = new StringBuffer();

String temp = null;

while((temp = freadBuffered.readLine()) != null)

{ bffr.append(temp+”\n”); }

fs.close();

Part 4會介紹如何把Part1~3的功能合起來使用, 例如使用Listener管理AsyncTask的結果, Inflate自訂Layout等

Release Mode程式, 利用Ildasm除錯

如果.NET程式遇到圖一這種情況, 可以利用事件檢視器和Ildasm工具找出問題的原因.

首先, 打開 控制台>…>事件檢視器, 路徑會因為作業系統而改變, Win7是控制台>系統及安全性>檢視事件記錄檔

找出錯誤記錄, 圖二, 記錄描述解讀如下

P1 出錯的程式名稱, P4 出錯的組件, P7 組件裡出錯的方法, P9 錯誤名稱

根據圖二, 出錯的是system.windows.forms, 錯誤為argumentnullexception

接下來就要找出是那一個方法出錯, 先打開Ildasm反組譯工具, 可能找看看下列位置

% WindowsRoot%\Microsoft.NET\Framework\version\

%Program Files%\Microsoft SDKs\Windows\version\bin

我是在%Program Files%\Microsoft SDKs\Windows\v7.0A\bin 找到的

執行Ildasm, File>Open, 把P4的組件打開, 打開後點選View>MetaInfo>Show!

會打開一個文字檔, 裡面是該組件的meta資料, 利用Find功能找出有問題的方法

搜尋 0600+P7, 根據圖二, 這裡要搜尋的是06001521, 找到一個名稱Method #467 (06001521) NotifyEnter的方法

也就是說, 程式裡導致錯誤的原因跟system.windows.forms NotifyEnter方法相關

P.S. Report to Microsoft一點用都沒有, 還是靠自己吧

Android 開發Part2

介面設計

Package Explorer裡, 在Android專案的res資料夾, right click->New->Android XML file. 選一種Layout (最基本的是LinearLayout), 點Next

點Next之後, 會出現一個設定Qualifers的介面, 這裡要小心, 如果你不知道甚麼是Qualifers, 就先不要設定, 不然啟動Activity時會有ResourcesNotFound Exception. 那是因為你有加Qualifers限制, 而AndroidManifest.xml的設定沒匹配, 就會找不到Resources, 也就是你的layout.xml會找不到.

Android介面物件的大小, 單位是dp, 例如一個ImageView的Height = 100dp. 錯了單位compiler不會過. Padding和Margin的單位是dip, 例如marginBottom=”10dip”.

要對齊元件, 要使用Container(或Layout)的Gravity屬性

如果發現Graphical顯示的結果, 長寬怪怪的, 或者orientation不對,  請在畫面設計工具上方設定target device, orientation和主題

啟動Main Activity

跟整個程式有關的設定, 都在AndroidManifest.xml.

先設定本程式會用到的資源uses-permission, 例如android.permission.INTERNET就是需要上網

AndroidManifest.xml裡應該有註明本程式所有的Activity, 一個程式裡的每一個頁面都是獨立的Activity. 要指定一個Activity作為初始, 在<activity/>裡加入<intent-filter/>, filter裡有放兩個elements, 分別是<action android:name=”android.intent.action.MAIN” />和<category android:name=”android.intent.categroy.LAUNCHER”/>

資源管理

layout的xml, drawable如圖片檔, values的xml, 都是放在res裡的資源.

加入圖片檔, 使用一般的複製貼上就可以, 但android對不同resolution的畫面可以使用不同的圖, 分別存放在後綴hdpi, ldpi, mdpi, xhdpi.

顏色定義, 命名好像規定要用Color當後綴, value可以是#RRGGBB或#AARRGGBB

利用資源id除錯. 寫以下try, catch

try

{  //do things with resources }

catch(Exception e)

{

Log.e(“ERROR”, e.toString());

}

Log會告訴你出錯的resource ID, 進去gen>package.yours>R.java 裡面有寫

內部Intent

利用內部Intent在程式內切換頁面

主動的Activity

Intent turnPageIntent = new Intent(view.getContext(), NextActivity.class);

startActivityForResult(turnPageIntent, 0);

這樣就會叫起下一個Activity

被呼叫的Activity

Intent returnIntent = new Intent();

setResult(RESULT_OK, returnIntent);

finish();

這樣就會回到前一個Activity

一定要用到的Event

在Activity裡

@Override

Public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.yourLayout);

}

按鍵事件

Button theButton = (Button)findViewById(R.id.buttonId);

theButton.setOnClickListener(//your code);