web-dev-qa-db-fra.com

Android ListView n'actualise pas après notifyDataSetChanged

Mon code de liste 

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 la ListView. Même après le redémarrage de l'application, les éléments mis à jour ne sont pas affichés. Ma ItemAdapter s'é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 aider s'il vous plaît?

106
Coder

Regardez votre méthode onResume 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 private List<Item> items; de l'adaptateur, mais le champ du même fragment déclaré. L’adaptateur conserve toujours une référence à la liste des éléments que vous avez transmis lors de la création de l’adaptateur (par exemple, dans fragment onCreate) . simplement pour 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) supprimer les éléments private List<Item> items; de ItemFragment - nous devons garder la référence à eux uniquement dans l'adaptateur

2) changer onCreate pour:

@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 pour:

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

Vous affectez des éléments rechargés à des éléments de variable globale dans onResume(), mais cela ne sera pas reflété dans la classe ItemAdapter, car elle possède sa propre variable d'instance appelée 'éléments'.

Pour actualiser ListView, ajoutez un refresh () dans la classe ItemAdapter qui accepte les données de liste, c'est-à-dire les éléments.

class ItemAdapter
{
    .....

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

mettre à 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);**
}
21
Santhosh

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 dites jamais à votre adaptateur la nouvelle liste d'éléments. Si vous ne souhaitez pas transmettre une nouvelle liste à votre adaptateur (comme il semble que vous n'en ayez pas), utilisez simplement items.addAll après votre clear(). Cela garantira que vous modifiez la même liste que celle de l'adaptateur. 

8
Justin Breitfeller

Si vous voulez mettre à jour votre liste, peu importe si vous voulez le faire sur onResume(), onCreate() ou dans une autre fonction, vous devez d'abord vous rendre compte que vous n'avez pas besoin de créer une nouvelle instance de l'adaptateur remplissez simplement les tableaux avec vos données à nouveau . L'idée ressemble à 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 devriez utiliser le même List<Item> dans votre Fragment. Lors de votre première initialisation d'adaptateur, vous remplissez votre liste avec les éléments et définissez l'adaptateur sur votre liste. Ensuite, à chaque modification de vos éléments, vous devez effacer les valeurs du List<Item> items principal, puis les ré-peupler avec vos nouveaux éléments et appeler notifySetDataChanged();.

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

4
hardartcore

Si l'adaptateur est déjà défini, il ne sera pas actualisé si la liste est affichée. Au lieu de cela, commencez par vérifier si la liste 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 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 d'actualisation sur le thread d'interface utilisateur:

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

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

Essaye ça 

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

Une réponse de AlexGo a fait le tour pour moi:

getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
         messages.add(m);
         adapter.notifyDataSetChanged();
         getListView().setSelection(messages.size()-1);
        }
});

La mise à jour de liste a fonctionné pour moi auparavant lorsque la mise à jour a été déclenchée à partir d'un événement d'interface graphique, faisant ainsi partie du thread d'interface utilisateur.

Cependant, lorsque je mets à jour la liste à partir d'un autre événement/fil - c'est-à-dire d'un appel externe à l'application, la mise à jour ne serait pas dans le fil de l'interface utilisateur et l'ignorant de l'appel à getListView. Appeler la mise à jour avec runOnUiThread comme ci-dessus a été le bon choix pour moi. Merci!!

3
user2996950
adpter.notifyDataSetInvalidated();

Essayez ceci avec la méthode onPause() de la classe d’activité.

2
Som
adapter.setNotifyDataChanged()

devrait faire l'affaire.

1
Hitman

Essayez comme ça: 

this.notifyDataSetChanged();

au lieu de: 

adapter.notifyDataSetChanged();

Vous devez notifyDataSetChanged() dans ListView et non dans la classe d'adaptateur.

0
Jachu

Si votre liste est contenue dans l'adaptateur même, l'appel de la fonction qui met à jour la liste doit également appeler notifyDataSetChanged().

L'exécution de cette fonction à partir du thread d'interface utilisateur a fait l'affaire pour moi:

La fonction refresh() à l'intérieur de l'adaptateur

public void refresh(){
    //manipulate list
    notifyDataSetChanged();
}

Ensuite, exécutez cette fonction à partir du fil de l'interface utilisateur.

getActivity().runOnUiThread(new Runnable() { 
    @Override
    public void run() {
          adapter.refresh()  
    }
});
0
Dévan Coetzee