113 votes

Le ListView Android ne se rafraîchit pas après notifyDataSetChanged

Mon code ListFragment

public class ItemFragment extends ListFragment {

    private DatabaseHandler dbHelper;
    private static final String TITLE = "Items";
    private static final String LOG_TAG = "debugger";
    private ItemAdapter adapter;
    private List<Item> items;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.item_fragment_list, container, false);        
        return view;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.setHasOptionsMenu(true);
        super.onCreate(savedInstanceState);
        getActivity().setTitle(TITLE);
        dbHelper = new DatabaseHandler(getActivity());
        items = dbHelper.getItems(); 
        adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
        this.setListAdapter(adapter);

    }

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter.notifyDataSetChanged();
}

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        if(dbHelper != null){ //item is edited
            Item item = (Item) this.getListAdapter().getItem(position);
            Intent intent = new Intent(getActivity(), AddItemActivity.class);
            intent.putExtra(IntentConstants.ITEM, item);
            startActivity(intent);
        }
    }
}

Mon ListView

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

    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
    />

</LinearLayout>

Mais cela ne rafraîchit pas le ListView . Même après avoir redémarré l'application, les éléments mis à jour n'apparaissent pas. Mon ItemAdapter étend BaseAdapter

public class ItemAdapter extends BaseAdapter{

    private LayoutInflater inflater;
    private List<Item> items;
    private Context context;

    public ProjectListItemAdapter(Context context, List<Item> items) {
        super();
        inflater = LayoutInflater.from(context);
        this.context = context;
        this.items = items;

    }

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

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

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemViewHolder holder = null;
         if(convertView == null) {
             holder = new ItemViewHolder();
             convertView = inflater.inflate(R.layout.list_item, parent,false);
             holder.itemName = (TextView) convertView.findViewById(R.id.topText);
             holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
             convertView.setTag(holder);
          } else {
             holder = (ItemViewHolder) convertView.getTag();
          }
          holder.itemName.setText("Name: " + items.get(position).getName());
          holder.itemLocation.setText("Location: " + items.get(position).getLocation());
          if(position % 2 == 0){
              convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
            }else{
              convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
          }
          return convertView;
    }

    private static class ItemViewHolder{
        TextView itemName;
        TextView itemLocation;
    }
}

Quelqu'un peut-il m'aider ?

224voto

Tomasz Gawel Points 2554

Regardez votre onResume méthode dans ItemFragment :

@Override
public void onResume() {
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); // reload the items from database
    adapter.notifyDataSetChanged();
}

ce que vous venez de mettre à jour avant d'appeler notifyDataSetChanged() n'est pas le champ de l'adaptateur private List<Item> items; mais le champ identiquement déclaré du fragment. L'adaptateur stocke toujours une référence à la liste des éléments que vous avez passés lorsque vous avez créé l'adaptateur (par exemple, dans le onCreate du fragment). Le moyen le plus court (en termes de nombre de modifications) mais non élégant de faire en sorte que votre code se comporte comme vous l'attendez est simplement de remplacer la ligne :

    items = dbHelper.getItems(); // reload the items from database

avec

    items.addAll(dbHelper.getItems()); // reload the items from database

Une solution plus élégante :

1) retirer les articles private List<Item> items; de ItemFragment - nous devons garder la référence à eux seulement dans l'adaptateur

2) changer onCreate en :

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setHasOptionsMenu(true);
    getActivity().setTitle(TITLE);
    dbHelper = new DatabaseHandler(getActivity());
    adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
    setListAdapter(adapter);
}

3) ajouter une méthode dans ItemAdapter :

public void swapItems(List<Item> items) {
    this.items = items;
    notifyDataSetChanged();
}

4) changez votre onResume en :

@Override
public void onResume() {
    super.onResume();
    adapter.swapItems(dbHelper.getItems());
}

22voto

Santhosh Points 1224

Vous assignez les éléments rechargés à la variable globale items dans onResume(), mais cela ne se reflétera pas dans la classe ItemAdapter, parce qu'elle a sa propre variable d'instance appelée 'items'.

Pour rafraîchir la liste, ajoutez une fonction refresh() dans la classe ItemAdapter qui accepte les données de la liste, c'est-à-dire les éléments.

class ItemAdapter
{
    .....

    public void refresh(List<Item> items)
    {
        this.items = items;
        notifyDataSetChanged();
    } 
}

mettez à jour onResume() avec le code suivant

@Override
public void onResume()
{
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); //reload the items from database
    **adapter.refresh(items);**
}

8voto

Justin Breitfeller Points 6375

Dans onResume() changez cette ligne

items = dbHelper.getItems(); //reload the items from database

à

items.addAll(dbHelper.getItems()); //reload the items from database

Le problème est que vous ne parlez jamais à votre adaptateur de la liste des nouveaux articles. Si vous ne voulez pas passer une nouvelle liste à votre adaptateur (ce qui semble être le cas), utilisez simplement la commande items.addAll après votre clear() . Cela permet de s'assurer que vous modifiez la même liste que celle à laquelle l'adaptateur fait référence.

4voto

AlexGo Points 285

Si l'adaptateur est déjà défini, le redéfinir ne rafraîchira pas le listview. Au lieu de cela, vérifiez d'abord si le listview a un adaptateur, puis appelez la méthode appropriée.

Je pense que ce n'est pas une très bonne idée de créer une nouvelle instance de l'adaptateur lors du paramétrage de la vue en liste. Au lieu de cela, créez un objet.

BuildingAdapter adapter = new BuildingAdapter(context);

    if(getListView().getAdapter() == null){ //Adapter not set yet.
     setListAdapter(adapter);
    }
    else{ //Already has an adapter
    adapter.notifyDataSetChanged();
    }

Vous pouvez également essayer d'exécuter la liste de rafraîchissement sur un thread d'interface utilisateur :

activity.runOnUiThread(new Runnable() {         
        public void run() {
              //do your modifications here

              // for example    
              adapter.add(new Object());
              adapter.notifyDataSetChanged()  
        }
});

4voto

Android-Developer Points 6542

Si vous voulez mettre à jour votre listview, peu importe si vous voulez le faire sur onResume() , onCreate() ou dans une autre fonction, la première chose que vous devez réaliser est que vous n'aurez pas besoin de créer une nouvelle instance de l'adaptateur, mais simplement de remplir à nouveau les tableaux avec vos données. L'idée est quelque chose de similaire à ceci :

private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;

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

    myListView = (ListView) findViewById(R.id.my_list);

    titles = new ArrayList<String>()

    for(int i =0; i<20;i++){
        titles.add("Title "+i);
    }

    adapter = new MyListAdapter(this, titles);
    myListView.setAdapter(adapter);
}

@Override
public void onResume(){
    super.onResume();
    // first clear the items and populate the new items
    titles.clear();
    for(int i =0; i<20;i++){
        titles.add("New Title "+i);
    }
    adapter.notifySetDataChanged();
}

Donc en fonction de cette réponse, vous devez utiliser la même List<Item> dans votre Fragment . Dans la première initialisation de l'adaptateur, vous remplissez votre liste avec les éléments et définissez l'adaptateur sur votre listview. Après cela, chaque fois que vous modifiez vos éléments, vous devez effacer les valeurs de l'adaptateur principal. List<Item> items puis de le remplir à nouveau avec vos nouveaux éléments et de l'appeler notifySetDataChanged(); .

C'est comme ça que ça marche : ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X