そーす

福岡在住のプログラマ

Fuse インストールからAndroid実機リアルタイムプレビューまで

f:id:saburesan:20160216030851p:plain

最近知ったワンソースでiOS,Androidのアプリが作れるツールFuse

なんとSketchから直接インポートでアプリが出来るというかなり良さ気なツールです!!

Sketchの変更をリアルタイムで実機プレビューが出来るらしい。。。素晴らしい。

ってことで導入

インストール

Download Fuse

f:id:saburesan:20160216031227p:plain

メールアドレス入力して「I accept the license agreement for Fuse.」にチェックで、ダウンロードできます。 パッケージインストーラからインストールできます。簡単。

起動

アプリケーションフォルダの.appから起動

f:id:saburesan:20160216031731p:plain

ターミナルから起動

パスは通ってるはずです。

fuse

help

% fuse help
Usage: fuse <CommandName> [<Args>] [--version]
COMMANDS
    build            Build a project for a given target
    create           Create a project or file from a template
    daemon           Start the fuse daemon
    daemon-client    Create a connection to a daemon.
    help             Shows help for the specified command
    import           Import a file to your fuse project
    inspector        Open the inspector application
    install          Install an external component
    kill-all         Kill all Fuse processes (even the daemon)
    monitor          Monitor run-time events
    preview          Preview an app
    tutorial         Go to tutorials and guides
DESCRIPTION
Fuse consists of a set of tools that simplifies app development for a range of platforms and devices.

基本的にはCUIで進めます。

新規プロジェクト作成

% fuse create app sample
Created project: 'sample' at '/Users/Sabure/sample'

作成後はこんな構成

% ls
MainView.ux     sample.unoproj

MainView.ux

<App Theme="Basic">
</App>

sample.unoproj

{
  "RootNamespace":"",
  "Packages": [
        "Fuse.Animations",
        "Fuse.BasicTheme",
        "Fuse.Themes",
        "Fuse.Controls",
        "Fuse.Designer",
        "Fuse.Drawing",
        "Fuse.Drawing.Primitives",
        "Fuse.Effects",
        "Fuse.Elements",
        "Fuse.Entities",
        "Fuse.Gestures",
        "Fuse.Navigation",
        "Fuse.Shapes",
        "Fuse.Triggers",
        "Fuse.Reactive",
        "Fuse.Android",
        "Fuse.Desktop",
        "Fuse.iOS",
        "Fuse.UserEvents",
        "FuseCore",
        "Uno.Collections",
        "Uno.Geometry"
  ],
  "Includes": [
    "*"
  ]
}

名前からして.uxはUI定義、.unoprojはプロジェクト設定っぽい。

プレビュー起動

% fuse preview
Build started: FullCompile
Configuring
(2,819.20 ms)

Parsing source code
(377.53 ms)

Compiling syntax tree
(1,703.43 ms)

Generating code and data
(2,458.72 ms)

Build completed in 7.37 seconds.
    0 Warning(s)
    0 Error(s)
Build ended
GL_VERSION: 2.1 INTEL-10.12.13
GL_VENDOR: Intel Inc.
GL_RENDERER: Intel(R) Iris(TM) Graphics 6100

f:id:saburesan:20160216034249p:plain

おーー。 ちなみに、プレビューはデフォルトiPhone6で他にもあるっぽい。カスタマイズもできる。優秀。 f:id:saburesan:20160216034525p:plain

試しにテキストを追加してみたのですが…

<App Theme="Basic">
  <Text>Hello world!</Text>
</App>

f:id:saburesan:20160216035817p:plain

プレビューと立ち上げた状態だと、保存直後にリアルタイムプレビューが行われました。優秀!

UIを定義してみる

Panel

一番ベーシックなUIコンポーネントらしい

<App Theme="Basic">
  <Panel MaxHeight="400" Background="#EEE" Alignment="Bottom">
      <Text Padding="40">This...</Text>
      <Text Margin="100">...will be on top of this</Text>
      <Rectangle Alignment="BottomLeft" Height="20" Width="20" Fill="#678" />
  </Panel>
</App>

Panelコンポーネントはデフォルトで親と同じ大きさに作れる。 また、子コンポーネントを持つことが出来る。 AndroidでいうとRelativeLayoutのようなもの。

f:id:saburesan:20160216042719p:plain

Text

<App Theme="Basic">
  <Text TextColor="#555">Hello world!</Text>
</App>

リスト表示

StackPanelのデフォルトOrientationは縦

<App Theme="Basic">
  <StackPanel>
    <Text TextColor="#555">Hello world!</Text>
    <Text TextColor="#555">Hello world!</Text>
    <Text TextColor="#555">Hello world!</Text>
    <Text TextColor="#555">Hello world!</Text>
    <Text TextColor="#555">Hello world!</Text>
  </StackPanel>
</App>

f:id:saburesan:20160216041111p:plain

<App Theme="Basic">
  <StackPanel Orientation="Horizontal">
    <Text TextColor="#555">Hello world!</Text>
    <Text TextColor="#555">Hello world!</Text>
  </StackPanel>
</App>

f:id:saburesan:20160216041144p:plain

グリッド

<App Theme="Basic">
    <Grid RowCount="2" ColumnCount="3">
      <Rectangle Fill="Red"/>
      <Rectangle Fill="Blue"/>
      <Rectangle Fill="Green"/>
      <Rectangle Fill="Black"/>
    </Grid>
</App>

リファレンスのサンプルでは

<Rectangle Row="0" Column="1" Fill="Red"/>

みたいにrowとcolumnを指定するようになってましたが、デフォルトでは左上詰めで配置がされるようです。

f:id:saburesan:20160216041431p:plain

ちなみにGridはデフォルトColumn1でRowはエレメント自動で合わせられるようです。 また、GridのサイズはエレメントのRow,Colmnの最大に合わせて分割をしてくれるようです

<App Theme="Basic">
    <Grid>
      <Rectangle Fill="Red"/>
      <Rectangle Fill="Blue"/>
      <Rectangle Fill="Green"/>
      <Rectangle Row="4" Column="5" Fill="Black"/>
    </Grid>
</App>

f:id:saburesan:20160216042006p:plain

ScrollView

かなりの頻度で使うであろうScrollView

<App Theme="Basic">
  <ScrollView AllowedScrollDirections="Vertical">
    <StackPanel>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
      <Text Padding="16" Background="#EEE">Item</Text>
      <Text Padding="16">Item</Text>
    </StackPanel>
  </ScrollView>
</App>

f:id:saburesan:20160216043715p:plain

Button

<App Theme="Basic">
  <JavaScript>
        module.exports = {
            buttonClick: function (args) { console.log("Button was clicked"); }
        }
    </JavaScript>
  <ScrollView AllowedScrollDirections="Vertical">
    <StackPanel>
      <Button Text="Basic"></Button>
      <Button Text="Set Clicked Action" ux:Name="button1">
        <Clicked>
            <Set button1.Text="Clicked!" />
            <DebugAction Message="Clicked!" />
        </Clicked>
      </Button>
      <Button Text="Set Clicked Action using JS" Clicked="{buttonClick}"></Button>
      <Rectangle Fill="#F15548" Height="50" Margin="8" CornerRadius="6" ux:Name="custom">
        <Pressed>
            <DebugAction Message="Rectangle got clicked" />
            <Set custom.Height="100"/>
        </Pressed>
        <Released>
            <DebugAction Message="Rectangle got clicked" />
            <Set custom.Height="50"/>
        </Released>
      </Rectangle>
    </StackPanel>
  </ScrollView>
</App>

f:id:saburesan:20160216051119p:plain

デザインモード

プレビューをアクティブにして

⌘+m, ⌘+i

ショートカットキーを押すと

f:id:saburesan:20160216051743p:plain

こんな感じのWindowが開きます。 これはデザイモードといって、GUIでリアルタイムにViewが変更できかつ、それがView側のコードにも反映されるというすぐれものです。 試しに色を高さを変更して、影を付けてみました。コードも変更されていました。

f:id:saburesan:20160216052101p:plain

Andoridアプリとして書き出してみる

ネイティブアプリに書き出すコマンドは

fuse build --target=<Android or iOS> --run

なので、Androidに書き出してみます。

% fuse build --target=Android --run
Uno 0.20.1-OSX (build 902)

Configuring
(2,036.27 ms)

Parsing source code
(4,055.79 ms)

Compiling syntax tree
(12,192.34 ms)

Installing dependencies
download http://az664292.vo.msecnd.net/files/GrBRdlh33GhogMMq-Xli-0.8.517-Android.zip
extract /usr/local/share/uno/Packages/UnoCore/0.20.1/Targets/CPlusPlus/Prebuilt/Android
download http://az664292.vo.msecnd.net/files/MnOVi7Npwn4mb2DD-V8-Android.zip
extract /usr/local/share/uno/Packages/Fuse.Scripting.V8/0.19.3/lib/V8-Android
(9,629.33 ms)

Generating code and data
(22,934.95 ms)

Building native target
ERROR: 'ant' was not found.
(unknown): E0000: Native build failed
(22.48 ms)

Build completed in 50.88 seconds.
    0 Warning(s)
    1 Error(s)

Error Summary
-------------

(unknown): E0000: Native build failed
FATAL ERROR: Build failed.
fuse: Errors were encountered while building the project

エラー出ました。 antが無いと。 リファレンス見ると

fuse install android

で必要なパッケージインストールせよと。 インストール後に再度。

% fuse build --target=Android --run
Uno 0.20.1-OSX (build 902)

Configuring
(2,819.03 ms)

Parsing source code
(4,760.95 ms)

Compiling syntax tree
(13,547.95 ms)

Generating code and data
(21,713.16 ms)

Building native target
1/2: libsample.so
2/2: sample-debug.apk
(78,461.75 ms)

Build completed in 121.31 seconds.
    0 Warning(s)
    0 Error(s)

sh .build/Android-Debug/run.sh
Detecting Android device
adb I  8991 99636 usb_osx.cpp:259] Found vid=18d1 pid=4ee7 serial=0ca8c0ec54203433
adb I  8991 99636 usb_osx.cpp:259]
Success

Trying to uninstall existing version of APK
Failure [DELETE_FAILED_INTERNAL_ERROR]

Installing new version of APK
6151 KB/s (6876121 bytes in 1.091s)
    pkg: /data/local/tmp/sample-debug.apk
Success

Starting APK on device
Starting: Intent { act=android.intent.action.MAIN cat=[d] flg=0x10200000 cmp=com.sample/.sample }
---

お、実機繋いでたのでそのままインストールされました

f:id:saburesan:20160216054139j:plain

ナビゲーションバー被ってる…。 まぁいいや、これは次回直そう。

実機でリアルタイムプレビュー

さて、実機インストールも確認できたので最後にリアルタイムプレビューやりましょう。

% fuse preview --target=Android
Build started: FullCompile
Configuring
(2,444.99 ms)

Parsing source code
(4,014.21 ms)

Compiling syntax tree
(16,147.00 ms)

Generating code and data
(26,397.24 ms)

Building native target
1/2: libsample.so
2/2: sample-debug.apk
(145,061.25 ms)

Build completed in 194.07 seconds.
    0 Warning(s)
    0 Error(s)
Build ended
sh .build/Simulator/Android/run.sh
Detecting Android device
Success

Trying to uninstall existing version of APK
Success

Installing new version of APK
5476 KB/s (8616819 bytes in 1.536s)
    pkg: /data/local/tmp/sample-debug.apk
Success

Starting APK on device
Starting: Intent { act=android.intent.action.MAIN cat=[d] flg=0x10200000 cmp=com.sample/.sample }
---

youtu.be

これはエミュレータではなく実機をキャプチャしています。 はー、すげー。

感想

凄いですね。 Fuse思った以上に使えそうです。AndroidiOSもネイティブで書けなくはないのですが、Sketchからインポートできてワンソースで両対応できるならこれはかなり強力ですね。 私もデザインではSketchを使っているのでネイティブ実装が必須でない開発では時間がかなり短縮できそうです。 次回はSketchからのインポートとかロジック部分について調べたいですね。