LoginSignup
2
4

More than 5 years have passed since last update.

NativeScriptでサクッとAndroidのSurfaceViewを実装する方法

Last updated at Posted at 2017-07-04

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で動作確認

完成画面

kei_o___STANDING_OVATION_Slack.jpg

事前準備

tns-platform-declarations というTypeScriptの型定義ファイルを入れておきましょう。
IDE上でAndroidのAPIの補完やチェックが効くようになって開発が捗ります。

npm i tns-platform-declarations --save-dev

HTML

まず、HTMLには <Placeholder> という部品を配置します。

component.html
<Placeholder (creatingView)="creatingView($event)">

creatingViewイベントに、ビューを作成するイベントハンドラをバインドします。

TypeScript

コンポーネントのTypeScriptを見ていきましょう。

まず、SurfaceViewを継承したMySurfaceView クラスを実装していきます。

component.ts
/**
 * カスタム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のオブジェクトが得られるようになります。

component.ts
  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

component.ts
  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を紐付けます。

component.ts
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からプロパティアクセスしたり、メソッド呼出したり、クラス継承したり出来るのは本当に楽チン。感動しました。

2
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4