Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android listview displays false data after scrolling (custom adapter)

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 added my adapter can either be labeled-text (not editable), editable text or a spinner. The crazy stuff is: when I scroll the ListView, there are two problems:

(1) the (selected) value that is shown in the spinner items changes sometimes although I only scrolled!! when I click on the spinner, the old selected value is still shown (the one, that should be shown by the spinner) (2) the order of the ListViewItems changes when I scroll!

=> BUT the data in the adapter doesn't change (neither the data itself nor the order) - so it must be a problem of the View itself?! maybe android caches in background and doesn't refresh the ListViewItems soon enough or sth like that?!

Can anybody help me please?

Thx a lot!

Ok, I have found a solution that is not very nice, but it works. I simply did not use convertView anymore although that is suboptimal regarding to memory and performance. In my case it should be ok because my ListView's maximal amount of items is 15. Here is my Adapter-Class:

 public class FinAdapter extends ArrayAdapter<Param>{
    public Param[] params;
    private boolean merkzettel;
    protected EditText pv;

    public FinAdapter(Context context, int textViewResourceId, Param[] items, boolean merkzettel) {
        super(context, textViewResourceId, items);
        this.params = items;
        this.merkzettel = merkzettel;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final Param p = params[position];
        if(p != null){
            if(merkzettel){
                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.merk_det_item, null);
                }
                TextView tvl = (TextView) convertView.findViewById(R.id.paramM);
                TextView edl = (TextView) convertView.findViewById(R.id.param_valueM);
                TextView pal = (TextView) convertView.findViewById(R.id.param_unitM);
                if (tvl != null) {
                    tvl.setText(p.getName());                            
                }
                if(pal != null){
                    pal.setText(p.getUnit());
                }
                if(edl != null){
                    edl.setText(p.getDefData());
                }
            }
            else{
                if(p.isSelect()){

                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.fin_prod_list_item_select, null);
                }            
                TextView tvs = (TextView) convertView.findViewById(R.id.paramS);
                Spinner sp = (Spinner) convertView.findViewById(R.id.spinner_kalk);
                TextView paU = (TextView) convertView.findViewById(R.id.param_unitS);


                if (tvs != null) {
                    tvs.setText(p.getName());                            
                }
                if(paU != null){
                    paU.setText(p.getUnit());
                }
                if(sp != null){
                    String[] values = new String[p.getData().size()];
                    for(int i=0; i<values.length; i++){
                        values[i] = p.getData().get(i);
                    }
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this.getContext(), android.R.layout.simple_spinner_item, values);
                    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                    sp.setAdapter(adapter);
                    sp.setSelection(p.getData().indexOf(p.getDefData()));
                    sp.setOnItemSelectedListener(new OnItemSelectedListener(){
                        public void onItemSelected(AdapterView<?> parent,
                                View convertView, int pos, long id) {
                            p.setDefData(p.getData().get(pos));
                            p.setChanged(true);
                        }
                        public void onNothingSelected(AdapterView<?> arg0) {
                            // TODO Auto-generated method stub

                        }

                    });
                }
            }       
            else if(p.isEdit()){
                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.fin_prod_list_item_edit, null);
                }
                TextView pa = (TextView) convertView.findViewById(R.id.param);
                pv = (EditText) convertView.findViewById(R.id.param_value);
                TextView paE = (TextView) convertView.findViewById(R.id.param_unit);
                if (pa != null) {
                    pa.setText(p.getName());                            
                }
                if(paE != null){
                    paE.setText(p.getUnit());
                }
                if(pv != null){
                    pv.setText(p.getDefData());
                    pv.setOnEditorActionListener(new OnEditorActionListener(){
                        public boolean onEditorAction(TextView convertView, int actionId,
                                KeyEvent event) {
                            // TODO Auto-generated method stub
                            p.setDefData(pv.getText().toString());
                            p.setChanged(true);
                            return false;
                        }                   
                    }); 
                }

            }
            else if(p.isLabel()){
                if (convertView == null) {
                    LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    convertView = vi.inflate(R.layout.fin_prod_list_item_label, null);
                }
                TextView tvl = (TextView) convertView.findViewById(R.id.paramL);
                TextView edl = (TextView) convertView.findViewById(R.id.param_valueL);
                TextView pal = (TextView) convertView.findViewById(R.id.param_unitL);
                if (tvl != null) {
                    tvl.setText(p.getName());                            
                }
                if(pal != null){
                    pal.setText(p.getUnit());
                }
                if(edl != null){
                    edl.setText(p.getDefData());
                }   
            }}
        }
        return convertView;
    }
}
like image 317
user1384200 Avatar asked Aug 15 '12 15:08

user1384200


2 Answers

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.

like image 82
vortexwolf Avatar answered Nov 12 '22 02:11

vortexwolf


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.

like image 28
Pete Houston Avatar answered Nov 12 '22 01:11

Pete Houston