2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Anvil - Reactライクな差分レンダリングをAndroidでやってみる

Last updated at Posted at 2016-12-10

この記事はWanoグループAdventCalendar2016の10日目の記事になります。
今の所ちゃんと埋まっていてホッとしています。(ちょっと遅れた)

書くこと

本稿では、Androidのネイティブ開発用のViewライブラリ、Anvilについて取り扱います。
AnvilはWebフロントエンドでの仮想DOMライブラリ、Reactやmithril.jsにインスパイアされています。
個人的にはDataBindingなどの記法より好きで(あれをいじってるとなんかAngular1系のこと思い出してウッ...てなる)、なかなか便利なライブラリですが、日本語での情報を見かけないのでご紹介しようと思います。

ReactNativeに移行するほどではないけど、Reactの使い勝手をネイティブアプリ開発で求めている方にオススメです。

Anvil概要

Anvil はAndroidのモバイルアプリ開発用のライブラリでありながら、webフロントエンド界隈でいう仮想DOMの"差分変更"という考え方にインスパイアされています。

リポジトリのページやanvil.site や example作者さんのブログ? を見ればたいていの用法はわかるかと思うのですが、いくつか使い方をご紹介します。
導入が恐ろしく簡単です。

Kotlinでももちろん使えますが、今回は Java + retolambda でコードを記述してみます。

Componentのimport

最低限anvil-sdk本体をimportすれば使えますが、他にもいろいろ揃ってます。
build.gradle

compile 'co.trikita:anvil-sdk15:0.4.0'
compile 'co.trikita:anvil-appcompat-v7:0.4.0'
compile 'co.trikita:anvil-support-v4:0.4.0'
compile 'co.trikita:anvil-design:0.4.0'
compile 'co.trikita:anvil-recyclerview-v7:0.4.0'

importする


import trikita.anvil.Anvil;
import static trikita.anvil.DSL.*;
import static trikita.anvil.appcompat.v7.AppCompatv7DSL.*;
// ... 自由

Stateless Component

関数的に走る、状態を持たないviewの記述法。
たいていの場合はこれだけで済むかなあと思います。



Anvil.mount(findViewById(R.id.anvil_content) , ()->{
    relativeLayout(()->{
        size(FILL,FILL);

        init(()->{
            //初回だけなんかするときはここ
        });

        textView(()->{
            id(R.id.dummy_id_1);
            size(WRAP,WRAP);
            textSize(R().getDimensionPixelSize(R.dimen.my_big_text_size));
            centerHorizontal();
            centerVertical();
            textColor(R().getColor(R.color.white));

            if (myData.text != null){
              text(myData.text);
            } else {
              text("HogeHoge");
            }
        });
    });
});

myData.text = "FugaFuga"; //モデルの変更
Anvil.render(); //差分再計算

Stateful Component

一時的にコンポーネント内に情報を保持しておきたい場合やなど、自前のロジックでViewの状態を管理する必要があるときは


public class MyView extends RenderableView {
    public MyView(Context c) { super(c); }
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        // Initialize your view component
    }
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        // De-initialize your view component
    }
    public void view() { /* your layout here */ }
}

こんな感じで RenderableView を継承して使います。

独自XMLやサードパーティのView 

ライブラリで定義されていないViewをさっくり当てる方法もあります。
ここでは、CSSのFlexBoxレイアウトに慣れてる方であれば超絶便利なGoogle先生のflexbox-layout を当ててみます。


v(FlexboxLayout.class , ()->{

    init(()->{
        FlexboxLayout flexboxLayout = Anvil.currentView();
        flexboxLayout.setJustifyContent(FlexboxLayout.JUSTIFY_CONTENT_SPACE_BETWEEN);
        flexboxLayout.setFlexDirection(FlexboxLayout.FLEX_DIRECTION_ROW);
    });

    for (ChildModel childModel : childModels){
        textView(()->{
            size(WRAP ,WRAP);
            text(childModel.text);
        });
    }
});

これでカスタムViewも使えるようになりました!

カスタマイズAttribute

上記の例ではinit();内で無理やりカスタムビューのセッターを叩いていましたが、もちろんこれでは初めの1回しか適用されません。
ここでは、Supportv4DSLの内容を参考に、画像ロードライブラリPicassoの入力差分を管理する関数を書いてみます。

private static final class PicassoFunc implements Anvil.AttrFunc<String> {
    public static final PicassoFunc instance = new PicassoFunc();

    public void apply(View v, final String newValue, final String oldValue) {
        if (v instanceof ImageView) {
            if (newValue == null){
                return;
            }

            if (newValue.equals(oldValue)){
                return;
            }

            Picasso.with(v.getContext())
                    .load(newValue)
                    .into((ImageView) v);

        }
    }
}

public static Void picasso(String arg) {
    return BaseDSL.attr(PicassoFunc.instance, arg);
}


これで,

imageView(()->{
    size(FILL,FILL);
    picasso("https://???.png");
    
});

のように、Picassoの差分をとる関数ができました。

結び

いくつか使用方法をご紹介しました。

Flux/Redux系のデザインパターンとの相性も非常にいいのではないでしょうか。
もちろん、がっつりフレームワークとして使わなくとも、ちゃちゃっとviewを動的に当てたい、などの用途に関しても非常に有用かと思いますので、
この際に試してみてください。

明日のwanoグループのアドベントカレンダーはbbq-allstars氏の「アプリのCIについて」です。

2
3
0

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
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?