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はズレる可能性ありそう。
でも今回の用途には使用しないので詳しくはみてない。

0 件のコメント: