Android: ListView not refreshing on notifyDataSetChanged();

FloIancu picture FloIancu · Dec 3, 2011 · Viewed 15.7k times · Source

I've got a custom BaseAdapter and an add button in the main activity. The button opens a dialog with a textbox and you can add new elements to the list that way. The problem is that the list is not refreshing. In the onActivityResult() function I print the number of elements in the list and each time I hit OK in the dialog box the number increases, so I know it's just the refreshing that doesn't work. My BaseAdapter and my activity:

class ListaOrase extends BaseAdapter{
    private Activity context;
    ArrayList<String> orase;

    public ListaOrase(Activity context){
        this.context=context;
        orase=new ArrayList<String>();
    }
    public void add(String string){
        orase.add(string);
        this.notifyDataSetChanged();
    }

    public View getView (int position, View convertView, ViewGroup list)  {
        View element;
        if (convertView == null)
        {
         LayoutInflater inflater = context.getLayoutInflater();
         element = inflater.inflate(R.layout.lista, null);
        }
        else element = convertView;
        TextView elementLista=(TextView)element.findViewById(R.id.elementLista);    
        elementLista.setText(orase.get(position));
        return element;
    }

}

public class WeatherAppActivity extends ListActivity {

    Button buton;
    ListaOrase lista;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        lista=new ListaOrase(this);
        buton=(Button)findViewById(R.id.buton);


        lista.add("Bucuresti");
        lista.add("Sibiu");
        setListAdapter(lista);

    }

    public void add(View view){
        Intent intent=new Intent();
        intent.setClass(this, Adauga.class);
        startActivityForResult(intent, 0);
    }

    public void onActivityResult (int requestCode, int responseCode, Intent data){
        System.out.println("Apelata");
        if(responseCode==1){
            lista.add(data.getStringExtra("oras")); // e chiar getText()
            System.out.println(lista.getCount());
            lista.notifyDataSetChanged();
        }
    }
}

As you can see, I'm trying to refresh (notifyDataSetChanged();) both when adding a new element (in the BaseAdapter extending class) and in method onActivityResult, after the dialog passes the new element to the main Activity. I repeat, the element IS added to the list because the count increases, it just doesn't refresh.

Thanks for your answers!

Answer

gwvatieri picture gwvatieri · Dec 3, 2011

It's normal that it doesn't refresh, you are adding an item to "lista" but the adapter keeps its own copy of that list, so or you set again the list in the adapter and then you call notifyDataChanged or you add the new item to the adapter.

Anyway I see couple of weird things, I thing you could semplify everything using an array adapter, you don't need to implement add,etc. I wrote some code simplyfing yours:

public class WeatherAppActivity extends ListActivity {

Button buton;
ItemsAdapter lista;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    List<String> initialList = new ArrayList<String>();
    initialList.add("Bucuresti");
    initialList.add("Sibiu");

    lista=new ItemsAdapter(this, initialList);
    buton=(Button)findViewById(R.id.button1);
    buton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
             lista.add(""+System.currentTimeMillis()); // e chiar getText()
             lista.notifyDataSetChanged();

        }
    });

    setListAdapter(lista);

}

class ItemsAdapter extends ArrayAdapter<String> {

    public ItemsAdapter(Context context, List<String> list) {
        super(context, R.layout.lista, list);
    }

    @Override
    public View getView(final int position, View row, final ViewGroup parent) {
        final String item = getItem(position);

        ItemWrapper wrapper = null;
        if (row == null) {
            row = getLayoutInflater().inflate(R.layout.lista, parent, false);
            wrapper = new ItemWrapper(row);

            row.setTag(wrapper);
        } else {
            wrapper = (ItemWrapper) row.getTag();
        }
        wrapper.refreshData(item);

        return row;
    }

    class ItemWrapper {

        TextView text;

        public ItemWrapper(View row) {
            text = (TextView) row.findViewById(R.id.elementLista);
        }

        public void refreshData(String item) {
            text.setText(item);
        }

    }
    }    

}

These are the xml that I have used:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="63dp"
    android:text="Button" />

<ListView
    android:id="@id/android:list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" >
</ListView>

</RelativeLayout>

lista.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
    android:id="@+id/elementLista"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Medium Text"
    android:textAppearance="?android:attr/textAppearanceMedium" />

</LinearLayout>

This is the version of the adapter using a baseadapter:

class ItemsBaseAdapter extends BaseAdapter {

private List<String> items;
private Context mContext;

public ItemsBaseAdapter(Context context, List<String> list) {
    items = list;
    mContext = context;
}

public void addItem(String str) {
    items.add(str);
}

@Override
public View getView(final int position, View row, final ViewGroup parent) {
    final String item = (String) getItem(position);

    ItemWrapper wrapper = null;
    if (row == null) {
        row = getLayoutInflater().inflate(R.layout.lista, parent, false);
        wrapper = new ItemWrapper(row);

        row.setTag(wrapper);
    } else {
        wrapper = (ItemWrapper) row.getTag();
    }
    wrapper.refreshData(item);

    return row;
}

class ItemWrapper {

    TextView text;

    public ItemWrapper(View row) {
        text = (TextView) row.findViewById(R.id.elementLista);
    }

    public void refreshData(String item) {
        text.setText(item);
    }

}

@Override
public int getCount() {
    return items.size();
}

@Override
public Object getItem(int position) {
    return items.get(position);
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return 0;
}
}

And this is the version of the list item wich also include an imageview on the left:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<ImageView 
    android:layout_height="wrap_content" 
    android:src="@android:drawable/btn_star_big_on" 
    android:scaleType="fitCenter" 
    android:layout_width="wrap_content" 
    />

<TextView
    android:id="@+id/elementLista"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Medium Text"
    android:textAppearance="?android:attr/textAppearanceMedium" 
    />

</LinearLayout>