mattbdean
mattbdean

Reputation: 2552

Updating the contents of a View in a ListView

I have a ListView which will host compound Views called TaskView, which is a visual representation of a Task. The background work is done in an AsyncTask and the TaskView's children (two TextViews and one ProgressView) are updated in onProgressUpdate(). After I'm finished updating the Views, I call listView.invalidateViews(), which I believe is supposed to redraw all its child Views. However, this is not the case because none of the views change. After setting some breakpoints and observing the values of the various views, I have determined that they do change, but their new values just aren't reflected onscreen

Here is an outline of my AsyncTask (note: class and method names have been simplified)

private TaskView view;

public Monitor(TaskView view) {
    this.view = view;
}

protected Void doInBackground(Void... params) {
    // <set up a handler to push events to onProgressUpdate()>
    // <do work>
    return null;
}

protected void onProgressUpdate(Event... values) {
    Task task = view.getTask();

    // <update task based on the Event>

    view.update(); // Tells view to update its TextViews and ProgressBar
                   // based on task's values
    listView.invalidateViews(); // Refresh ListView views?
}

How can I get ListView to redraw its children?


As requested, here is TaskAdapter.getView()

public View getView(int position, View convertView, ViewGroup parent) {
    Task task = getItem(position);
    TaskView view = (TaskView) convertView;
    if (view == null) {
        // Instantiates a TaskView and sets the Task
        view = TaskView.newInstance(getContext(), task);
    }
    return view;
}

and TaskView.update()

public void update() {
    // https://github.com/Todd-Davies/ProgressWheel
    progressWheel.setProgress(task.getProgress());
    if (task.isIndeterminate()) {
        progressWheel.spin();
    } else {
        progressWheel.stopSpinning();
    }

    // status and secondaryStatus are TextViews
    status.setText(task.getStatus());
    secondaryStatus.setText(task.getSecondaryStatus());
}

Upvotes: 2

Views: 90

Answers (2)

mmlooloo
mmlooloo

Reputation: 18977

Use the invalidate() method for each view you've changed. Another trick is : updating the array list object that you pass to the adapter for example if the view that you want to update is at position i do this:

list.remove(i);
list.add(i, new object()); // the new object must have the updated content 
adapter.notifyDataSetChange();

Update:

let me tell you the problem again: you have a list with a taskview object ( each row corresponds to a taskview) in each row you have a progrees bar and 2 textviews you want to update some rows

Solution :

 protected void onProgressUpdate(Event... values) {
    Task task = view.getTask();

    task.setProgress() // first change the content of taskView data
    task.setStatus()
    task.setSecondaryStatus()

    // now let the adapter do update the rows
    adapter.notifyDataSetChange() // if it dose not work remove task and add to list and then again call notify

 }

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

    LayoutInflater inflater = 
                (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.list_item, parent, false);

    Task task = getItem(position);
    TextView status = view.findViewById();
    status.setText(task.getStatus());
    Progressbar pb = view.findViewById();
    //....

    TextView secondaryStatus = view.findViewById();
    secondaryStatus.setText(task.getSecondaryStatus());
    return view;
 }

Upvotes: 0

DanKodi
DanKodi

Reputation: 3640

To refresh the listview with new data call the notifyDataSetChanged() method of the adapter assigned to the listview.

Upvotes: 3

Related Questions