2

I have this ListView:

enter image description here

I'm using a custom adapter.

As you see, each row is made of a checkbox, a big TextView, and a little TextView. All items have defined the little TextView, even the "Item 2" but it's a void string.

The problem comes when I tap the EditText placed in the header of the list:

enter image description here

The keyboard appears, and the rows are recycled, so the getView method of my adapter is called. In that method I have an if clause where I check if the length of the "optional" text (the little TextView) is greater than 0. In that case I make some room (the space that you can see in the screenshot) and I display it.

The problem is that "Item 2" has the "optional" text initialized but it's void (0-sized). I don't understand why the if clause is executed. But more strangely... the else is also executed! In the else I just show a void string in the little TextView.

Why is this happening? The app is really simple. This is my getView method:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = vi.inflate(R.layout.list_row, null);
    }

    ListItem list_item = items.get(position);

    TextView item_title = (TextView) convertView.findViewById(R.id.item_title);
    TextView item_optional = (TextView) convertView.findViewById(R.id.item_optional);

    item_title.setText(list_item.getTitle());

    // If the task has an optional text, make some room and display it
    if (list_item.hasOptional()) {
        // This portion of code will be executed when you tap the EditText and the keyboard appears, putting the item up in the row
        LayoutParams layout_params = (LayoutParams) item_title.getLayoutParams();
        layout_params.topMargin = 10;
        layout_params.height = -2;  // -2: wrap_content
        item_title.setLayoutParams(layout_params);
        item_optional.setText(list_item.getOptional());
        item_optional.setVisibility(0);
    } else {
        // This portion of code will ALSO be executed when you tap the EditText... why? this should not happen!
        item_optional.setText("");
    }

    return convertView;
}

The source code can be seen here (github).

David Morales
  • 17,816
  • 12
  • 77
  • 105

1 Answers1

2

When you modify a recycled view you have no idea what the state of the view is, with respect to how it might have been customized by previous calls to getView. The view you are recycling is not a fresh-out-the-box inflation of R.layout.list_row. Think of it as a "second hand" or "used" view.

So I can see under if (list_item.hasOptional().. you make some modification to the item_title.getLayoutParams(). As a view created here may later be recycled for a list item that will fail the check if (list_item.hasOptional() under the else code block you must reset the values you modify to the default specified in the layout.

Jim Blackler
  • 22,946
  • 12
  • 85
  • 101
  • So, I must rollback the values I modify in the if statement, inside the else... is for this reason that it seems both the if and the else are executed at the same time? – David Morales Apr 23 '11 at 23:26
  • I mean, in the else I must do just the opposite that the if does. Am I correct? My doubt is: why when I tap the TextEdit, it seems that the if and the else statements are executed at the same time? – David Morales Apr 24 '11 at 02:16
  • 3
    In the else clause you must indeed do the opposite of your if clause. And an if and an else cannot be executed at the same time. – Romain Guy Apr 24 '11 at 05:09
  • @Romain: Yes I know, but in this case, it seems like both are executed when the keyboard appears. The if clause makes some room, and the else just sets the text to a void string, both behaviors are applied. That's the doubt I have... why? Wouldn't be better to just execute one of them? How could I accomplish it? – David Morales Apr 24 '11 at 22:35
  • 2
    They cannot be both executed at the same time. You made a mistake in your code. – Romain Guy Apr 25 '11 at 02:04
  • Ok, now I have noticed what was wrong. I was not understanding how my custom adapter was being called, and that affected the logic that I implemented. At my eyes, it was very strange as it seemed to execute the if and the else at the same time, but fortunately I have identified the confusing issue. Thanks for your help @Jim and @Romain. – David Morales Apr 25 '11 at 12:20
  • Mmmm... nope, I'm still having a very strange problem with rows recycling that is confusing me again. I'm making a little app to isolate the problem, but if I cannot figure out what is happening I'll start another question with a simpler example. – David Morales Apr 25 '11 at 13:48
  • You could just disable recycling (removing the `if (convertView == null)` clause would do it). Recycling helps performance on long lists, anything less than a few screenfuls you may not see the difference. – Jim Blackler Apr 25 '11 at 13:53
  • I have simplified the app and made it very general to show the problem. I have opened a new question [here](http://stackoverflow.com/questions/5779747/different-visibility-values-in-each-row-in-a-listview). – David Morales Apr 25 '11 at 15:09