0

So I have this ListView with a custom adapter that allows me to "select" an item on item clicks. Now every row of the list has an ImageView whose Drawable I change to a "selected" image when the respective item is selected. Working fine on my Lollipop device.

I construct the Drawables in the constructor of the list adapter

public DoubleRowSelectableArrayAdapter(Context context, int resource2, List<Recording> recordings) {
    super(context, resource2, recordings);

    idle = ContextCompat.getDrawable(context,R.drawable.idleicon);
    animation = (AnimationDrawable) ContextCompat.getDrawable(context, R.drawable.animation);
}

Now, as you can see, the "selected" item is an AnimationDrawable that shall play whenever a view is selected (it is a single-select mode btw).

Views are updated in the getView method

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

    if (convertView == null) {
        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
        convertView = layoutInflater.inflate(resource, null);
    }

    if (position == selection) {
        Log.d("getView", "selection = position");
        convertView.setSelected(true);
        convertView.setPressed(true);

        //get the imageview from the current row
        final ImageView pic = ((ImageView) convertView.findViewById(R.id.iconview));
        pic.setImageDrawable(animation);
    }
    else{
        Log.d("getView","else");
        convertView.setSelected(false);
        convertView.setPressed(false);

        //set idle icon
        ((ImageView) convertView.findViewById(R.id.iconview)).setImageDrawable(idle);
    }

I would assume that the AnimationDrawable has a "global" state for all the list items. So starting the animation once in the constructor should let the animation play all the time, for every list item.

I managed to get the animation playing for every item, for example by starting it in the constructor or in the getView method with a post() call. However, I omitted that part of the code because I want to focus on a slightly different thing.

Here is the problem: When I select a different listitem while the animation is playing (oneshot=false), the newly selected item's animation will play if it is BELOW the old item's position BUT does NOT play if it is ABOVE the old items position in the list. So, I select the first item. Animation playing. Second item selected, animation playing. Third selected, animation playing....10th item selected, animation playing. In the other direction, this is not working. If I have the 10th item's animation playing and select one of the items with position 1-9, the animation won't play at all. I would have to wait for the end of the running animation before a new one could play.

This behaviour changes if I get the Drawable from the resources every time getView is called - then it is working absolutely fine. So in getView

AnimationDrawable d = (AnimationDrawable) getContext().getResources().getDrawable(R.drawable.animation);
pic.setImageDrawable(d);
d.start(); //or by post() call, etc. -- however working.

My wild guess is that this has something to do with View recycling in the list. What should I do to save the (expensive?) instantiation of the drawable from resources in every getView call?

Questions: Why is that? How to set the animation correctly? Is it clever to get the animation drawable from the resources for every new animation start (probably not)? How to do it right?

mad
  • 3,493
  • 4
  • 23
  • 31

1 Answers1

1

Why is that? Whenever an animation is played, behind the scene a thread is generated and values are applied to target object. This thread is synchronised so only one process can enter at time. Hence you have create separate animation object for each view.

How to set the animation correctly? What should I do to save the (expensive?) instantiation of the drawable from resources in every getView call? Is it clever to get the animation drawable from the resources for every new animation start (probably not)? How to do it right? You are doing it right you have to create animationDrawable in each getview() call. If you want to get rid of creating drawable more than one time(expensive task) for a view, set animationDrawable as a tag and retrieve it at each getView call and reinitialise if it is null.

I hope you got your answer if not let me know.

Sachin Chandil
  • 17,133
  • 8
  • 47
  • 65
  • Thanks so far. But still, why does the animation play for list items that are selected below the currently selected item? – mad Dec 29 '15 at 13:36
  • Because each time you interact with listView. `notifyDataSetChanged()` method of its adapter gets called to make sure everything is drawn as per new dataset. That means getView method is called for each dataset values in adapter those are visible on device screen and you have coded adapter to animate selected items. Hence all the visible views get redrawn each time listView refreshes its item. This is the reason selected items animate when you select another item in listView. – Sachin Chandil Dec 30 '15 at 06:18