GroovyとRxJavaとRxAndroid、シンプルなサンプルを作ってみた

  • 67
    Like
  • 0
    Comment
More than 1 year has passed since last update.

はじめに

 @rejuspotaroさんの関数型言語を学ぶことは実務でどう役に立ったかを見てRxJavaを使ってみたくなりました。また、iOSのSwiftとAndroidのGroovyを見て、RxJavaをAndroidで使うなら、Groovyでだなと思いました。

 また、上記の記事では、言及されていませんでしたがRxAndroidというものがあるようです。Viewのクリックのストリームや、EditTextの変更のストリームを生成するメソッドが用意されているみたいです。

 今まで私は、RxJavaをちょっと試してみるならGroovy(これこれ)とか、RxGroovyについて投稿していましたが、今回やっとRxAndroid、RxJavaをGroovyで使ってみたいと思います。

ボタンクリック回数を表示

click_count.png

 ボタンクリックの回数をTextViewに表示したいと思います。(ええ、分かっていますよ。Rx使う必要ないシンプルすぎる例ですね。)

ボタンクリック回数を表示用のレイアウト
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view"
        android:layout_centerInParent="true"
        android:text="0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button"
        android:layout_centerHorizontal="true"
        android:text="click"
        android:layout_alignParentBottom="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

 rx.android.observables.ViewObservableクラスのclicksメソッドを使います。引数に渡したViewのクリックストリームを作ります。(@rejuspotaroさんの記事で紹介していたサンプルでは、このViewObservable#clicks該当するメソッドを@rejuspotaroさんが自作されていました。)

 Javaでまず使ってみます。(Retrolambdaは使っていないです。)

ボタンクリック回数を表示(java)
package com.mrstar.rx_anroid;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import rx.android.events.OnClickEvent;
import rx.android.observables.ViewObservable;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.functions.Func2;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewObservable.clicks(findViewById(R.id.button))
            .map(new Func1<OnClickEvent, Integer>() {
                @Override
                public Integer call(OnClickEvent clickEvent) {
                    return 1;
                }
            })
            .scan(new Func2<Integer, Integer, Integer>() {
                @Override
                public Integer call(Integer acc, Integer current) {
                    return acc + current;
                }
            })
            .subscribe(new Action1<Integer>() {
                @Override
                public void call(Integer clickCount) {
                    TextView textView = (TextView)findViewById(R.id.text_view);
                    textView.setText(clickCount.toString());
                }
            });
    }
}

 インターフェースを実装した匿名クラスのインスタンスを作るところが、かなり冗長ですね。(Retrolambdaを使えばいいのでしょうが。)

 次にGroovyで書いてみます。

ボタンクリック回数を表示(groovy)
package com.mrstar.rx_anroid

import android.app.Activity
import android.os.Bundle
import rx.android.observables.ViewObservable

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewObservable.clicks(findViewById(R.id.button))
            .map { 1 }
            .scan { acc, current -> acc + current }
            .subscribe {
                findViewById(R.id.text_view).setText(it.toString())
            }
    }
}

 すっきり!

EditText内の文字数を入力するたび更新し表示

edit_text_length.png

 EditText内に入力した文字の文字数を、TextViewに表示しようと思います。EditText内の文字列が更新されたら、TextViewの文字数も更新されます。(はい、これも別にRx使う必要は無いくらいシンプルですね。)

テキスト文字数表示用のレイアウト
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view"
        android:layout_centerInParent="true"
        android:text="0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/edit_text"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>

 今度はrx.android.observables.ViewObservableクラスのtextメソッドを使います。これの返り値は、Observable<String>ではなくて、Observable<OnTextChangeEvent>です。

テキストの文字数を表示(Java)
package com.mrstar.rx_anroid;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import rx.android.events.OnTextChangeEvent;
import rx.android.observables.ViewObservable;
import rx.functions.Action1;
import rx.functions.Func1;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewObservable.text((TextView)findViewById(R.id.edit_text))
            .map(new Func1<OnTextChangeEvent, Integer>() {
                @Override
                public Integer call(OnTextChangeEvent onTextChangeEvent) {
                    return onTextChangeEvent.text.length();
                }
            })
            .subscribe(new Action1<Integer>() {
                @Override
                public void call(Integer textLength) {
                    TextView textView = (TextView)findViewById(R.id.text_view);
                    textView.setText(textLength.toString());
                }
            });
    }
}

 こちらもGroovyで書き換えます。

package com.mrstar.rx_anroid

import android.app.Activity
import android.os.Bundle

import rx.android.observables.ViewObservable

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewObservable.text(findViewById(R.id.edit_text))
            .map{ it.text.length() }
            .subscribe{
                    findViewById(R.id.text_view).setText(it.toString())
            }
    }
}

 これまた、すっきり。

まとめ

 非常に簡単な例ですが、RxJava・RxAndroidそしてGroovyを使ってみました。

 もっと複雑なコードにもチャレンジしてみたいです。

良かったら、これもどうぞ!

app/build.gradle

app/build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.2'
        classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.2.+'
    }
}

apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.mrstar.rx_anroid"
        minSdkVersion 15
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    packagingOptions {
        // workaround for http://stackoverflow.com/questions/20673625/android-gradle-plugin-0-7-0-duplicate-files-during-packaging-of-apk
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/groovy-release-info.properties'
    }
}

apply plugin: 'me.champeau.gradle.groovy-android'

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'io.reactivex:rxandroid:0.22.0'
    compile 'org.codehaus.groovy:groovy:2.4.0-beta-3:grooid'
    compile ('org.codehaus.groovy:groovy-json:2.4.0-beta-3') {
        transitive = false
    }
}

repositories {
    jcenter()
}