AndroidのViewの中でも頻出かつクセのある ListView関連のTips集Vol.3
美しい(笑)アニメーション付きListViewを実装したときのハマリポイントとその原因の考察。
はじめに
ListView Tipsではありますが、今回はViewのアニメーションについての考察がメインです。
最後に今回の考察を活かした「アニメーション付きListView」の実装を紹介します!
実装についてはこちらを参考にさせて頂きました。
ありがとうございます!
結論(忙しい人向け)
ListViewなど、(ほぼ)同時に同じアニメーションをView.startAnimation(Animation anim)
で作る際は、
**anim
をViewごとに持たせる(=別インスタンスにする)**ほうが良い!!
(細かい考察は以下をお読み下さい)
出来上がりはこんな感じになります。
ListViewのgetView
内でアニメーション設定したんだけど。。。
ガタガタになりました。
最初のコードはこちら。
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);
}
}
}
実行するとこんな感じです。
原因を探るためView.startAnimation
を追って見ると、上記の「ココ!!」の部分がまずかったのです。
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)
を見てみます。
■ 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に引っかかってガタガタになっていたんです。
つまり、
getView
内でアニメーションする時は、ViewごとにAnimation
インスタンスをつくれば良い!!
となるわけですね!!
美しいListViewの実装
上記を踏まえて再実装します。
ポイントは ViewごとにAnimationインスタンスを持たせることです!
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);
}
}
}
ご参考までにアニメーションリソースも載せておきます。
<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>
うまくいくと、以下の様な挙動(再掲)になります!
これでどんどんListViewで遊べますね!
※注意
このレイアウトが美しいかは、ご自身でご判断下さい。笑
ListView Tips集
- 【ListView Tips Vo.1】ListViewの子要素(アイテム)のClickがおかしくなる 〜主な原因と対処法〜
- 【ListView Tips Vol.2】忙しい人のためのStickyListHeadersListViewの使い方 〜基本編〜
- 【ListView Tips Vol.3】ListViewでアイテムアニメーションしよう! 〜View.startAnimationに関する考察〜
- 【ListView Tips Vol.4-1】ButterKnifeで効率よくIDバインドする 〜findViewByIdもOnClickも、もういらない(仮)〜