问题
Is it possible to redraw a single row in a ListView
? I have a ListView
with rows that are LinearLayout
s. I listen to a preference change and sometimes I need to change just one View
inside the LinearLayout
of a single row. Is there a way to make it redraw that row without calling listview.notifyDatasetChanged()
?
I've tried calling view.invalidate() on the view (inside the LinearLayout
) but it doesn't redraw the row.
回答1:
As Romain Guy explained a while back during the Google I/O session, the most efficient way to only update one view in a list view is something like the following (this one update the whole View
data):
ListView list = getListView();
int start = list.getFirstVisiblePosition();
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++)
if(target==list.getItemAtPosition(i)){
View view = list.getChildAt(i-start);
list.getAdapter().getView(i, view, list);
break;
}
Assuming target
is one item of the adapter.
This code retrieve the ListView
, then browse the currently shown views, compare the target
item you are looking for with each displayed view items, and if your target is among those, get the enclosing view and execute the adapter getView()
on that view to refresh the display.
As a side note invalidate()
doesn't work like some people expect and will not refresh the view like getView()
does, notifyDataSetChanged()
will rebuild the whole list and end up calling getview()
for every displayed items and invalidateViews()
will also affect a bunch.
One last thing, one can also get extra performance if he only needs to change a child of a row view and not the whole row like getView
does. In that case, the following code can replace list.getAdapter().getView(i, view, list);
(example to change a TextView
text):
((TextView)view.findViewById(R.id.myid)).setText("some new text");
In code we trust.
回答2:
The view.invalidate()
didn't work for me. But this works like a charm:
supose you are updating the position "position
" in the row.
The if
avoid weird redraws stuffs, like text dancing when you are
updating a row that are not in the screen.
if (position >= listView.getFirstVisiblePosition()
&& position <= listView.getLastVisiblePosition()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
listView.invalidateViews();
}
});
}
回答3:
Romain Guy answered to this question at "Google IO 2010 The world of ListView" Video at the exact time where this question was asked : http://www.youtube.com/watch?feature=player_detailpage&v=wDBM6wVEO70#t=3149s
So according to Romain Guy, that should work, and I think we can trust him.
The reason why your view is not redrawn is a bit mysterious. Maybe try disabling all cache options available in listView
setAnimationCacheEnabled(false);
setScrollingCacheEnabled(false);
setChildrenDrawingCacheEnabled(false);
setChildrenDrawnWithCacheEnabled(false);
回答4:
Put some tag on the row that you want to update. When you want to redraw that specific view or row then do
ListView lv = (ListView)findViewById(....)
and then
View v = lv.findViewWithTag(tagobject);
and then you do whatever you want with the view.
来源:https://stackoverflow.com/questions/4075975/redraw-a-single-row-in-a-listview