12
12

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.

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

Last updated at Posted at 2015-08-16

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

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

はじめに

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

実装についてはこちらを参考にさせて頂きました。
ありがとうございます!

結論(忙しい人向け)

ListViewなど、(ほぼ)同時に同じアニメーションをView.startAnimation(Animation anim)で作る際は、

**animをViewごとに持たせる(=別インスタンスにする)**ほうが良い!!
(細かい考察は以下をお読み下さい)

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

beautiful_listview.gif


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

原因を探るため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インスタンスを持たせることです!

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);
        }
    }
}

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

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>

うまくいくと、以下の様な挙動(再掲)になります!

beautiful_listview.gif


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

ListView Tips集

12
12
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
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?