Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Animation in Adapter with ViewHolder Pattern

Tags:

android

I have a problem when using an Animation in my Adapter.

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

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(context);
            convertView = inflater.inflate(resource, parent, false);
            holder = new ViewHolder();

            holder.newRoomView = (TextView) convertView.findViewById(R.id.newRoom);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Room item = items.get(position);

        // animate new rooms
        if (item.isNewRoom()) {
            AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);
            alphaAnim.setDuration(1500);
            alphaAnim.setAnimationListener(new AnimationListener() {
                public void onAnimationEnd(Animation animation) {
                    holder.newRoomView.setVisibility(View.INVISIBLE);
                }

                @Override
                public void onAnimationStart(Animation animation) {}

                @Override
                public void onAnimationRepeat(Animation animation) {}
            });
            holder.newRoomView.startAnimation(alphaAnim);
        }

        // ...

        return convertView;
    }

When adding a new room outside of the adapter and calling notifyDataSetChanged the new room is correctly animated, but when onAnimationEnd is called, another (not new room) is hidden.

Is there any way to get the correct room hidden?

like image 977
Chris Avatar asked Apr 21 '15 19:04

Chris


2 Answers

Since you haven't declared the holder variable in the getView() method, I can only assume that you've declared it as an instance variable in your class. This is your problem. By the time the animation is completed, the variable holder is holding a reference to a completely different item.

You need to use a local variable which is declared as final inside the getView() method. I don't know if you need this holder variable outside the getView() method or not, but if you do, you can do this:

    // animate new rooms
    if (item.isNewRoom()) {
        final ViewHolder holderCopy = holder; // make a copy
        AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);
        alphaAnim.setDuration(1500);
        alphaAnim.setAnimationListener(new AnimationListener() {
            public void onAnimationEnd(Animation animation) {
                holderCopy.newRoomView.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onAnimationStart(Animation animation) {}

            @Override
            public void onAnimationRepeat(Animation animation) {}
        });
        holder.newRoomView.startAnimation(alphaAnim);
    }

This, of course, will not work if the animation takes so long that the view has been recycled in the meantime.

like image 64
David Wasser Avatar answered Nov 16 '22 01:11

David Wasser


    if (item.isNewRoom()) {

        // store view reference first
        final View newRoomView = holder.newRoomView;
        ...
        alphaAnim.setAnimationListener(new AnimationListener() {
            public void onAnimationEnd(Animation animation) {

                // hide exactly this view when animation ends
                newRoomView.setVisibility(View.INVISIBLE);
            }
        ...
    }
like image 38
sergej shafarenka Avatar answered Nov 15 '22 23:11

sergej shafarenka