NativeScriptで、タッチした場所に●(丸)を描画するプログラムを書いてみます。
AbsoluteLayoutなどを使っても出来るとは思いますが、今回あえてNativeScript標準ではサポートされていないAndroidのSurfaceViewを使って実装してみました。Androidでのみ動きます。
NativeScriptからネイティブのAndroid APIを呼ぶサンプルとしても参考にしてもらえれば。
参考)
https://docs.nativescript.org/runtimes/android/generator/extend-class-interface
環境
NativeScript 3.1 + Angular 4
Android 7.1で動作確認
完成画面
事前準備
tns-platform-declarations というTypeScriptの型定義ファイルを入れておきましょう。
IDE上でAndroidのAPIの補完やチェックが効くようになって開発が捗ります。
npm i tns-platform-declarations --save-dev
HTML
まず、HTMLには <Placeholder>
という部品を配置します。
<Placeholder (creatingView)="creatingView($event)">
creatingViewイベントに、ビューを作成するイベントハンドラをバインドします。
TypeScript
コンポーネントのTypeScriptを見ていきましょう。
まず、SurfaceViewを継承したMySurfaceView クラスを実装していきます。
/**
* カスタムSurfaceView
*/
class MySurfaceView extends android.view.SurfaceView {
constructor(context: android.content.Context) {
super(context);
// to transform the Android native class to a JavaScript object
return global.__native(this);
}
コンストラクタで return global.__native(this);
しているのがポイント。
new したときの戻り値として、JavaScriptのオブジェクトではなく、ネイティブのJavaのオブジェクトが得られるようになります。
init(){
var self = this;
let holder = this.getHolder();
holder.addCallback( new android.view.SurfaceHolder.Callback({
surfaceCreated(holder: android.view.SurfaceHolder){
self.drawCircle(100, 100);
console.log('surface Created');
},
surfaceChanged(){},
surfaceDestroyed(holder: android.view.SurfaceHolder){}
}) );
this.setOnTouchListener( new android.view.View.OnTouchListener({
onTouch(v: android.view.View, ev: android.view.MotionEvent){
if( ev.getAction() == android.view.MotionEvent.ACTION_DOWN ){
self.drawCircle(ev.getX() , ev.getY() );
}
return true;
}
} ) );
console.log('init');
}
次に、コンストラクタで初期化後に呼ばれる init メソッドを実装します。
ここでは、SurfaceHolderのコールバックと、タッチイベントを取得する
OnTouchListenerをバインドしています。
SurfaceHolder.Callbackは、インターフェースですが、new することで
匿名関数として即インスタンス化しています。便利ですね!
surfaceCreated、surfaceChanged、surfaceDestroyedは
実装しなければいけないイベントメソッド達です。
以下のドキュメント、シンプルでわかりやすかったです。
http://qiita.com/croquette0212/items/24dc2b6de3730e831aab
drawCircle(x: number, y: number){
let holder = this.getHolder();
let c = holder.lockCanvas();
c.drawColor( new Color("#000000").android );
let p = new android.graphics.Paint();
p.setStyle( android.graphics.Paint.Style.FILL );
p.setColor( new Color("#FF0000").android );
c.drawCircle(x, y, 50, p);
holder.unlockCanvasAndPost(c);
}
}
drawCircleメソッドは、引数でもらった座標に、赤の円を書く描画メソッドになります。
以下のページを参考にしました。
http://donsuka-kk.hatenablog.com/entry/20121114/1352870204
これでMySurfaceViewの準備ができました。
ではいよいよ、画面にSurfaceViewを紐付けます。
export class Component {
creatingView(args: any) {
args.view = new MySurfaceView(args.context);
console.log('creatingView done');
}
}
MySurfaceViewのインスタンスを args.view へ紐付ければ完了です。
ビルドして動かしてみましょう!
タッチすると、タッチしたところに赤い丸が描画されます。
所感: NativeScriptすごい
SurfaceViewのプログラミングは初めてでしたが、NativeScriptでSurfaceViewへの描画、タッチイベントをハンドリングする実装まで出来ました。最も時間がかかったのは、NativeScriptからJavaのSurfaceViewクラスをExtendして、TypeScriptへのブリッジインターフェースを実装する箇所で、
これは今回かなり勘所がわかったので、次からは悩まなそうです。
SurfaceViewの操作自体は巷のAndroid向け開発資料をいくつか見て、ほぼそのままTypeScriptでサクッと実装できたのが感動的でした。
Javaで書かなくてはいけないはずのAndroid APIに、TypeScriptからプロパティアクセスしたり、メソッド呼出したり、クラス継承したり出来るのは本当に楽チン。感動しました。