2013年4月13日 星期六

建立Windows Phone 7平台的PhoneGap專案

要建立Windows Phone 7的PhoneGap專案很容易,步驟如下:

1. 下載並安裝Windows Phone SDK 7.1Windows Phone SDK update for Windows Phone 7.8(為什麼不選擇Windows Phone SDK 8.0?因為8.0的Visual Studio 2012需在Windows 8 64-bit上運行,而Windows Phone 8模擬器甚至指定要Windows 8專業版以上才能相容......):


禁止Windows Phone 7的PhoneGap專案的滑動效果

在Windows Phone 7上使用PhoneGap專案,我們通常會希望不要有兩指縮小放大的功能,要停掉這個功能很容易,只需要在html檔的<head>區域加入:


<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />



除此之外,我們或許還希望禁止滑動效果,那麼則需要開啟BrowserMouseHelper.cs,找到Border_MouseMove()內的這一行:


ScrollDisabled = InvokeSimulatedMouseEvent("mousemove", pos);

將其改為:


ScrollDisabled = true;


這樣就不再有滑動效果了。

2013年3月30日 星期六

解決連結清單的「請修正此表單中的錯誤」問題

這幾天編輯Blogger的連結清單,應該都會顯示「請修正此表單中的錯誤」的字樣,如下:

解決方法很簡單,示範如下:

2013年3月28日 星期四

減少Windows Phone系統上PhoneGap專案的應用程式需求


當你將Windows Phone APP第一次上架,或許會糾結於介紹頁面那一大串應用程式需求:
但天可憐見,你明明沒有使用到這些功能,卻要使用者吐出這些權限,雙方難免都心有疙瘩,為了避免這樣的尷尬事,以下便簡單示範如何縮減Windoes Phone的PhoneGap專案所要求的權限:

Visual Studio 2010專案的編譯版本改為Release

如果你跟我一樣沒有趕上升級Windows 8的限時特賣列車,又想開發Windows Phone APP的話,那麼勢必只能將就地使用Visual Studio 2010 for Windows Phone。基本上可以選擇編譯Debug版或Release版,預設為Debug,以下簡單示範如何改為Release:

2013年3月26日 星期二

將遊戲軟體登錄到台灣官方分級網站


若你也有在開發遊戲APP,請先瀏覽這篇文章。其中大意是說從去年開始,台灣推行一項新的法令,要求所有市面上的遊戲都需要做分級並線上登陸到官方分級網站,否則將可能被強制下架。

Google Play沒有審核制度,因此尚未看到對此條法令的反應,但Windows Phone市集則否,當你提交遊戲應用時,市集會提醒你各國的分級要求(可參考這篇文章:Windows Phone 遊戲分級設定),其中特別提及台灣的此條特殊法令,需要開發者自行備案。基於我們都是守法的好公民,以下簡單示範分級登陸的流程:

2013年3月25日 星期一

申請BlackBerry World的開發者帳號

以下簡單示範如何申請黑莓世界(BlackBerry World)的開發者帳號:

解決"The connection to the server was unsuccessful"的問題

Android的PhoneGap專案有可能會遇到"The connection to the server was unsuccessful"此一錯誤訊息,解決方法是在MainActivity.java檔的onCreate()內盡可能加大timeout時間,如下:

1
2
3
4
5
6
7
8
9
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        
    // 解決 "The connection to the server was unsuccessful" 的問題
    super.setIntegerProperty("loadUrlTimeoutValue", 900000000); 
        
    super.loadUrl("file:///android_asset/www/index.html" );
}



2013年3月24日 星期日

取得Google Code的使用者帳號和密碼

Google Code的界面不是很友好,之前為了找SVN的使用者帳號密碼翻來覆去,好不容易才找著(或者是我特別笨拙?),特別在此紀錄,避免日後又重蹈覆轍。

音效檔播放的問題

PhoneGap提供的播放音樂API似乎存在一些效能和相容性問題,在Windows Phone上會明顯延遲;在Android上的播放次數有限制,超過便無法播放;在IOS上則是單純播放都有問題(1.8.1版)。因此須另找替代方案:

Windows Phone上的專案若只有播放單一音效的需求,可考慮採用播放系統提示聲的API,因為此API不會造成延遲問題,而且可以自行更換提示聲音效檔:

navigator.notification.beep( 1 );  // 播放提示音效一次

至於其他平台,可以先將要播放的音效檔定義在html檔,而後再呼叫使用。譬如說要播放click.wav這個音效檔,可以在html檔這麼定義:

<audio id="clickAudio" src="click.wav" preload="auto"></audio>

然後在要播放的地方加入下面這一句:

document.getElementById( "clickAudio" ).play(); // 播放物件

如此便能播放了。實測下來,在Chrome和Firefox OS上播放順暢,不過在IOS上第一次播放會略有延遲,仍不夠理想,需要再想辦法改進改進。

至於Android系統,很遺憾不支援上面的做法,需要呼叫PhoneGap API,操作如下:

1. 撰寫取得檔案路徑的javascript funciton:
1
2
3
4
5
6
7
// 取得Phonegap可識別的檔案路徑 .
function getPhoneGapPath() 
{
    var path = window.location.pathname;
    path = path.substr( path, path.length - 10 );
    return 'file://' + path;
}


2. 撰寫透過PhoneGap API播放音效的javascript function:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 藉由PhoneGap API來播放音效檔
function playAudio( url )
{    
    // Play the audio file at url
    var my_media = new Media( url,
                          // success callback
                          function()
    {
        console.log( "playAudio():Audio Success" );
    },
    // error callback
    function( err )
    {
        console.log( "playAudio():Audio Error: " + err );
    } );

    my_media.play(); // 開始播放

    //my_media.stop();
    //my_media.release();
}


3. 將音效檔(範例中的檔名為click.wav)丟入存放index.html的資料夾,而後在需要播放音效的地方呼叫剛剛寫的function:
playAudio( getPhoneGapPath() + "click.wav" ); 




2013年3月23日 星期六

提交APP到Firefox OS Marketplace

延續上一篇,現在來示範如何提交APP到Firefox Marketplace,簡易步驟如下:

1. 首先登入Firefox的開發者帳號,而後進入提交頁面

建立Firefox OS的APP專案

基於嘗鮮心理,稍微摸了一下Firefox OS的APP專案製作,初步感覺與Chrome應用程式大同小異,皆以一個manifest檔為核心,且都有區分Hosted AppsPackaged Apps,以下僅列簡易製作步驟:

IOS上的觸摸事件

IOS上沒有滑鼠事件(Mouse Event),只有觸摸事件(Touch Event),因此若要將Windows Phone或Android的PhoneGap專案移植到IOS,須稍做修改,簡易步驟如下:

1. 先找到程式碼中的滑鼠事件定義,譬如說:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 滑鼠左鍵按下的事件 for Windows Phone, Android, ...
document.onmousedown = function( event )
{
    try
    {
        var x = event.pageX + document.documentElement.scrollLeft;
        var y = event.pageY + document.documentElement.scrollTop;
        mousedown( x, y ); // 在(x,y)位置點擊後的事件處理
        
    }
    catch ( err )
    {
        errorMessage.innerHTML += "發生錯誤: " + err.stack + "<br>";
    }
}



2. 在此滑鼠事件定義的旁邊添加相對應的觸摸事件:
touchstart對應mousedown、touchmove對應mousemove、touchend對應mouseup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 對應滑鼠按下的觸摸事件 for IOS
document.ontouchstart = function( event )
{
    try
    {
        var touch = event.touches[0];
        var x = touch.clientX + document.body.scrollLeft;
        var y = touch.clientY + document.body.scrollTop;
        mousedown( x, y ); // 在(x,y)位置點擊後的事件處理
        
    }
    catch ( err )
    {
        errorMessage.innerHTML += "發生錯誤: " + err.stack + "<br>";
    }
}


3. 測試是否可行,打完收工。

MAC OS X Snow Leopard上建立Xcode的PhoneGap專案

由於我本身用的是MAC OS X 10.6.8,沒有繼續升級,Xcode僅支援到4.2版,因此以下僅示範4.2版的Xcode如何建立PhoneGap專案:

1. 首先當然是下載並安裝Xcode,若你的作業系統版本跟我一樣是Snow Leopard,可直接使用以下BT連結:

http://thepiratebay.se/torrent/6721955/


不依賴PhoneGap API前提下設置IOS系統語言

若你跟我一樣,MAC OS X的版本停留在10.6.8,那麼勢必也只能使用4.2版以下的Xcode,進一步說,也就是只能加載1.8版以前的PhoneGap專案。不幸的是,1.8版的PhoneGap API尚未成熟,其中一項,便是無法獲得系統當前語言。

然而山不轉路轉,我們依然有辦法獲取系統語言,只是需要碰觸到一丁點的原生程式碼(對我來說,Object C完全是火星文般的存在,非萬不得已,最好是眼不見為淨),簡易步驟如下:

1. 開啓Classes裡頭的MainViewController.m。


2. 在webViewDidFinishLoad()內加入數行程式碼,如下:

 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
- (void) webViewDidFinishLoad:(UIWebView*) theWebView 
{
     // only valid if ___PROJECTNAME__-Info.plist specifies a protocol to handle
     if (self.invokeString)
     {
        // this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready
        NSString* jsString = [NSString stringWithFormat:@"var invokeString = \"%@\";", self.invokeString];
        [theWebView stringByEvaluatingJavaScriptFromString:jsString];
     }
     
     // Black base color for background matches the native apps
     theWebView.backgroundColor = [UIColor blackColor];
    
    // ------------- 加入開始 -----------------
    
    // 取得系統語言 
    NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0];
    
    // 組合呼叫用的字串
    NSString * front = [NSString stringWithFormat:@"setDefaultLanguage(\""];
    NSString * back = [NSString stringWithFormat:@"\");"];
    NSString * temp = [front stringByAppendingString:language];
    NSString * jsFunctionString = [temp stringByAppendingString:back];
    
    // 呼叫設置語言的javascript funciton
    [theWebView stringByEvaluatingJavaScriptFromString:jsFunctionString]; 
    
    // ------------- 加入結束 -----------------

    return [super webViewDidFinishLoad:theWebView];
}



3. 一樣是編寫設置語言的javascript function:

 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
// 設置預設語言
function setDefaultLanguage( language )
{
    if ( ON_DEVICE ) // 實機
    {
        if ( language.toUpperCase().match( "TW" ) ||
            language.toUpperCase().match( "HK" ) ) 
        {
            gLanguageIndex = ZH; // 繁體中文
        }
        else if ( language.toUpperCase().match( "CN" ) )
        {
            gLanguageIndex = CN; // 簡體中文
        }
        else if ( language.toUpperCase().match( "EN" ) )
        {
            gLanguageIndex = EN; // 英文
        }
        else if ( language.toUpperCase().match( "JA" ) ||
                  language.toUpperCase().match( "JP" ) )
        {
            gLanguageIndex = JA; // 日文
        }
        else if ( language.toUpperCase().match( "KO" ) )
        {
            gLanguageIndex = KO; // 韓文
        }
        else
        {
            gLanguageIndex = EN; // 實機的預設語言
        }
    }
    else // 非實機
    {
        gLanguageIndex = TW; // 非實機的預設語言
    }
}



4. 編譯後即可看到成果。




2013年3月22日 星期五

IOS和Android上取得系統語言

IOS和Android平台,可以利用PhoneGap(2.2版以上)所提供的API來獲取當前系統語言,簡易步驟如下:

1. 設置 device ready事件:

1
2
3
4
5
6
7
8
// 初始化動作,啓動程式時呼叫
function init()
{
    if ( ON_DEVICE )
    {
        document.addEventListener( "deviceready", onDeviceReady, false );
    }
}


2. 定義device ready事件發生時所呼叫的function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var gLocaleName; // 取得的區域名稱

function onDeviceReady()
{
    // 因為Windows Phone 7不支援此API,所以做個區隔
    if ( gDeviceName == ANDROID || gDeviceName == IOS )
    {
        navigator.globalization.getLocaleName(
            function ( locale ) { gLocaleName = locale.value; },
            function () { alert( "Error getting locale\n" ); }
        );
 
        setDefaultLanguage( gLocaleName ); // 以區域名稱來設置語言
    }
}


3. 定義設置語言的function:

 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
// 設置預設語言
function setDefaultLanguage( language )
{
    if ( ON_DEVICE ) // 實機
    {
        if ( language.toUpperCase().match( "TW" ) ||
             language.toUpperCase().match( "HK" ) ) 
        {
            gLanguageIndex = ZH; // 繁體中文
        }
        else if ( language.toUpperCase().match( "CN" ) )
        {
            gLanguageIndex = CN; // 簡體中文
        }
        else if ( language.toUpperCase().match( "EN" ) )
        {
            gLanguageIndex = EN; // 英文
        }
        else if ( language.toUpperCase().match( "JA" ) )
        {
            gLanguageIndex = JA; // 日文
        }
        else if ( language.toUpperCase().match( "KO" ) )
        {
            gLanguageIndex = KO; // 韓文
        }
        else
        {
            gLanguageIndex = EN; // 實機的預設語言
        }
    }
    else // 非實機
    {
        gLanguageIndex = TW; // 非實機的預設語言
    }
}


4. 每次啓動可取得當前語言gLanguageIndex,再隨之做調整即可。

2013年3月21日 星期四

Windows Phone應用市集提交結果告知信


當我們將APP提交到Windows Phone應用市集後,會轉入上面的訊息頁面,它告訴我們此時能做的,就是耐心等待。

等待三至五天後,會收到從開發者中心寄來的通知信件,裡頭可能有兩種結果。

2013年3月20日 星期三

利用Google Drive來擺放Chrome App的網頁


Chrome APP分兩種,一種是Packaged Apps,一種是Hosted Apps。前者是本地端應用,可以將所有文件都包覆在壓縮檔內,無須上網便能直接啟動;而後者則是遠程端應用,壓縮檔內可以只放描述檔manifest.json,啟動後會直接連往指定的首頁,簡而言之,可以視為一種封裝後的書籤。

我後來選擇包裝成Hosted App,因為一來包裝簡便,二來更新容易,只需要更新伺服器端的js檔,便能同步更新用戶端。

而若要製作Hosted App,首先有個問題:上哪找網頁存放空間?js檔好解決,因為js檔可當作檔案被存取,但html檔不能被當作檔案,否則瀏覽器會直接顯示html的原始碼。

探訪了幾個免費存放網頁的服務,較著名的Google SiteDropPages都不能擔負此重責大任,前者僅能提供格式受限的網頁,後者則會擅改html和js檔。探查結果,大概只有GitHub PagesGoogle Drive符合需求。在這邊我選擇使用後者來存放html檔,而js檔則置放於Google Code,以提醒自己不至於疏忽更新。

Google Drive原則上屬於網路硬碟,若要存放公開網頁,需要一些步驟,簡易流程如下:



Windows Phone改寫回上一頁的行為

因為PhoneGap天生侷限,對換頁反應比較遲鈍,因此地鐵暗棋所有頁面轉換都在同個檔案的canvas上進行。而這麼做雖然有效避免頁面切換的延遲時間,但也會造成一些問題,主要是回上一頁的預設行為與我們預期將不再相符。

譬如說我們希望不論身處何處,只要按上一頁的按鈕,都能回到程式主頁,但系統並不知曉程式還有頁面之分,所以會一律跳回Live Title頁面,導致程式結束。

所幸,我們可以藉由改寫回上一頁的行為,來達到符合預期的頁面切換。PhoneGap有提供回上一頁的API,在Android系統上可直接呼叫使用,但並沒有很好的支援Windows Phone 7,因此還是直接修改原始碼會比較具有彈性。簡易流程如下:

1. 我們可利用InvokeScript()容易丟出例外的特性,在任一js檔撰寫一個回上一頁的function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 進行回到上一頁的處理
function goBack()
{
    if ( isStartPageNow() )
    {
        navigator.app.exitApp(); // 以InvokeScript執行此句會造成例外
    }
    else
    {
        showPage( START_PAGE ); // 秀出開始頁面
    }
}



2. 開啟專案中的CordoveView.xaml.cs,修改page_BackKeyPress():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void page_BackKeyPress( object sender, CancelEventArgs e )
{
    try
    {
        CordovaBrowser.InvokeScript( "goBack", "" ); // 若有例外發生代表正處於開始頁面
        e.Cancel = true; // 取消預設回到上一頁的行為
        return;
    }
    catch ( Exception ex )
    {
        // 代表正處在開始頁面,故不處理繼續執行,以結束程式。
    }
    
    // ------ 以下程式碼不動 ------



3. 重編譯後即可見到效果。

2013年2月24日 星期日

Windows Phone設置預設語言

PhoneGap雖然有提供地域和語言的相關API,但悲劇地是並不支援Windows Phone 7.5/7.8,因此需要自行添加此功能,簡單流程如下:


1. 加入已設置好各國語言的字串變數 :

var gLanguageIndex = EN; // 目前預設語言 

var ZH = 0; // 繁
var CN = 1; // 簡
var EN = 2; // 英
var JA = 3; // 日
var KO = 4; // 韓

var STR_NORMAL = new Array( "一般", "一般", "Normal", "一般", "일반" );
var STR_SHADOW = new Array( "陰影", "阴影", "Shadow", "影", "그림자" );
var STR_HALO = new Array( "光暈", "光晕", "Halo", "暈", "후광" );


2. 在任一js檔中加入設置預設語言的javascript function :

 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
// 設置預設語言
function setDefaultLanguage( language )
{
    if ( ON_DEVICE ) // 實機
    {
        if ( language.toUpperCase().match( "TW" ) )
        {
            gLanguageIndex = TW;
        }
        else if ( language.toUpperCase().match( "CN" ) )
        {
            gLanguageIndex = CN;
        }
        else if ( language.toUpperCase().match( "EN" ) )
        {
            gLanguageIndex = EN;
        }
        else if ( language.toUpperCase().match( "JA" ) )
        {
            gLanguageIndex = JA;
        }
        else if ( language.toUpperCase().match( "KO" ) )
        {
            gLanguageIndex = KO;
        }
        else
        {
            gLanguageIndex = EN; // 預設語言
        }
    }
    else // 非實機
    {
        gLanguageIndex = TW;
    }
}


3. 在CordovaView.xaml.cs中加入設置預設語言的C# function :

1
2
3
4
5
6
7
8
9
// 設置預設語言
public void SetDefaultLanguage()
{
    String language = CultureInfo.CurrentUICulture.Name;
    Debug.WriteLine("語言:" + language);

    // 設置跟系統相同的顏色
    CordovaBrowser.InvokeScript("setDefaultLanguage", new String[1] { language }); 
}


4. 在CordovaView.xaml.cs的GapBrowser_LoadCompleted()呼叫設置預設語言的C# function :

1
2
3
4
5
6
void GapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    //CordovaBrowser.Opacity = 1;
    Debug.WriteLine("GapBrowser_LoadCompleted :: " );
    SetSystemColor(); // 設置系統背景顏色和色塊顏色
    SetDefaultLanguage(); // 設置預設語言

windows phone設置系統顏色


Windows Phone的一個特點是可以設置系統顏色,並體現在Live Title與各樣的APP介面,而PhoneGap並沒有提供相關的API可供呼叫,因此需要手動加入,以下是簡單的流程:

1. 寫個設置系統顏色的javascript function :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 設置系統顏色
function setSystemColor( accentBrushColor, backgroundBrushColor )
{
    if ( ON_DEVICE ) // 實機執行
    {
        systemBackChessColor = accentBrushColor; // 系統前景顏色
        systemBackColor = backgroundBrushColor; // 系統背景顏色
    }
    else // 非實機測試
    {
        systemBackChessColor = backChessColor;
        systemBackColor = backColor;
    }
}


2. 在 CordovaView.xaml.cs中加入設置系統顏色的 C# function :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 設置系統背景顏色和方塊顏色
public void SetSystemColor()
{
    SolidColorBrush accentBrush = (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
    String accenColor = "#" + accentBrush.Color.ToString().Substring(3);

    SolidColorBrush backgroundBrush = (SolidColorBrush)Application.Current.Resources["PhoneBackgroundBrush"];
    String backgroundColor = "#" + backgroundBrush.Color.ToString().Substring(3);

    //Debug.WriteLine("\naccenColor: " + accenColor );
    //Debug.WriteLine("\nbackgroundColor: " + backgroundColor);

    CordovaBrowser.InvokeScript( "setSystemColor", new String[2] { accenColor, backgroundColor }); // 設置跟系統相同的顏色
}


3. 在 GapBrowser_LoadCompleted() 中呼叫設置系統顏色的 C# function :

1
2
3
4
5
6
void GapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    //CordovaBrowser.Opacity = 1;
    Debug.WriteLine("GapBrowser_LoadCompleted :: " );
    SetSystemColor(); // 設置系統背景顏色和色塊顏色
    SetDefaultLanguage(); // 設置預設語言


4. 設置大底完成,如此每次啟動程式,都能取得目前系統顏色,再依個人喜好條配。




2013年2月19日 星期二

起始

這是一個小型專案的開發日誌,專案名為地鐵暗棋。顧名思義,這是款暗棋遊戲,而地鐵這個詞,則是描述本作的界面風格,由於最先以Windows Phone作為發展平台,因此刻意摹仿微軟近期的地鐵風格(Metro Style)。

地鐵暗棋基於PhoneGap,最終希望能跨足眼前絕大多數的移動平台,諸如iOS、Android、Windows Phone、BlackBerry等,和新興作業系統,如Windows 8和Chrome OS等。

由於僅能利用下班閒暇時間進行,因此進度不快,且在開發過程中遇到許多瓶頸,容易讓人消磨熱情,於是建立此日誌,用於紀錄開發進度與技術疑難,別做抒發之用。



2013年2月13日 星期三

High Contrast:黑底白字的另類選擇


介紹一個可讓網頁黑底白字的Chrome擴充程式:High Contrast,跟一般變色手法不同,它不是改變字體和背景的CSS色碼,而是在上頭加上一層濾鏡,別具特色,一般白底黑字網頁可轉為黑底白字,而原本就是黑底白字的網頁,則又轉為白底黑字。而且快捷鍵切換色調也相當迅速,推薦一試。