愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

Event handling with anon-func style on iOS / Android

JavaScriptでユーザからのイベントを受け取ってホゲホゲ、という処理は大体次のようなスタイルで書く。なお、jQueryとか使っていても概要は同じ。

    el.addEventListener("eventName", function(evt) {
        // do something
    }, false);

iOS appでこのようなイベントハンドーリング的処理を行う場合、基本的にはdelegateを使う。

    -(id)init
    {
        self = [super init];
        if ( self != nil ) {
            Foo* foo = [[Foo alloc] init];
            foo.delegate = self;

            Bar* bar = [[Bar alloc] initWithsomething:nil target:self selector:@selector(didReceiveBarsEvent)];
        }
    }

    -(void)didReceiveFoosEvent
    {
        // do something
    }

    -(void)didReceiveBarsEvent
    {
        // do something
    }

Android appでこのような処理を行う場合、基本的にはなんとかEventListenerを使う。

    view.setOnClickListener(new View.OnClickListener() {
        @override
        public void onClick(View view) {
        }
    });


これらでもまあ悪くはないのだが、

  • iOSのは、イベントハンドラ自体の記述とそれをsetしている部分が分離するので可読性が下がる(場合もある)
  • Androidのはかったるい

という問題があるので、それぞれについてJavaScriptのようにさらっと書くための方法をしるす。ちなみに、一番上で示したようなスタイルで書くのが常に便利だ・よい、といっているわけではない。

iOS

iOS4.0以降ならBlock(Objective-Cにおける、いわゆる無名関数)が使える。が、iOS標準のUIKitにはBlockを受け取ってdelegateに実装されたメソッドとみなす、というようなインタフェースがないので(多分)、BlocksKitを使う。

そうすると、それっぽい記述が出来るようになる。実はまだ試してない。

Android

Javaをやめて、Scalaを使う。基本的にネイティブアプリを別言語で書く系のソリューションは地雷が多いので利用には慎重になるべきですが、Scala on Androidはこれ↓

が大分レールをしいてくれるので、あまり悲しいことにはなりません。


で、その上で以下のようなImplicit Conversionを書いてよみこむ。ちなみにこれは僕が考えついたのではなく、パクリです。

object ViewConversions {
    implicit def toClickListener(bt: Button) : ButtonViewConversions = {
        new ButtonViewConversions(bt)
    }
}

class ButtonViewConversions(bt: Button) {
    type F = (View) => Unit

    def onClick(f: F): Unit = {
        bt.setOnClickListener(
            new View.OnClickListener() {
                override def onClick(v: View) = {
                    f(v)
                }
            }
        )
    }
}

と、このように書けるようになる

    val button = findView(TR.button1)
    button.onClick {
       v => // do something
    }

まとめ

このような方法は存在しますが、イベントハンドラが長くなる場合はちゃんと名前のある関数にするなどした方が無難でしょう。