开发者

Android: ListView with a Custom ArrayAdapter acting up on long lists -- Timing issue!

HI all,

I have this "search results" ListView. The search results can be of different "kinds" (different sections, call it). To separate the "kinds" I add a row with a title. (I know about the expandable list, but can't use it for other reasons).

In my getView(), I check for a property, and if it's set, I change the background color of the row.

The problem: when I run a query that returns just a few rows (say 15), everything is fine. But when I run another that returns, say 600 rows, something goes wacko and it changes the background randomly, at a somewhat regular interval. Same thing happens when I'm running in debug mode and stop things in the middle.

So, it's definitely a timing issue.

I'm thinking this might be due to having to re-render the big list as the on-screen keyboard closes.

So, is the Adapter to blame? Is there any solution for this?

If the keyboard is the problem, is there a mechanism to tell the list "wait until the thing closes" before start rendering? (Not sure I like that, but it's better than getting a cute little rainbow...)

Thanks!

Llappall

--

Here's the adapter and the element layout (below):

private class ElementAdapter extends ArrayAdapter<Element> {
  private ArrayList<Element> rows;
  private Element.typeEnum type;

  public ElementAdapter(Context context, int textViewResourceId, ArrayList<Element> rows) {
    super(context, textViewResourceId, rows);
    this.rows = rows;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
      LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      v = vi.inflate(R.layout.element, null);
    }
    Element row = rows.get(posi开发者_如何学编程tion);
    if (row == null) {
      return v;
    }
    v.setTag(row);
    type = row.getType();
    boolean isSectionType = type == Element.typeEnum.DIV118SECTION || type == Element.typeEnum.APPASECT ||
      type == Element.typeEnum.APPBSECT || type == Element.typeEnum.AZSECT;

    TextView title = (TextView) v.findViewById(R.id.title);
    TextView body = (TextView) v.findViewById(R.id.body);
    if (isSectionType) {
      body.setMaxLines(5000);
    }
    title.setText(row.getTitle());
    if (row.getBody() != null) {
      body.setText(row.getBody());
    }
    if (type == Element.typeEnum.SEARCHLISTHEADER) {
      v.setBackgroundColor(Color.rgb(230, 230, 250));
      title.setBackgroundColor(Color.rgb(230, 230, 250));
      body.setBackgroundColor(Color.rgb(230, 230, 250));
      star.setBackgroundColor(Color.rgb(230, 230, 250));
    }
    return v;
  }
}

==ELEMENT LAYOUT==

<TextView
  android:id="@+id/body"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:maxLines="1"
  style="@style/ListItemSubTitle" />
</LinearLayout>


It would be much easier if you would post a getView() method here. From what I can tell, you might be using recycled views wrong. Check if background is changed to something if property is not set. For example:

if (peoperty.isSet()) {
  changeBackGround();
}

Just by itself will be wrong if you are reusing convertView, since the background will stay the same color how it was, when this view was used for a different row.

must be something like:

if (peoperty.isSet()) {
  changeBackGround();
} else {
  changeBackgroundToSomethingNeutral()
}


I took a liberty to rewrite that code for you, since you make too much weird stuff. Here's what I think is a optimized working code for your situation (didn't test any of it, but should work):

    private class ElementAdapter extends ArrayAdapter<Element> {

    public ElementAdapter(Context context, int textViewResourceId, ArrayList<Element> rows) {
        super(context, textViewResourceId, rows);
        this.rows = rows;
    }

    private final ArrayList<Element> rows;

    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {
        ViewsHolder holder = null;
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.element, parent, false);
            holder = new ViewsHolder();
            holder.title = (TextView) v.findViewById(R.id.title);
            holder.body = (TextView) v.findViewById(R.id.body);
            convertView.setTag(holder);
        } else {
            holder = (ViewsHolder) convertView.getTag();
        }
        final Element row = rows.get(position);
        final Element.typeEnum type = row.getType();
        if (type.equals(Element.typeEnum.DIV118SECTION) || type.equals(Element.typeEnum.APPASECT) ||
                  type.equals(Element.typeEnum.APPBSECT) || type.equals(Element.typeEnum.AZSECT)) {
            body.setMaxLines(5000);
        }
        holder.title.setText(row.getTitle());
        if (row.getBody() != null) {
            holder.body.setText(row.getBody());
        } else {
            holder.body.setText("");
        }
        if (type == Element.typeEnum.SEARCHLISTHEADER) {
            convertView.setBackgroundColor(Color.rgb(230, 230, 250));
            holder.title.setBackgroundColor(Color.rgb(230, 230, 250));
            holder.body.setBackgroundColor(Color.rgb(230, 230, 250));
            //star.setBackgroundColor(Color.rgb(230, 230, 250)); // Where did that come from?
        }
        return convertView;
      }

    private final class ViewsHolder {
        public TextView title;
        public TextView body;
    }
}

Couple of notes on the original code:

if (row == null) { return v; }

is wrong. You shouldn't have any null elements in your list for any position in the list. Even if you have, you shouldn't just throw some random view for the row. What you are doing here, is returning "v", that can very well be (and probably will be) some recycled old row, that still displays old data, and that's going to confuse the user. I made an assumption that you won't have any empty elements when wrote the code.

if (row.getBody() != null) {
  body.setText(row.getBody());
}

Is almost ok, but again, if you are reusing convertView (which is some random previous row that isn't displayed anymore), then if body is actually null you will just be displaying the old data, which again will confuse the user. If body is null, just set the string empty.

P.S. I recommend you to watch this for tips and tricks about how to work with ListView: The world of ListView

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜