How can I update a single row in a ListView?

2018-12-31 17:02发布

I have a ListView which displays news items. They contain an image, a title and some text. The image is loaded in a separate thread (with a queue and all) and when the image is downloaded, I now call notifyDataSetChanged() on the list adapter to update the image. This works, but getView() is getting called too frequently, since notifyDataSetChanged() calls getView() for all visible items. I want to update just the single item in the list. How would I do this?

Problems I have with my current approach are:

  1. Scrolling is slow
  2. I have a fade-in animation on the image which happens every time a single new image in the list is loaded.

10条回答
孤独总比滥情好
2楼-- · 2018-12-31 17:38
int wantedPosition = 25; // Whatever position you're looking for
int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;

if (wantedChild < 0 || wantedChild >= linearLayoutManager.getChildCount()) {
    Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
    return;
}

View wantedView = linearLayoutManager.getChildAt(wantedChild);
mlayoutOver =(LinearLayout)wantedView.findViewById(R.id.layout_over);
mlayoutPopup = (LinearLayout)wantedView.findViewById(R.id.layout_popup);

mlayoutOver.setVisibility(View.INVISIBLE);
mlayoutPopup.setVisibility(View.VISIBLE);

For RecycleView please use this code

查看更多
倾城一夜雪
3楼-- · 2018-12-31 17:39

get the model class first as global like this model class object

SampleModel golbalmodel=new SchedulerModel();

and initialise it to global

get the current row of the view by the model by initialising the it to global model

SampleModel data = (SchedulerModel) sampleList.get(position);
                golbalmodel=data;

set the changed value to global model object method to be set and add the notifyDataSetChanged its works for me

golbalmodel.setStartandenddate(changedate);

notifyDataSetChanged();

Here is a related question on this with good answers.

查看更多
只靠听说
4楼-- · 2018-12-31 17:40

I used the code that provided Erik, works great, but i have a complex custom adapter for my listview and i was confronted with twice implementation of the code that updates the UI. I've tried to get the new view from my adapters getView method(the arraylist that holds the listview data has allready been updated/changed):

View cell = lvOptim.getChildAt(index - lvOptim.getFirstVisiblePosition());
if(cell!=null){
    cell = adapter.getView(index, cell, lvOptim); //public View getView(final int position, View convertView, ViewGroup parent)
    cell.startAnimation(animationLeftIn());
}

It's working well, but i dont know if this is a good practice. So i don't need to implement the code that updates the list item two times.

查看更多
弹指情弦暗扣
5楼-- · 2018-12-31 17:40

exactly I used this

private void updateSetTopState(int index) {
        View v = listview.getChildAt(index -
                listview.getFirstVisiblePosition()+listview.getHeaderViewsCount());

        if(v == null)
            return;

        TextView aa = (TextView) v.findViewById(R.id.aa);
        aa.setVisibility(View.VISIBLE);
    }
查看更多
永恒的永恒
6楼-- · 2018-12-31 17:47

The answers are clear and correct, I'll add an idea for CursorAdapter case here.

If youre subclassing CursorAdapter (or ResourceCursorAdapter, or SimpleCursorAdapter), then you get to either implement ViewBinder or override bindView() and newView() methods, these don't receive current list item index in arguments. Therefore, when some data arrives and you want to update relevant visible list items, how do you know their indices?

My workaround was to:

  • keep a list of all created list item views, add items to this list from newView()
  • when data arrives, iterate them and see which one needs updating--better than doing notifyDatasetChanged() and refreshing all of them

Due to view recycling the number of view references I'll need to store and iterate will be roughly equal the number of list items visible on screen.

查看更多
笑指拈花
7楼-- · 2018-12-31 17:51

This is how I did it:

Your items (rows) must have unique ids so you can update them later. Set the tag of every view when the list is getting the view from adapter. (You can also use key tag if the default tag is used somewhere else)

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    View view = super.getView(position, convertView, parent);
    view.setTag(getItemId(position));
    return view;
}

For the update check every element of list, if a view with given id is there it's visible so we perform the update.

private void update(long id)
{

    int c = list.getChildCount();
    for (int i = 0; i < c; i++)
    {
        View view = list.getChildAt(i);
        if ((Long)view.getTag() == id)
        {
            // update view
        }
    }
}

It's actually easier than other methods and better when you dealing with ids not positions! Also you must call update for items which get visible.

查看更多
登录 后发表回答