【ListView Tips Vol.3】ListViewでアイテムアニメーションしよう！ 〜View.startAnimationに関する考察〜


AndroidのViewの中でも頻出かつクセのある **ListView**関連のTips集Vol.3

美しい（笑）アニメーション付きListViewを実装したときのハマリポイントとその原因の考察。


## はじめに
ListView Tipsではありますが、今回はViewのアニメーションについての考察がメインです。
最後に今回の考察を活かした「アニメーション付きListView」の実装を紹介します！

実装については[こちら](http://dev.classmethod.jp/smartphone/android-listview-sideslide/)を参考にさせて頂きました。
ありがとうございます！


## 結論（忙しい人向け）
ListViewなど、（ほぼ）同時に同じアニメーションを`View.startAnimation(Animation anim)`で作る際は、 <br>
**`anim`をViewごとに持たせる（＝別インスタンスにする）**ほうが良い！！
（細かい考察は以下をお読み下さい）
<br>

出来上がりはこんな感じになります。

![beautiful_listview.gif](https://i.gyazo.com/6125b812f5f0d5983adc7035f165726d.gif)

<br>


## ListViewの`getView`内でアニメーション設定したんだけど。。。
ガタガタになりました。
最初のコードはこちら。

```BeautifulListAdapter.java
public class BeautifulListViewAdapter extends BaseAdapter {

    private LayoutInflater mInflater;
    private List<String> mList;
    private Animation mAnimation;	// ココ！！

    public BeautifulListViewAdapter(Context context, List<String> list) {
        mInflater = LayoutInflater.from(context);
        mList = list;
        mAnimation = AnimationUtils.loadAnimation(context, R.anim.item_enter_anim);		// ココ！！
    }

	//------------------------
	// その他Overrideメソッドなど
	//------------------------
	
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.listview_item_row, parent, false);
            holder = new ViewHolder(convertView);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.textView.setText(getItem(position).toString());

        convertView.startAnimation(mAnimation);	// ココ！！

        return convertView;
    }

    public static class ViewHolder {
        TextView textView;

        public ViewHolder(View view) {
            textView = (TextView) view.findViewById(R.id.item_textview);
        }
    }
}
```

実行するとこんな感じです。

![non_beautiful_listview.gif](https://i.gyazo.com/db3683490eec139c8ecf7ce787b87896.gif)


原因を探るため`View.startAnimation`を追って見ると、上記の「ココ！！」の部分がまずかったのです。
<br>

## `View.startAnimation`を追ってみた
内容は簡単ですので、時間のある方は一緒に追ってみて下さい。

#### ■ `startAnimation`

まず最初はこちら。
上の例では`convertView.startAnimation(mAnimation)`の部分です。

```
public void startAnimation(Animation animation) {
    animation.setStartTime(Animation.START_ON_FIRST_FRAME);
    setAnimation(animation);
    invalidateParentCaches();
    invalidate(true);
}
```

なるほど、

* 時間をセットして
* **実行するアニメーションをセットして**
* キャッシュを消して
* ビューをリフレッシュ

している訳ですね。

では次は、`setAnimation(animation)`を見てみます。
<br>

#### ■ `setAnimation`
実はここが問題です。

```
public void setAnimation(Animation animation) {
    mCurrentAnimation = animation;

    if (animation != null) {
        // If the screen is off assume the animation start time is now instead of
        // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
        // would cause the animation to start when the screen turns back on
        if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF
                && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
            animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
        }
        animation.reset();
    }
}
```

ふむふむ、引数で受け取った`animation`がnullでない場合は、もろもろif文があって・・・ **`animation.reset()`**！？

そうなんです、今回はgetViewで先に生成されたViewに紐付いた`mAnimation`が、このresetに引っかかってガタガタになっていたんです。
<br>
つまり、
**`getView`内でアニメーションする時は、Viewごとに`Animation`インスタンスをつくれば良い！！**
となるわけですね！！
<br>

## 美しいListViewの実装
上記を踏まえて再実装します。
ポイントは **ViewごとにAnimationインスタンスを持たせる**ことです！

```BeautifulListAdapter.java
public class BeautifulListViewAdapter extends BaseAdapter {

    private Context mContext;
    private LayoutInflater mInflater;
    private List<String> mList;

    public BeautifulListViewAdapter(Context context, List<String> list) {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mList = list;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.listview_item_row, parent, false);
            holder = new ViewHolder(convertView);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.textView.setText(getItem(position).toString());

		// ココが大事！！
        convertView.startAnimation(AnimationUtils.loadAnimation(mContext, R.anim.item_enter_anim));

        return convertView;
    }

    public static class ViewHolder {
        TextView textView;

        public ViewHolder(View view) {
            textView = (TextView) view.findViewById(R.id.item_textview);
        }
    }
}
```

<br>

ご参考までにアニメーションリソースも載せておきます。

```item_enter_anim.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="750"
    android:fillAfter="true"
    android:fillEnabled="true"
    android:interpolator="@android:anim/accelerate_interpolator">

    <scale
        android:fromXScale="0%"
        android:fromYScale="0%"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="100%"
        android:toYScale="100%" />
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />
</set>
```

<br>

うまくいくと、以下の様な挙動（再掲）になります！

![beautiful_listview.gif](https://i.gyazo.com/6125b812f5f0d5983adc7035f165726d.gif)

<br>

これでどんどんListViewで遊べますね！
※注意
このレイアウトが美しいかは、ご自身でご判断下さい。笑
<br>

## ListView Tips集

* [【ListView Tips Vo.1】ListViewの子要素（アイテム）のClickがおかしくなる 〜主な原因と対処法〜](http://qiita.com/taisho/items/44dfd83e19dc16b9a831)
* [【ListView Tips Vol.2】忙しい人のためのStickyListHeadersListViewの使い方 〜基本編〜](http://qiita.com/taisho/items/84fe76a79eb8c507764d)
* 【ListView Tips Vol.3】ListViewでアイテムアニメーションしよう！ 〜View.startAnimationに関する考察〜
* [【ListView Tips Vol.4-1】ButterKnifeで効率よくIDバインドする 〜findViewByIdもOnClickも、もういらない（仮）〜](http://qiita.com/taisho/items/5f66712d96fd0683a8ca)