android listview displays false data after scrolling (custom adapter)

后端 未结 3 2109
感动是毒
感动是毒 2021-01-04 22:06

I\'ve got a strange problem that drives me crazy. In my android application I customized my own adapter that extends from ArrayAdapter. The items of the ListView to which I

相关标签:
3条回答
  • 2021-01-04 22:23

    As mentioned by Sam, Android system always tries to recycle if possible to conserve resources. That means, developers need to keep track of the View on ListView themselves. For the entity, or so-called the presentation data structure, you can have something to keep track of the selected/de-selected state, for example:

    class PresentationData {
        // other stuffs..
        boolean isSelected = false;
    }
    

    Mapping these data structures to the Adapter, and if any item clicked, set the state to true: listPresentationData.get(selected_position).isSelected = true

    Ok so in the getView() of the adapter, keep track the presentation correctly for your data.

    if(listPresentationData.get(position).isSelected) 
    { 
        // set background color of the row layout..or something else 
    }
    

    Also, worth to mention is better to have a memory cache for every views, usually called ViewHolder to improve performance as well.

    0 讨论(0)
  • 2021-01-04 22:25

    As everyone says, Android recycle the views. What does that mean? Imagine you have 10 items in your list, but only 3 of them are displayed on the screen. When scrolling down, the first will disappear and the forth will appear. The fourth item was already in the Android system, but the fifth was created from the first item, using the same view. In my case, I had a TextView with a default background. But if some condition was met, I change the background.

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ...
        if (someCondition) {
            holder.textView.setBackground(R.drawable.different_background)
        }
        ...
    }
    

    The problem was that I don't set back the default background if the condition is not met (as I thought that the view was created for each item). The problem appeared when the first item had the condition true, so the recycled view was used with the different_background instead of the default background. To avoid the problem, I had to always set both branches of the condition and set the default values as follows:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ...
        if (someCondition) {
            holder.textView.setBackground(R.drawable.different_background)
        } else {
            holder.textView.setBackground(R.drawable.default_background)
        }
        ...
    }
    
    0 讨论(0)
  • 2021-01-04 22:36

    I had a similar problem with multiple item types in the list. In my case the list item was either a section (label) item or a common list item.

    To work with such types of lists, you should override getViewTypeCount and getItemViewType methods. Something like this:

    private static final int ITEM_VIEW_TYPE_ITEM = 0;
    private static final int ITEM_VIEW_TYPE_SEPARATOR = 1;
    
    @Override
    public int getViewTypeCount() {
        return 2;
    }
    
    @Override
    public int getItemViewType(int position) {
        return this.getItem(position).isSection() ? ITEM_VIEW_TYPE_SEPARATOR : ITEM_VIEW_TYPE_ITEM;
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final Item item = this.getItem(position);
    
        if (convertView == null) {
            convertView = mInflater.inflate(item.isSection() ? R.view1 : R.view2, null);
        }
    
        if(item.isSection()){
            //...
        }
        else{
            //...
        }
    
        return convertView;
    }
    

    Then the convertView parameter will always be correct and contain that type which you need.

    And another thing: you explicitly added the public Param[] params field when you already have it in the base class ArrayAdapter<Param>.

    I would recommend to inherit from the BaseAdapter class.

    Edit: Here is the code which you can try to use in order to make your Spinner work:

    sp.setTag(p);
    sp.setOnItemSelectedListener(new OnItemSelectedListener(){
    public void onItemSelected(AdapterView<?> parent, View convertView, int pos, long id) {
        Param currentItem = (Param)parent.getTag();
        currentItem.setDefData(currentItem.getData().get(pos));
        currentItem.setChanged(true);
    }
    //...
    

    Try to use the combination of the getTag and setTag methods. I remember that I had similar problems with event handlers and final variables, but I completely forgot the cause of them, so I can't explain exactly why this happens.

    0 讨论(0)
提交回复
热议问题