はじめに
Curlでモバイルアプリを開発できる環境「Caede Preview版」は、スマートフォンとPCのアプリケーションを同じ開発環境/開発言語で作れることがコンセプトで、開発者はCurl言語を習得するだけで両プラットフォーム対応のアプリを開発できます。例えば、エンタープライズの現場において、バックエンドシステムを主体としながらも一部のサブモジュールをモバイルにて運用したい、というようなシーンにはぴったりです。
もともと、CurlはWindowsだけでなく、MacやLinux上でも稼動する言語です。スマートフォンやタブレットなどのデバイス環境が多様化する昨今、モバイル環境にも従来のPC環境にも対応しているCurlは、とても魅力的な言語です。
また、Curlは、エンタープライズRIAに取り組んでいる言語であるため、バックエンドシステムをCurlで構築している事例が数多くあります。そのため、安心して使える、安定した言語でもあります。安心・安定した言語であることは、企業システムでは必須の事項です。詳細はCurlのサイトをご覧ください。
モバイル用統合開発環境「Caede」の仕組み
Caedeとは、Curlで作られたモバイル対応の統合開発環境で、Translator、Framework、Librariesおよび開発ツールなどを提供しています。「Eclipse上でCurlのソースコードを書き、Translatorでソースコードを変換し、結果をシミュレータで確認する」というのが基本的な開発スタイルです。Translatorを使い、モバイルプラットフォームをサポートするためのHTML/CSS、JavaScriptを生成します。詳しくは、Caedeのサイトを参照ください。
作成するアプリケーション
今回はCaedeを使い、棚卸し時に使える簡単なアプリを作ってみたいと思います。
商品一覧をクリックすると個別の画面が起動し、個別の画面にて、実在庫の数を入力します。必要に応じて発注依頼を実施したり、商品情報を参照したりできるアプリです。今回は記事の都合上、発注依頼や商品情報の参照機能は省略します。
インストール
実装するにあたり、まずは開発環境を整えます。下記の製品をインストールしてください。
アプリの実装手順
今回作成するアプリケーションの実装手順を説明します。手順は以下の通りです。
- Eclipse上でAndroidプロジェクトを作成・設定
- Eclipse上でCurlプロジェクトを作成・設定
- 作成したCurlプロジェクト上で画面プログラムを作成
- Translatorを用いてデプロイ(ソースコードの変換、Androidプロジェクトへの配置)
- Androidプロジェクト上のシミュレータを用いて実行
Caede Getting Startedも併せてご覧ください。
手順1)Eclipse上でAndroidプロジェクト作成・設定
では、上記の手順に沿って、アプリの作成方法を説明していきます。まず、Eclipseのファイルメニューから[New]-[Android Project]を選択します。
[Next]ボタン押下後、次画面にて利用するAndroidを選択し、以下のように設定します。
ダウンロードしたcaedeディレクトリのlib/android配下から、resディレクトリ、libsディレクトリ、AndroidManifest.xmlファイルを作成したAndroidプロジェクトのルートにコピー(上書き)します。最後に、Androidプロジェクトを右クリックして[Properties]-[Java Build Path]を選択し、「libs/caede-android.jar」ファイルを設定すれば、完了です。
手順2)Eclipse上でCurlプロジェクトを作成・設定
続いて、モバイル用プログラムを実装する上で必要な、Curlプロジェクトの作成・設定について説明します。ファイルメニューから[New]-[Curlプロジェクト]を選択します。
次のようにプログラミングしていきます。今回作成するファイルの構成は、下図の赤枠内のようになります。
Caedeフレームワークでは、画面クラス(Graphicクラス)と画面コントロールクラス(Screenクラス)を各画面ごとに作成します。画面クラスはUI部分で、画面コントロールクラスはロジック部分です。ここでは、画面Aとそこからの遷移先画面Bの画面クラスと画面コントロールクラスをそれぞれ別ファイルとして作成し、パッケージファイル(load.scurl)に集約します(パッケージファイルにすべてのクラスを直接記述しても問題ありません)。
MYPACKAGEファイル構成
MYPACKAGEに含まれるファイル構成について、詳しく見て行きましょう(※第1画面の「Ag.scurl」「As.scurl」、第2画面の「Bg.scurl」「Bs.scurl」については、次ページにて解説します)。
load.scurl
後述する各画面を定義する際に必要なファイル郡(Ag.scurl、As.scurl、Bg.scurl、Bs.scurl)を集約し、パッケージファイルとして定義するためのファイルです。
-
{package MYPACKAGE}
このファイルがMYPACKAGEというパッケージであることを示します。モバイル用のプログラムであることを示すために、caede-client-applet? = trueを指定します。
-
{import}
他パッケージを参照するには、import文を用います。
-
{include}
他ファイルを読み込みます。
-
{client}
Caedeフレームワーク特有の構文です。作成される画面とその任意の名前を定義します。
start.curl
起動用ファイルです。アプレット起動時に最初に実行されるファイルです。次のようにプログラミングします。
-
{applet manifest = "manifest.mcurl"}
利用するマニフェストファイルを指定します。モバイル用のプログラムであることを示すために、caede-client-applet? = trueを指定します。
-
{import}
他パッケージを参照するには、import文を用います。今回の例では、Caedeフレームワークと、自作したMYPACKAGEを利用します。
-
{startup GamenAScreen}
startupプロシージャにて、最初に起動する画面コントロールクラスを指定します。ここでは、GamenAScreenという画面コントロールクラスを指定しています。
manifest.mcurl
マニフェストファイルです。マニフェストファイルはアプリケーションリソースを管理・共有し、アプリケーションを配置するためのファイルです。以下のように記述されます。このファイルは通常、CDEの機能にて自動で作成される部分です。
-
{delegate-to COM.CURL.CAEDE.FRAMEWORK}
ダウンロードしたcaedeディレクトリにあるパッケージ(COM.CURL.CAEDE.FRAMEWORK)をデリゲートします。通常は、以下の手順を踏むことで自動で生成されます。
- Curlプロジェクトを右クリックし、「Properties」を選択
- Properties for XXX(Curlプロジェクト)ダイアログにて[Curl]-[ライブラリ]を選択
- [外部ライブラリを追加]ボタンをクリックし、ダウンロードしたCaedeディレクトリ配下のlib/common/curl/framework/manifest.mcurlを選択
- [OK]ボタンをクリック
-
{component pakage MYPACKAGE}
自作したパッケージを定義します。この部分もCDEにてパッケージクラスを作成した場合は自動で生成されます。
-
{component file start.curl}
起動用ファイルを定義します。この部分もCurlプロジェクトを作成した際に自動で生成されます。モバイル用のプログラムであることを示すために、caede-client-applet? = trueを指定します。(caede-client-applet? = trueは自動生成されませんので、手動で記述する必要があります。)
手順3)作成したCurlプロジェクト上でアプリ画面を実装
前述の手順2までで、諸設定は完了しました。いよいよ画面を実装していきます。
実装(第1画面)
この画面を作成するためのCurlのソースコードを紹介します。この画面では、CaedeListViewにて3行の商品情報を出力します。各明細をクリックすると、個別の画面が起動する仕組みです。Caedeでは、各画面ごとに画面クラスと画面コントロールクラスを定義する必要があります。
画面クラス
まずは、画面クラスから確認してみましょう。画面クラスのことをGraphicクラスと呼びます。
{define-class public GamenAGraphic {inherits Frame} {constructor public {default} {construct-super {VBox width = 480px, height = 800px, background = "#C6CDD5", {HBox width = 480px, height = 80px, background = "#798EA9", valign = "center", {caede-image {url "./image/title.gif"}, width = 40px, height = 40px}, {Fill width = 5px}, {TextFlowBox color = "white", font-size = 24px, width = 300px,"Inventory control system for mobile" } }, {VBox width = 480px, {Fill height = 10px}, {HBox width = 480px, height = 50px, valign = "center", {caede-image {url "./image/icon1.png"}}, {TextFlowBox width = 200px , color = "white", font-size = 24px, "Item List" } }, {CaedeListView width = 480px, name = "b-list-view", font-size = 24px, icon-map = {new {HashTable-of String, Url}, "1", {url "./image/image001.png"}, "2", {url "./image/image003.png"}, "3", {url "./image/image003.png"} }, {CaedeListViewItemDivider "商品名"}, {CaedeListViewItemData "ハロゲンヒータ(赤)", "名称1" ,height = 80,icon-name = "1"}, {CaedeListViewItemData "ハロゲンヒータ(青)", "名称2" ,height = 80,icon-name = "2"}, {CaedeListViewItemData "ハロゲンヒータ(黄)", "名称3" ,height = 80,icon-name = "3"} } } } } } }
画面クラス「GamenAGraphic」を定義しています。コンストラクタにてレイアウトを定義しています。
HBoxと呼ばれる枠にVBoxがありその中にHBox、CaedeListViewがあります。HBoxとは、水平方向にGraphicのコレクションを配置するためのコンテナで、Curl上でレイアウトを作成する際にはおなじみのオブジェクトです。同様にVBoxは垂直方向に配置するためのものです。Fillはグラフィックディスプレイの空き領域を埋め、パディングを提供します。TextFlowBoxは、テキストをグラフィック階層内に表示するために使用します。今回は"Item List"を表示しています。
また、Curlの標準APIには存在しないオブジェクトとして、下記の2つを使用しています。
- CaedeListView:モバイル用のGUIとして提供されているオブジェクト
- caede-image:モバイル上で画像を表示するためのプロシジャ
続いて、オブジェクトのプロパティ(パラメータ)を見ていきましょう。幅(width)や高さ(height)、背景(background)などは想像がつくと思います。valignとは、HBoxのグラフィカルな子の間の垂直方向の整列方法を指定するためのプロパティです。今回はHBox内にある画像、TextFlowBoxをHBox内で中央に揃えるために"center"を指定しています。Curl言語では、レイアウトをきめ細かく制御するための多数のプロパティが用意されており、モバイルを実装する際も利用することができます。
Curlで利用できるGUIは他にも多数存在し、そのままCaedeでも利用できます(※現在のCaedeはプレビュー版のため、Curlで用意されている全てのGUIが使えるわけではありません)。
画面コントロールクラス
次に、画面コントロールクラスから確認してみましょう。画面コントロールクラスのことをScreenクラスと呼びます。スクリーンクラスは、グラフィッククラスを継承して作成します。
{define-class public GamenAScreen {inherits {Screen-of GamenAGraphic}} field b-list-view:CaedeListView {constructor public {default} ||CaedeListViewのオブジェクト set self.b-list-view = {self.find-graphic-by-name "b-list-view"} asa CaedeListView ||各行にイベントを定義する。 {self.b-list-view.set-action-handler-for-index {on Action do ||詳細画面へ遷移する。その際商品名称をパラメータで渡す。 {self.change-page GamenBScreen, data = "ハロゲンヒータ(赤)"} }, 1 } {self.b-list-view.set-action-handler-for-index {on Action do {self.change-page GamenBScreen, data = "ハロゲンヒータ(青)"} }, 2 } {self.b-list-view.set-action-handler-for-index {on Action do {self.change-page GamenBScreen, data = "ハロゲンヒータ(黄)"} }, 3 } } }
Screenクラスでは、画面を操作するための手続きを定義します。上記例では、CaedeListViewの各行がクリックした際のイベントを定義しています。
{self.b-list-view.set-action-handler-for-index {on Action do ||詳細画面へ遷移する。その際商品名称をパラメータで渡す。 {self.change-page GamenBScreen, data = "ハロゲンヒータ(赤)"} }, 1 }
この定義は、明細行をクリックした際に、詳細画面へ遷移するためのロジックです。画面遷移するには、{change-page}と書くだけで遷移できます。画面遷移時にパラメータを渡すこともできます。
なお、ボタンが押された際のイベントは次のように定義します。
{self.bt-hachu.add-event-handler {on Action do ||TODO:ボタン押下時の処理・・・・ } }
Curlではリッチクライアントならではの多くのイベントハンドラが用意されています。GUIと同様に、Curlで利用できるイベントハンドラは他にも多数存在し、そのままCaedeでも利用できます(※現在のCaedeのリリースでは機能制限があり、Curlで用意されている全てのイベントハンドラが使えるわけではありません)。
実装(第2画面)
詳細画面のソースコードを紹介します。項目が多いためソースコードは長いですが、ロジックは単純です。
画面クラス
では、画面クラスから確認してみましょう。
{import * from COM.CURL.CAEDE.TRANSLATOR.HTML.CLIENT} {define-class public GamenBGraphic {inherits Frame} {constructor public {default} {construct-super {VBox width = 480px, height = 800px, background = "#C6CDD5", {HBox width = 480px, height = 80px, background = "#798EA9", valign = "center", {CommandButton name = "bt-modoru", label = {VBox {Fill}, {HBox valign = "center", {caede-image {url "./image/btn_modoru.gif"}, width = 32px, height = 32px}, "戻る" }, {Fill} }, font-size = 24px, font-size = 24px, width = 100px, height = 50px }, {Fill width = 5px}, {caede-image {url "./image/title.gif"}, width = 40px, height = 40px}, {Fill width = 5px}, {TextFlowBox color = "white", font-size = 24px, width = 300px,"Inventory control system for mobile" } }, {HBox {Fill width = 20px}, {VBox {Fill height = 30px}, {HBox {VBox {HBox valign = "center", {caede-image {url "./image/i_info.gif"}, width = 40px, height = 40px}, {Fill width = 5px}, {TextFlowBox color = "white", font-size = 24px, "Information" } }, {HBox ||商品名称 {TextDisplay border-color= "#C6CDD5", border-style = "none" background = "#C6CDD5", name = "td-sname" , font-size = 20px, width = 260px, height = 30px } } }, {Fill}, {VBox ||商品情報画面遷移ボタン {CommandButton name = "bt-shohin", label = {VBox {Fill}, {HBox valign = "center", {caede-image {url "./image/btn_shohin-004.gif"}, width = 32px, height = 32px }, "商品情報" }, {Fill} }, font-size = 24px, width = 155px, height = 80px } }, {Fill width = 20px} }, {Fill height = 30px}, {HBox width = 460px, valign = "center", {caede-image {url "./image/i_yuko.gif"}, width = 40px, height = 40px}, {Fill width = 5px}, {TextFlowBox color = "white", font-size = 24px, "有効在庫数" } }, {HBox width = 480px, height = 50px, ||有効在庫数 {VBox border-color= "gray", border-style = "flat" background = "#C6CDD5", name = "td-yukosu" , halign = "right", width = 440px, height = 40px, {Fill}, {HBox {TextFlowBox font-size = 40px, height = 40px, "190" }, {Fill width = 20px} }, {Fill} }, {Fill width = 20px} }, {Fill height = 30px}, {HBox width = 460px, valign = "center", {caede-image {url "./image/i_jitsu.gif"}, width = 40px, height = 40px}, {Fill width = 5px}, {TextFlowBox color = "white", font-size = 24px, "実在庫数" }, {Fill} }, {HBox width = 460px, ||実在庫数 {TextField font-size = 20pt, name = "tf-zaikosu" , width = 420px, height = 40px }, {Fill width = 20px} }, {Fill height = 60px}, {HBox width = 480px, valign = "center", {caede-image {url "./image/i_ope.gif"}, width = 40px, height = 40px}, {Fill width = 5px}, {TextFlowBox color = "white", font-size = 24px, "Operation" } }, {HBox background = "#C6CDD5", height = 120px, ||発注依頼画面へ遷移するボタン {CommandButton name = "bt-hachu", label = {VBox {Fill}, {HBox valign = "center", {caede-image {url "./image/btn_hachu.gif"}, width = 32px, height = 32px}, "発注" }, {Fill} }, font-size = 24px, width = 210px, height = 80px }, {Fill}, ||確定ボタン {CommandButton name = "bt-kakutei", label = {VBox {Fill}, {HBox valign = "center", {caede-image {url "./image/btn_kakutei.gif"}, width = 32px, height = 32px }, "確定" }, {Fill} }, font-size = 24px, width = 210px, height = 80px }, {Fill width = 20px} } } } } } } }
先ほど紹介していないオブジェクトとしては、次のようなものを使っています。
- TextDisplay:単一行の編集不可能なテキストエントリボックス
- CommandButton:基本的なプッシュボタンコントロール
これらのオブジェクトでは、Curl言語では良く利用します。今回は、手書きでオブジェクトを並べてレイアウトを作成しましたが、CurlにはVLEと呼ばれるレイアウト作成エディタが存在しており、グラフィカルにレイアウトを作成できます(※現時点のCaedeでは、VLE機能は提供されていません)。
また、このようにレイアウトを定義した場合でも、作成されるソースコードはテキストベースであることもCurlの特徴となります。レイアウトのちょっとした修正をしたい場合、わざわざレイアウト作成エディタを用いなくても、テキストエディタで修正することが可能です。
画面コントロールクラス
続いて、詳細画面の画面コントロールクラスから確認してみましょう。
{define-class public GamenBScreen {inherits {Screen-of GamenBGraphic}} field td-sname:TextDisplay field td-zaikosu:TextDisplay field tf-zaikosu:TextField field td-yuko:TextDisplay field td-yukosu:TextDisplay field bt-kakutei:CommandButton field bt-hachu:CommandButton field bt-modoru:CommandButton {constructor public {default} set self.td-sname = {self.find-graphic-by-name "td-sname"} asa TextDisplay set self.td-zaikosu = {self.find-graphic-by-name "td-zaikosu"} asa TextDisplay set self.tf-zaikosu = {self.find-graphic-by-name "tf-zaikosu"} asa TextField set self.td-yuko = {self.find-graphic-by-name "td-yuko"} asa TextDisplay set self.td-yukosu = {self.find-graphic-by-name "td-yukosu"} asa TextDisplay set self.bt-kakutei = {self.find-graphic-by-name "bt-kakutei"} asa CommandButton set self.bt-hachu = {self.find-graphic-by-name "bt-hachu"} asa CommandButton set self.bt-modoru = {self.find-graphic-by-name "bt-modoru"} asa CommandButton ||戻るボタン押下時の処理 {self.bt-modoru.add-event-handler {on Action do {self.change-page GamenAScreen} ||明細画面へ遷移する } } ||確定ボタン押下時の処理 {self.bt-kakutei.add-event-handler {on Action do {if self.tf-zaikosu.value == "" then {popup-message "入力してください。"} else {popup-message "確定しました。"} {self.change-page GamenAScreen} ||明細画面へ遷移する } } } ||発注依頼ボタン押下時の処理 {self.bt-hachu.add-event-handler {on Action do {popup-message "発注を依頼しました。"} ||TODO:発注指示の処理・・・・ } } } ||画面遷移時の処理 {method public {on-page-changed data:any }:void ||明細画面より渡されたパラメータを設定する。 set self.td-sname.value = data asa String } }
ボタン押下時の処理や、画面遷移時の処理を定義しています。簡易的なメッセージダイアログを出力するには、 {popup-message}プロシージャを用います。このプロシジャもCurlでは御馴染みのAPIです。画面遷移時の処理は、 on-page-changedメソッドをオーバライドして定義します。
||画面遷移時の処理 {method public {on-page-changed data:any }:void ||明細画面より渡されたパラメータを設定する。 set self.td-sname.value = data asa String }
上記例では、遷移元画面で設定したパラメータを受け取り、td-snameオブジェクトに設定しています。
今回の例では、発注ボタン押下時の処理や、商品情報ボタン押下時の処理を定義していませんが、通常はこのクラスにて業務ロジック(画面コントロールロジック)を定義することになります。
手順4)Translatorでソースコードを変換、Androidプロジェクトへ配置
Curlでのコーディングが完了したら、ソースコードを変換します。
ダウンロードしたCaede内の「bin/graphical-deploy-tool.dcurl」を起動します。上記の例では、生成先(Deployment Settings)にAndroidプロジェクトを指定しています。フォルダを設定して[Deploy]を押すと、指定した場所にファイルが生成されます。
手順5)エミュレータを用いた実行
Eclipse上のAndroidプロジェクトに生成されているので、そのまま実行できます。
エミュレータが起動します。HelloCaedeをクリックすると、作成した画面が起動します。
デバイスインタフェース
今回のサンプルでは画面だけの紹介でしたが、ローカルストレージを用いて本格的なアプリを構築することもできます。さらに、ローカルストレージだけでなく、カメラ、GPS、加速度センサーと言ったデバイスインタフェースも利用できます。詳しくはDeveloperCenter デバイスインタフェースを参照ください。
デバイスインタフェースと業務アプリの連携は、知恵を絞ると面白いシステムができるのではないでしょうか。GPSを使った物流管理や、カメラを使った商品管理など、モバイルを上手に利用するアプリが簡単に実装できそうです。
まとめ
初めてモバイルアプリを作成したのですが、こんなに簡単に作成できるとは思いませんでした。一方で、モバイルアプリ特有のデザインと従来の業務アプリのデザインをどこまで融合できるか、が課題だと感じました。
特に、スマートフォンでは表示領域が限られているため、ボタンの押しやすさ、見やすさを考慮して配置するかを研究する必要があります。「業務アプリがスマフォで動くようになりました」というだけのデザインだと、利用者にとって使いにくい可能性もあります。
とはいいつつも、Curlを習得するだけでバックエンドシステムもモバイルアプリも作成できることは、開発者にとってはありがたいことだと思いますし、同じ開発者を流用できることはプロジェクトマネジメントの観点からも有意義と言えるでしょう。新たに言語を習得するには、開発者の情熱と企業側の体力が求められますが、Curlを提供しているSCSKでは、次のような様々な環境を用意しており、学習コストを必要最低限に抑えることができます。
セミナー・トレーニング
Curl WBT(Web Based Training)では、E-learningツールを使用してオンラインでトレーニングを公開しています。時間や場所に制限されず、利用者の都合に合わせてトレーニングを進めることができます。
Curlのコミュニティサイト上にはCaede専用のフォーラムが用意されています。困った際はこちらに投稿でき、Curlを開発しているエンジニアが回答してくれることもあるので、非常に心強いです。
現在、Caedeはプレビュー版の位置づけのため、Curlで用意されているAPIがすべて使えるわけではありません(※執筆時点)。使用できるAPIの一覧は「CurlDeveloperCenter GUIコンポーネント」を確認ください。
Caedeは発展途上なこともあり、いくつかの問題点も発見されています。詳しくは「DeveloperCenter 既知の問題点」をご覧ください。皆さんもぜひCaedeをお試しいただき、要望等をぜひコミュニティサイトにて投稿ください。今後の対応状況についてはCaedeサイト上のロードマップを確認し、これからの機能追加にご期待ください。
「Caede」開発秘話 ~ 開発者が考えるCaedeのコンセプトとは
CodeZineでは、Caede開発に実際に携わった開発者の方々のインタビュー記事も公開されています。開発者の生の声を聞き、ぜひCaedeのコンセプトを感じてください!