2011年3月23日水曜日

Designing for PerformanceからAvoid Enumsが消えてた

In-app Billingのテスト実装をするためサンプルソースを読んでいたら、
ガッツリenum型が使われていた。

以前パフォーマンスの問題で単なるintと同じ扱いするだけならなるべく使うなって、
Designing for Performanceに記述があったのに、と思って改めて調べてみると、
記述が削除されてた。


推測される理由?

static final intはガラケーを彷彿させて嫌だったので、
パフォーマンスに影響ない限りは使うべきところには今後enumを使っていきたい。


しかもこのサンプル、int型のレスポンスコードをordinal()で取ってるんだよなあ。
Effective Javaの項目31にこういう使い方は微妙って書いてあったような気が。
私が誤読してるのかな?
まあコピペしちゃいけない処理だしいいのか。

2011年3月22日火曜日

AndroidでXMLを読み込む(ユーティリティを使ったSAXパーサ)

AndroidでのXMLの読み込み方法は、

  1. SAX
  2. DOM
  3. XMLプルパーサ
がある。

今回は、1.SAXを使用した。

SAXでのXMLの解析は、
処理の大部分はハンドラに記述するわけだけど、
対象のXMLがネストの深い構造だったりすると、途端にソースの可読性が下がる。
(私がまだ綺麗な書き方を知らないだけかもしれない…)

AndroidでSAXを扱う場合、
android.saxパッケージにユーティリティクラスが用意されている。これホントに便利。
プルパーサに書き換えようと思っていたけど、とりあえずはこっちでいいかも。

※参考
AndroidでXMLを扱う
http://www.ibm.com/developerworks/jp/xml/library/x-android/



要素の属性を取得する場合は、
StartElementListenerTextlementListenerを使用する必要がある。
<xml>
<root>
<tag id="A001">data</tag>
</root>
みたいなものには、

public List<Data> parse() {
    final Data currentData = new Data();
    List<Data> list = new ArrayList<Data>();

    RootElement root = new RootElement("root");
    Element tag = root.getChild("tag");
    tag.setTextElementListener( new TextElementListener() {
        @Override
        public void start(Attributes attributes) {
            // id属性を取得
            String id = attributes.getValue("id");
            currentData.setId(id);
        }

        @Override
        public void end(String body) {
            // tag要素を取得
            currentData.setData(body);
            list.add(new Data(currentData));
        }
    });
    try {
        Xml.parse(getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
        return list;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

最初はよく理解せず、
ElementListenerとEndTextElementListenerを組み合わせていたけど、
リスナの呼び出し順が、
  1. ElementListener$start
  2. ElementListener$end
  3. EndTextElementListener$end
で、当然意図していたのと違っていた。(2と3が逆だと思った)

Simple Typeの要素にはTextElementListenerを、
Complex Typeの要素にはElementListenerを設定してあげるのが正しいやり方。

2011年3月11日金曜日

ViewAnimator.bringChildToFront()のバグ

というのか仕様というのか。

ViewAnimatorでアニメーションでどのViewが上に描画されるかは、ViewGroupで管理されている子Viewの配列順のようだ。
そこで、確実に上に描画されるようにViewAnimator.bringChildToFront()を使っていると、どうも期待する描画にならなくなってくる。

そこでViewAnimatorのソースを見てみると、どうも表示している子Viewの配列番号を指すべきprivateフィールドであるmWhichChildが、ViewAnimator.bringChildToFront()の呼び出しによりズレていっていた。
ズレが発生した後に、mWhichChildを参照している
ViewAnimator.showNext()
ViewAnimator.showPrevious()
ViewAnimator.getDisplayedChild()
ViewAnimator.getCurrentView()
なんかを使うとアプリの挙動が意図しないモノになる。

対処方法としては、

  1. 子Viewの並び替えが必要ないように最初から追加しておく
  2. showNext(),showPrevious()を使用せず、setDisplayedChild()で表示する。
  3. カスタムViewAnimatorクラスを作成する。
てところだろうか。

1は、ある程度の固定数の子Viewしか持たないことがわかっていれば何も問題ない。
今回は可変の数の子Viewが必要となるため、ListViewのようにViewを使い回し、必要なViewにだけ描画を行うようにしたかったので却下。
2は、更にViewAnimatorを使用する側のクラスでmWhichChildのような子View管理データを持つ必要がある。面倒。
今回は3で解決した。流用が効くし。ViewAnimatorからソース引っ張ってきて、以下のメソッドを追加。

@Override
public void bringChildToFront(View child) {
    int index  = indexOfChild(child);
    if (index < mWhichChild) {
        mWhichChild--;
    } else if (index == mWhichChild) {
        mWhichChild = getChildCount() - 1;
    }
    super.bringChildToFront(child);
}

ViewAnimatorってViewFlipper/ViewSwitcher/ImageSwitcher/TextSwitcherとかの親クラスになってて、結構この辺で問題起きそうな気がする。

addViewとかで現在表示しているViewの裏側になるindex指定してもmWhichChildはズレる可能性ありそう。
でも今回の用途には使用しないので詳しくはみてない。

2011年3月10日木曜日

開発の記録として

Macbook airを買ったので、プライベートでの開発を頑張る。
家は椅子が折りたたみ式の木製の椅子で落ち着かず、誘惑も多々あるので、
外で憧れのカフェプログラミングてのをやってみたい。
当面はAndroid開発をメインに。

とりあえずeclipseをインストール。