[程式語言] 手機APP開發入門 - 精華區

[程式語言] 手機APP開發入門

特種兵

特種兵圖像(預設)

2019-12-12 20:05:08

From:211.23.21.202

如今的手機APP開發方案(框架)很多,但是對視障者而言不是態複雜就是有障礙。
這套NativeScript不但開發過程視障朋友都能完全掌控,誠品的畫面輸出也都能支援 TalkBack 和旁白。
此外它還很簡單,而且相同的程式碼就能分別編譯為 Android 的 apk 和 iOS 的 ipa APP 檔案。

我跟大家一樣第一次接觸這個領域,找過不少資料也測試過幾個方案,
最後找到這個框架有提供命令列操作模式,覺得不錯所以推薦給大家。
歡迎大家跟我一起一步一步走進這個全新的世界。
有任何想法、問題都歡迎在下面留言,互相交流才能進步,我也才有動力繼續寫下去。
開發 APP 的電腦系統需求很簡單:
如果您要開發的是 Android APP 那就用 Windows + NVDA.
如果是 for iOS 或 Android 也要那就需要 macOS + VoiceOver.
然後最好能熟悉 JavaScript, XML, CSS 這三種語言。

接下來就是安裝開發環境所需要的軟體:

  1. node.js:
  2. NativeScript CLI:
    用命令列執行 npm install -g nativescript
  3. APP所需套件:
    • Windows: 以管理員身份執行 @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://www.nativescript.org/setup/win'))"
    • macOS: 執行 ruby -e "$(curl -fsSL https://www.nativescript.org/setup/mac)"
    • 以上最後如果詢問是否需要安裝模擬器,因為我們用不上就回答 n

最後診斷一下開發環境有否缺失,執行 tns doctor
如果有看到 no issue 這個關鍵詞那就恭喜大功告成,可以繼續往下閱讀了。

以上更多說明可以參閱 https://docs.nativescript.org/angular/start/quick-setup
※最後更新時間:2019-12-07 13:35:53 From:1.161.148.79 By:特種兵
開發環境建構好了,接下來就要動手實幹了。
首先我們借用官方提供的實例來熟悉整個流程。

第一步是建立專案,執行 tns create HelloWorld
指令最後的 HelloWorld 就是專案名稱,也是最後做出來的 APP 檔案 .apk (Android) 或 .ipa (iOS) 的主檔名。
您可以隨意取名,但是只能使用 A-Z, a-z, _, ., 和空格這些符號。

過程需要回應兩個問題:
第一是選擇搭配哪種開發風格,共有四種可選,
系統游標會停在最後一項後面,但是預設選項市地一個 Angular.
請用小鍵盤7往上閱讀,每個選項右邊都有說明。
請用游標上下來選取項目,請注意選取的項目開頭會多出一個 > 符號,如果語音沒有報讀可以按 NVDA+P 把標點符號等級條到全部。
為了方便說明,這次請統一選擇最後一向 Plain JavaScript. 確定選對後按 ENTER.

第二是選擇模板,操作方式同上。
共有三個模板,預設是第一個 Hello World. 我們直接按 ENTER.

接著它會上網抓取所需檔案,完成後會多出一個資料夾,就是您取的專案名稱。
您可以先用檔案總管瀏覽一下他的結構,接下來的內容就精彩了,敬請期待。
一般人開發APP可以隨時利用模擬器觀察執行結果並且即時做調整,可惜這部份我們無法操作。
幸運的是選用 NativeScript CLI 框架可以讓我們用實體手機替代達成相同的效果。

進行這個程序必須先在手機安裝NativeScript Playground APP 如下:

預覽的步驟是依照現實狀況執行下列指令:

  1. cd HelloWorld (請自行替換專案名稱)
  2. cls 清除畫面
  3. ALT+ENTER 把螢幕放到最大
  4. tns preview

等到螢幕上出現提示訊息,接著打開手機 Playground APP,
找到 Scan QR code 用兩指點兩下,聽到手機語音回會,將鏡頭對準電腦螢幕掃描 QR code.
掃描成功手機會發出嗶聲。首次執行惠要求安裝 NativeScript Preview APP, 點按安裝即可。
接著等候一段處理時間,手機螢幕就會出現這個專案APP的執行畫面,您就可以開始探索執行了。

這個APP會顯示標題為 My App.
接下來是提示訊息 Tap the button.
再下面是一個 TAP 按鈕
最後試提示訊息 42 tap left.
這時候您每點一次 TAP 按鈕,夏面的數字就會減一,到達1的時候再點一次訊息就會變成 Hoorraaay! You unlocked the NativeScript clicker achievement!

提醒您:如果這個預覽程序對您來說還是很有困難請別氣餒,
跳過這個程序並不影響您的開發工作,況且稍後還會有替代程序。

現在就先玩一下這個剛出爐的 APP, 下一截就來展示及時修改程式碼。
電腦端可以按兩次 CTRL+C 結束程序。
※最後更新時間:2019-12-08 14:25:32 From:1.161.148.79 By:特種兵
啟用預覽模式除了可以搶先觀察程式的效果外,
最棒的是能即時修改電腦上的程式碼,無須重新執行或編譯程式就能同步看到手機畫面上的改變,非常節省時間有效率。
那我們要修改什麼呢?

這個APP都顯示英文,個人看不慣,要改成中文。
再來從42點到1手指會抽筋,想要改成3就好。
下面就開始我們的奇妙旅程。

電腦端不要關閉終端機視窗同時直接用檔案總管瀏覽專案資料夾,其中的 app 子資料夾是我們要關注的對象,其他的都別動直接進入。
用記事本打開 package.json.
第2行: "main": "app.js", 這行告訴我們程式的進入點是 app.js,
所以關掉這個檔案,同樣用記事本打開 app.js.

從 /* 開始到 */ 之間的內容是 JavaScript 程式碼的註解說明,這段的大意是說

在 NativeScript 中,app.js 檔案是您的APP的進入點。
您可以用這個檔案為您的APP進行初始化,不過他的主要目的是把控制權交給指定的模組。

接下來這行
const application = require("tns-core-modules/application");
就是指定常數 application 代表 tns-core-modules 資料夾下的 application 模組。(該資料夾位於 ..\node_modules 資料夾下)

application.run({ moduleName: "app-root" });
這行呼叫 application 模組的 run 函數執行 app-root.

最後又是一段註解說明,提醒我們不要把程式碼放在 application.run 這行的下面,因為 iOS 不會執行這些程式碼。
到這裡這個檔案就可以官掉了。

再來就要找到他要執行的 app-root, 所以打開 app-root.xml.
這裡告訴我們框架的預設頁面是 main-page, 所以我們又要把它關掉再去打開 main-page.
可是等等,名為 main-page 的檔案有三個耶!
沒錯,用 NativeScript 寫的APP主角就是這三種檔案,也是我們要動刀的對象:

  • XML 負責輸出畫面,也就是使用者界面(UI).
  • CSS 負責版面處理,也就是美編工作。
  • JS 則是負責流程控制的 JavaScript 程式碼。

這三種檔案都可以用記事本打開。

首先打開 main-page.xml, 找到 <ActionBar title 這行,
等號右邊的 "My App" 就是我們在手機上看到的畫面標題,把它改成 "我的測試作品".

到這裡先按 CTRL+S 存檔,接著拿起手機看看是不是變了。很神奇吧!
證明有效後接著修改其他部份。

繼續往下找到 <StackLayout 這行,底下有三個組件:

  • <Label text= 這是文字標籤,把內容 "Tap the button" 改為 "點擊下面的按鈕".
  • <Button text= 這是按鈕,把按鈕名稱 "TAP" 改為 "點擊".
    緊接著的 tap="{{ onTap }}" 意思是一旦點擊該按鈕就會呼叫 onTap 這個處理函數,理論上它會被定義在 main-page.js 檔案李。
  • <Label text="{{ message }}" 又是一個文字標籤,內容應該由 main-page.js 定義的 message 函數產生。

存檔並關閉,這時候手機畫面應該都是中文了。

最後修改起始數值和最終的訊息:
打開 main-page.js, 跳過兩段註解說明,接下來看到這行
const createViewModel = require("./main-view-model").createViewModel;
得知控制權又轉到 main-view-model.js 了。

打開找到其中的 function createViewModel() { 裡面就是定義上面兩個函數的地方。
回到檔案開頭搜尋 return, 把 "Hoorraaay! You unlocked the NativeScript clicker achievement!" 改為 "恭喜您完成姐所!".
游標往下兩行還有個 return, 把 ${counter} taps left 改為 還勝 ${counter} 次點擊.
接著搜尋 42, 改為 3.
存檔,大功告成!

讀到這裡您一定感到暈頭轉向,我也是!
別擔心,等整個範例流程都跑完,我們會在重新建立一個專案,
從最基礎做起,代您一步步完成自己的APP開發。
※最後更新時間:2019-12-10 09:56:52 From:123.193.249.19 By:coscell
※最後更新時間:2019-12-10 09:58:35 From:123.193.249.19 By:coscell
※最後更新時間:2019-12-10 09:59:52 From:123.193.249.19 By:coscell
※最後更新時間:2019-12-10 10:06:12 From:123.193.249.19 By:coscell
經過如上的修改,假設已經無可挑剃了,接下來就進行編譯打包,指令是
tns build android

tns build ios

順利完成後訊息最後會告訴您成果檔案的存放位置,
您就設法把它複製到手機或任何手機能抓到的位置,例如雲端硬碟或 E-mail 來進行安裝。
當然如果您願意的話也可以虛榮一下把它分享給好友。
※最後更新時間:2019-12-11 09:43:05 From:123.193.249.19 By:coscell
前面 3~5 節介紹的程序再加上安裝、執行這些動作都可以用一個指令來達成,
掃描 QR code 有困難的人也可以用這個指令替代。
不過執行指令之前手機需要經過設定。
下面的設定步驟只針對 Android 手機,歡迎 iPhone 高手做補充:

  1. 進入系統設定->關於手機。
  2. 請點 Android 版本,如果沒有反應則點您的手機廠商提供的系統版本,按照語音回饋操作。
  3. 在系統設定或更多設定李找到開發者選項進入。
  4. 找到偵錯,底下的三個項目必須勾選:
    • USB偵錯
    • USB安裝
    • USB調適安全設定
  5. 使用USB線連接電腦。

做好準備工作就可以執行
tns run android

tns run ios

請注意手機畫面提示,一定要允許要求才能完成程序,最後會呈現APP的執行畫面。
這時候就可以比照第4節進行必要的修改,滿意後就可以關閉終端機視窗、退出手機APP.
到手機的應用城市列表李也可以找到剛剛安裝的專案。

到這裡整個流程介紹完了,證明視障者確實可以完全獨立完成自己的APP開發。
歡迎把這套方案推薦給更多視障朋友,期待早日看到台灣視障者開發的APP!
前面我們利用官方的範例樣板跑完了整個開發流程。
現在就從基礎開始,一步步做出我們自己的作品,從中學習必要的相關知識。

第一步還是要建立專案,執行以下指令:
tns create hello --template tns-template-hello-world

這裡我們增加了一個指令參數 --template tns-template-hello-world,
這樣可以省略選擇風格和模板的兩個步驟。
完成後我們來瀏覽一下 hello 資料夾結構,作一些小調整。

請進入 app 資料夾。
package.json 指出 app 進入點為 app.js.
app.js 告訴我們要執行 app-root.
app-root.xml 顯示主頁為 main-page.

從前面第4節我們學到 main-page.xml, main-page.js 和 main-view-model.js 這三個檔案是我們可以修改的對象。
因為要從頭開始,所以我們可以把這三個檔案刪除。
此外為了方便重起爐灶,我們可以新建一個資料夾 home.
這樣一來, app-root.xml 就必須作對應的修改:
<Frame defaultPage="main-page">
要改成
<Frame defaultPage="home/home">
意思就是我們要在剛剛新建的 home 資料夾裡自計建立 home.xml 內容可以向這樣:

<Page>
  <StackLayout>
    <Label text="大家好!這是我自己寫的第一個APP, 還請多多指教,也希望快點看到大家的作品喔!"/>
  </StackLayout>
</Page>

簡單解釋一下上面的元件:

  • <Page></Page>: XML 檔案的開頭和結尾
  • <StackLayout></StackLayout>: 這是一種縱向的畫面布局,其中的元件項目會依序由上而下排列
  • <Label/>: 文字標籤,文字內容由 text= 指定

到此就完成了我們的APP雛型,請選用前面第 3, 5, 6 節其中一種方法進行測試。
上面的APP雛型一切順利的話,畫面上會出現 label text 指定的文字內容。
這個用語音聽起來沒問題,但是如果用看的會發現文字不完整,後半部的內容被切掉了。
這是因為手機的螢幕太小,一行無法容納全部的內容。
隨著螢幕尺寸和解析度不同,顯示的字數也會不同。
解決的方法很簡單,只要在 label 元件中加上 textWrap 屬性來控制就會自動換行,像這樣:

<Label textWrap="true" text="大家好!這是我自己寫的第一個APP, 還請多多指教,也希望快點看到大家的作品喔!"/>

修改過後如果無法用視力判斷效果,可以把語音調成逐行閱讀就能聽出來了。
APP必須要與使用者互動,否則就太單調無趣。
最常見的互動界面就是按鈕,下面就是按鈕配置語法,
請打開 home.xml 在 label 下面增加一行:

<Button text="關於我" tap="onTap"/>
  • Button: 這是按鈕元件
  • text: 這裡為按鈕命名
  • tap: 這裡指定點按之後的反應

多了這行,APP主畫面在文字標籤下會多一個『關於我』的按鈕,按了會執行 onTab 這個函數。
還記得這個函數應該會被定義在哪裡嗎?
對了,就是 home.js, 所以我們還需要提供這樣一個 JavaScript 內容如下:

var frameModule = require("ui/frame");

function onTap() {
  frameModule.topmost().navigate("home/about");
}

exports.onTap = onTap;

一開始載入 ui/frame 模組並且命名為 frameModule.
接著定義要被按鈕呼叫的 onTap 函數。
函數內容為執行剛剛載入的模組裡的 topmost().navigate 函數。
該函數的作用是跳轉畫面到 home/about.
最後把定義好的函數會給 onTab.

從上面定義的函數內容所呼叫的 navigate 函數得知我們還需要提供 about.xml 也就是『關於我』的畫面檔案,
內容您自己寫,我的範例如下:

<Page>
  <StackLayout>
    <Label text="我喜歡電腦"/>
    <Label text="我也喜歡音樂"/>
    <Label text="所以我喜歡電腦音樂"/>
  </StackLayout>
</Page>

所以我們的 home 資料夾下現在已經有
home.xml, home.js, about.xml 三個檔案了,測試看看吧。
上面按了按鈕跳轉畫面的時候可以指定呈現不同的轉場動畫效果。
雖然這對我們沒有什麼意義,但是我們也不能不照顧到眼明人的視覺感受,
所以這部份我沒有省略,還是提出來給大家參考。

轉場效果由 onTap 函數控制,我先把可用的效果列出來:

效果 支援平台 說明
curlUp iOS 上捲
curlDown iOS 下捲
explode Android (5 以上) 爆炸
fade Android, iOS 淡入,淡出
flipRight Android, iOS 向右彈出
flipLeft Android, iOS 向左彈出
slideLeft Android, iOS 往左滑出
slideRight Android, iOS 往右滑出
slideTop Android, iOS 往上滑出
slideBottom Android, iOS 往下滑出

改寫 home.js 如下:

var frame = require("ui/frame");
var navigationEntry = {
  moduleName: "home/about",
  transition: {
    name: "slideBottom"
  }
};

function onTap() {
  frame.topmost().navigate(navigationEntry);
}

exports.onTap = onTap;

修改的部份首先定義一個物件 navigationEntry 用來存放要跳轉的畫面 "home/about" 和轉場效果 "slideBottom".
再把先前 navigate 函數要跳轉的對象替換成這個物件。

請注意上面用的轉場效果是 "slideBottom", 當返回的時候就會自動換成 "slideTop".
前面我們學過由於手機螢幕太小,一行文字如果太長可以用 textWrap="true" 讓它自動換行。
那如果您的豐功偉績太多,上面的『關於我』洋洋灑灑超過螢幕可以容納的行數該怎麼辦呢?
您可能習慣性的要用兩只往上推好讀到後面的內容,但是抱歉不能,
除非您有加上 ScrollView 這個標籤,像這樣:

<Page>
  <ScrollView>
    <StackLayout>
      // 您的一堆豐功偉業
    </StackLayout>
  </ScrollView>
</Page>

這樣作會讓整個畫面都能上下捲動,不過有時候我們可能只想讓部份內容可以捲動,
例如一篇文章的標題要固定在畫面上方,只有文章內容可以捲動。
這要怎麼作呢?

請展現舉一反三的能力,勇於實驗的精神自己作看看。
實在做佈出來就在下面留言,我在公佈答案。
文字輸入也是很重要的使用者界面。
NativeScript 提供兩種輸入框元件標籤:

  • <TextField/>: 單行輸入框
  • <TextView/>: 多行輸入框

為了讓 iOS 使用者能清楚辨認文字輸入的位置,上面兩個元件還需要加入 hint 屬性提示,像這樣:

<TextField hint="在此輸入文字"/>

上面的提示文字在使用者輸入第一個字後會自動消失,如果清除文字又會自動顯示出來。

首先參考第9節在 about.xml 最後再加上一個『與我聯絡』的按鈕。
接著當然要寫一個對應的 about.js 來處理這個按鈕,內容就跳轉到聯絡表單 contact.xml.
再來就是要設計這個聯絡表單,我們新學的文字輸入元件標籤就要上場了:

<Page>
  <StackLayout>
    <Label textWrap="true" text="歡迎填寫下面的表單與我聯絡,我會盡快回覆:" />
    <TextField hint="訊息主題" />
    <TextView hint="訊息內容" />
    <Button text="送出" tap="onTap" />
  </StackLayout>
</Page>

最後的重頭戲 contact.js 登場了:

var dialogsModule = require("ui/dialogs");

function onTap() {
  console.log("送出按鈕已被按下");

  dialogsModule.alert("您的訊息已送出");
}
exports.onTap = onTap;

正常情況下,這裡應該要把使用者的訊息透過簡訊或電子郵件發送給我們,但這抄出了我們目前的程度,
所以現在只能暫時先用簡單的程序處理:

之前我們跳轉畫面用的是 frame 模組,這次我們用到的是 dialogs 模組。
在 onTap 函數裡我們作兩件事:

console.log 是內建函數,用來顯示訊息在終端機視窗。
當您使用 tns preview 或 tns run 來測試程式,按下『送出』按鈕就會在終端機視窗上看到『送出按鈕已被按下』的訊息,
開發者往往也用這種方式來找出程式碼的錯誤所在。

dialogsModule.alert 函數的作用則是在使用者的畫面上跳出一個訊息框。
到目前為止,我們自己定義的函數都是被動的由使用者按鈕來執行。
其實我們也可以讓APP一打開就主動做一些事,例如把 home.xml 的第一行改成這樣:

<Page loaded="onLoaded">

這樣一旦 home.xml 被載入就會先執行 onLoaded 函數。
同樣當然我們需要在 home.js 增加一個 onLoaded 函數定義。
我練習的範例如下:

var dialogs = require("ui/dialogs");
function onLoaded() {
  dialogs.alert("感謝選購這個APP!");
}
exports.onLoaded = onLoaded;

把這個技巧適當弟套用到需要的畫面上會讓您的APP更豐富有彈性。

最後補充說明:
無論上面的 loaded= 或前面的 tap= 後面的函數名稱都是自己定義的,所以可以自己取,只要與 exports 的名稱相符即可。
之前一直用 Stack Layout 已經有讀者不耐煩了,現在就來介紹 Grid Layout 格狀排列。
NativeScript 總共提供5種畫面不覺模式,其中 Stack 和 Grid 使用情況超過 95%,
所以其他3種 Absolute, Dock 和 Wrap 為了加快進度就暫時不講,
等將來有機會在說,或著您自己參考官方說明.

Grid Layout 的定義格式如下:

<GridLayout rows="*,*,*" columns="*,*" width="300" height="450">
  ... //填充內容 
</GridLayout>

屬性說明如下:

  • rows: 定義行數(縱向)
  • columns: 定義列數(橫向)
  • width: 畫面寬度
  • height: 畫面高度

這是最普通的格式,每個格子大小相同。
另外還有兩種更複雜細緻的定義格式,可以分別定義每個格子的大小,這裡也先暫時掠過。

上面範例在寬 300, 高 450 點(像素)的畫面上分隔出直3橫2總共6個格子,
每個格子都一樣長寬都是 150 點。
這6個格子的座標分別是 (0,0), (0,1), (1,0), (1,1), (2,0), (2,1).
下面我們舊來時做這個畫面。

請在 home.xml 新增一個『我的其他作品』按鈕,作用是跳轉到 product 畫面。
product.xml 內容如下:

<Page>
  <GridLayout rows="*,*,*" columns="*,*" width="300" height="450">
    <Button row="0" col="0" text="猜數字"/>
    <Button row="0" col="1" text="計算機"/>
    <Button row="1" col="0" text="圈差遊戲"/>
    <Button row="1" col="1" text="戳戳樂"/> 
    <Button row="2" col="0" text="點點愛"/>
    <Button row="2" col="1" text="明盤欣賞"/>
  </GridLayout>
</Page>

GridLayout 如果要針對每一格套用兩個以上的元件標籤就必須套疊 StackLayout, 像這樣:

<GridLayout rows="*,*,*" columns="*,*" width="300" height="450">
  <StackLayout row="0" col="0" style="background-color: #CCCCCC">
    <Label text="針對這一格加上背景顏色和圖片" textWrap="true" />
    <Image src="MyPicture" width="75" height="75" />
  </StackLayout>
</GridLayout>

走到這裡應該要講講 CSS 美編部份了,可惜這不是我的專長再加上對我也確實有視力的限制,所以打算暫時跳過。
還好這種資料網路上並不難找,有需要就去找找吧。
不然就要等我公立進步了再回頭來講。

使用者界面部份當然不只這些內容,但是基於這是入門課程,
為了帶領大家早日登堂入室,暫時就此打住。
接下來就要往技術核心部份邁進,不怕苦的就繼續跟我走吧!
※最後更新時間:2019-12-29 11:57:15 From:123.193.249.19 By:coscell
特種兵在上一篇留言裡提到能不能只用 JavaScript 來寫APP兒擺脫XML的限制?
答案是可以的,但是畫面產生很麻煩,必須評估是否划算。
我找到一個簡單的畫面範例如下:

var frame = require("ui/frame");
var Page = require("ui/page").Page;
var StackLayout = require("ui/layouts/stack-layout").StackLayout;
var Label = require("ui/label").Label;
var Button = require("ui/button").Button;

exports.createPage = function() {
  var page = new Page();
  var layout = new StackLayout();
  var welcomeLabel = new Label();
  var backButton = new Button();

  page.actionBar.title = "Settings";
  welcomeLabel.text = "You are now in Settings!";
  welcomeLabel.cssClass = "message";

  backButton.text = "Go Back";
  backButton.on("tap", function () {
    frame.topmost().goBack();
  });

  layout.addChild(welcomeLabel);
  layout.addChild(backButton);

  page.content = layout;
  return page;
};

以上算算總共22行,換成XML就這樣:

<Page>
  <actionBar title="Settings"></actionBar>
  <StackLayout>
    <Label text="You are now in Settings!" cssClass="message"/>
    <Button text="Go Back" tap="onTap"/>
  </StackLayout>
</Page>

您會選哪個?


來源文章


最後更新:2020-01-03 19:52:27

From: 211.23.21.202

By: 特種兵