Как обновить список просмотров Android?

Как обновить Android ListView после добавления / удаления динамических данных?

Solutions Collecting From Web of "Как обновить список просмотров Android?"

Вызовите notifyDataSetChanged() в свой объект Adapter после изменения данных в этом адаптере.

Некоторые дополнительные особенности того, как / когда вызывать notifyDataSetChanged() можно просмотреть в этом видеообъявлении Google I / O.

Также вы можете использовать это:

 myListView.invalidateViews(); 

наслаждаться!

Пожалуйста, проигнорируйте все invalidate() , invalidateViews() , requestLayout() , … ответы на этот вопрос.

Правильная вещь (и, к счастью, также отмеченная как правильный ответ) заключается в вызове notifyDataSetChanged() на вашем адаптере .

Исправление проблем

Если вызов notifyDataSetChanged() не работает, все методы компоновки тоже не помогут. Поверьте, ListView был правильно обновлен. Если вам не удается найти разницу, вам нужно проверить, откуда взялись данные в вашем адаптере.

Если это всего лишь коллекция, которую вы храните в памяти, проверьте, что вы фактически удалили или добавили элементы (элементы) в коллекцию, прежде чем вызывать notifyDataSetChanged() .

Если вы работаете с базой данных или базой данных службы, вам придется вызвать метод для получения информации снова (или манипулировать данными в памяти) перед вызовом функции notifyDataSetChanged() .

Дело в том, что notifyDataSetChanged работает только в том случае, если набор данных изменился. Таким образом, это место для поиска, если вы не найдете изменений. При необходимости отлаживайте.

ArrayAdapter vs BaseAdapter

Я обнаружил, что работает с адаптером, который позволяет управлять коллекцией, например, BaseAdapter работает лучше. Некоторые адаптеры, такие как ArrayAdapter, уже управляют своей собственной коллекцией, что затрудняет доступ к соответствующей коллекции для обновлений. В большинстве случаев это просто лишний лишний уровень сложности.

Пользовательский интерфейс

Это правда, что это нужно вызывать из потока пользовательского интерфейса. В других ответах есть примеры того, как этого добиться. Однако это требуется только в том случае, если вы работаете над этой информацией из-за пределов пользовательского интерфейса. Это из службы или неинтерфейса. В простых случаях вы будете обновлять свои данные с помощью нажатия кнопки или другого действия / фрагмента. Так что все еще в потоке пользовательского интерфейса. Не нужно всегда вызывать этот runOnUiTrhead.

Быстрый пример проекта

Вы можете найти на странице https://github.com/hanscappelle/so-2250770.git . Просто клонируйте и откройте проект в Android Studio (gradle). Этот проект имеет MainAcitivity, создавая ListView со всеми случайными данными. Этот список можно обновить, используя меню действий.

Реализация адаптера, созданная для этого примера, ModelObject предоставляет сбор данных

 public class MyListAdapter extends BaseAdapter { /** * this is our own collection of data, can be anything we want it to be as long as we get the * abstract methods implemented using this data and work on this data (see getter) you should * be fine */ private List<ModelObject> mData; /** * our ctor for this adapter, we'll accept all the things we need here * * @param mData */ public MyListAdapter(final Context context, final List<ModelObject> mData) { this.mData = mData; this.mContext = context; } public List<ModelObject> getData() { return mData; } // implement all abstract methods here } 

Код от MainActivity

 public class MainActivity extends Activity { private MyListAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView list = (ListView) findViewById(R.id.list); // create some dummy data here List<ModelObject> objects = getRandomData(); // and put it into an adapter for the list mAdapter = new MyListAdapter(this, objects); list.setAdapter(mAdapter); // mAdapter is available in the helper methods below and the data will be updated based on // action menu interactions // you could also keep the reference to the android ListView object instead and use the // {@link ListView#getAdapter()} method instead. However you would have to cast that // adapter to your own instance every time } /** * helper to show what happens when all data is new */ private void reloadAllData(){ // get new modified random data List<ModelObject> objects = getRandomData(); // update data in our adapter mAdapter.getData().clear(); mAdapter.getData().addAll(objects); // fire the event mAdapter.notifyDataSetChanged(); } /** * helper to show how only changing properties of data elements also works */ private void scrambleChecked(){ Random random = new Random(); // update data in our adapter, iterate all objects and resetting the checked option for( ModelObject mo : mAdapter.getData()) { mo.setChecked(random.nextBoolean()); } // fire the event mAdapter.notifyDataSetChanged(); } } 

Больше информации

Еще один приятный пост о силе listViews можно найти здесь: http://www.vogella.com/articles/AndroidListView/article.html

Call runnable, когда вы хотите:

 runOnUiThread(run); 

OnCreate() , вы устанавливаете свой runnable thread:

 run = new Runnable() { public void run() { //reload content arraylist.clear(); arraylist.addAll(db.readAll()); adapter.notifyDataSetChanged(); listview.invalidateViews(); listview.refreshDrawableState(); } }; 

У меня возникли проблемы с динамическим обновлением моего списка.

Вызовите notifyDataSetChanged () для вашего адаптера.

Некоторые дополнительные особенности того, как / когда вызывать notifyDataSetChanged (), можно просмотреть в этом видеообъявлении Google I / O.

NotifyDataSetChanged () не работает должным образом в моем случае [я вызвал notifyDataSetChanged из другого класса]. Как раз в том случае, когда я редактировал ListView в текущей активности (Thread). Это видео благодаря Кристоферу дал последний намек.

В моем втором классе я использовал

 Runnable run = new Runnable(){ public void run(){ contactsActivity.update(); } }; contactsActivity.runOnUiThread(run); 

Для доступа к обновлению () из моей активности. Это обновление включает

 myAdapter.notifyDataSetChanged(); 

Чтобы сообщить адаптеру об обновлении представления. Насколько я могу судить, работал отлично.

Если вы используете SimpleCursorAdapter, попробуйте вызвать requery () в объекте Cursor.

Если вы еще не удовлетворены ListView Refreshment, вы можете посмотреть этот фрагмент, это для загрузки спискаView из базы данных. Фактически, вам нужно просто перезагрузить ListView после выполнения любой операции CRUD. Это не лучший способ Кода, но он обновит ListView по вашему желанию.

Это работает для меня …. если вы найдете лучшее решение, пожалуйста, разделите …

 .......
 ......
 Делайте свои операции с CRUD.
 ......
 .....
 DBAdapter.open ();
 DBAdapter.insert_into_SingleList ();
 // Принесите этот DB_results и добавьте его в список как его содержимое ....
                                             Ls2.setAdapter (новый ArrayAdapter (DynTABSample.this,
     Android.R.layout.simple_list_item_1, DBAdapter.DB_ListView));
                                             DBAdapter.close ();

Решения, предлагаемые людьми в этом сообщении, работают или не зависят в основном от версии вашего устройства Android. Например, чтобы использовать метод AddAll, вы должны поместить в Android-устройство android: minSdkVersion = "10".

Чтобы решить эти вопросы для всех устройств, я создал свой собственный метод в своем адаптере, и использование внутри метода add и remove наследует от ArrayAdapter, что без проблем обновляет ваши данные.

Мой код: используя собственный класс данных RaceResult, вы используете свою собственную модель данных.

ResultGpRowAdapter.java

 public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> { Context context; int resource; List<RaceResult> data=null; public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects) { super(context, resource, objects); this.context = context; this.resource = resource; this.data = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { ........ } //my own method to populate data public void myAddAll(List<RaceResult> items) { for (RaceResult item:items){ super.add(item); } } 

ResultsGp.java

 public class ResultsGp extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { ........... ........... ListView list = (ListView)findViewById(R.id.resultsGpList); ResultGpRowAdapter adapter = new ResultGpRowAdapter(this, R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data list.setAdapter(adapter); .... .... .... //LOAD a ArrayList<RaceResult> with data ArrayList<RaceResult> data = new ArrayList<RaceResult>(); data.add(new RaceResult(....)); data.add(new RaceResult(....)); ....... adapter.myAddAll(data); //Your list will be udpdated!!! 

Если вы хотите сохранить свою позицию прокрутки во время обновления, и вы можете сделать это:

 if (mEventListView.getAdapter() == null) { EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events); mEventListView.setAdapter(eventLogAdapter); } else { ((EventLogAdapter)mEventListView.getAdapter()).refill(events); } public void refill(List<EventLog> events) { mEvents.clear(); mEvents.addAll(events); notifyDataSetChanged(); } 

Подробную информацию см. В списке Android ListView: поддерживайте свою позицию прокрутки при обновлении .

После удаления данных из представления списка вы должны вызвать refreshDrawableState() . Вот пример:

 final DatabaseHelper db = new DatabaseHelper (ActivityName.this); db.open(); db.deleteContact(arg3); mListView.refreshDrawableState(); db.close(); 

deleteContact метод deleteContact в классе DatabaseHelper будет несколько похож

 public boolean deleteContact(long rowId) { return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0; } 

Мне не удалось получить notifyDataSetChanged (), чтобы работать над обновлением моего SimpleAdapter, поэтому вместо этого я попытался сначала удалить все представления, которые были прикреплены к родительскому макету, используя removeAllViews (), а затем добавить ListView, и это сработало, что позволило мне обновить UI:

 LinearLayout results = (LinearLayout)findViewById(R.id.results); ListView lv = new ListView(this); ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>(); SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } ); for (...) { HashMap<String, String> map = new HashMap<String, String>(); map.put("name", name); map.put("dept", dept); list.add(map); } lv.setAdapter(adapter); results.removeAllViews(); results.addView(lv); 

Для меня после изменения информации в базе данных sql ничто не могло обновить представление списка (чтобы быть конкретным расширяемым представлением списка), поэтому, если notifyDataSetChanged () не помогает, вы можете сначала очистить свой список и добавить его снова после этого вызова notifyDataSetChanged (). Например

 private List<List<SomeNewArray>> arrayList; List<SomeNewArray> array1= getArrayList(...); List<SomeNewArray> array2= getArrayList(...); arrayList.clear(); arrayList.add(array1); arrayList.add(array2); notifyDataSetChanged(); 

Надеюсь, это имеет смысл для вас.

В то время как SimpleCursorAdapter может вызвать changeCursor (newCursor) на адаптере.

Я был тем же самым, когда в фрагменте я хотел заполнить ListView (в одном TextView) с MAC-адресом устройств BLE, отсканированных в течение некоторого времени.

Я сделал это так:

 public class Fragment01 extends android.support.v4.app.Fragment implements ... { private ListView listView; private ArrayAdapter<String> arrayAdapter_string; ... @Override public void onActivityCreated(Bundle savedInstanceState) { ... this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView); ... this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo); this.listView.setAdapter(this.arrayAdapter_string); } @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { ... super.getActivity().runOnUiThread(new RefreshListView(device)); } private class RefreshListView implements Runnable { private BluetoothDevice bluetoothDevice; public RefreshListView(BluetoothDevice bluetoothDevice) { this.bluetoothDevice= bluetoothDevice; } @Override public void run() { Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString())); Fragment01.this.arrayAdapter_string.notifyDataSetChanged(); } } 

Затем ListView начал динамически заполнять MAC-адрес найденных устройств.

Я думаю, это зависит от того, что вы подразумеваете под обновлением. Вы имеете в виду, что дисплей графического интерфейса должен быть обновлен или вы хотите, чтобы дочерние представления были обновлены, чтобы вы могли программно вызвать getChildAt (int) и получить представление, соответствующее тому, что находится в адаптере.

Если вы хотите обновить графический интерфейс пользователя, вызовите функцию notifyDataSetChanged () на адаптере. GUI будет обновлен при следующем перерисовании.

Если вы хотите иметь возможность вызвать getChildAt (int) и получить представление, отражающее то, что находится в адаптере, затем вызвать layoutChildren (). Это приведет к восстановлению дочернего представления из данных адаптера.

У меня был ArrayList, который я хотел отобразить в списке. ArrayList содержит элементы из mysql. Я переопределил метод Refresh, и в этом методе я использовал tablelayout.removeAllViews (); А затем повторил процесс получения данных из базы данных. Но перед этим обязательно очистите ArrayList или любую структуру данных, иначе новые данные будут добавлены к старой.

Если вы хотите обновить список списков пользовательского интерфейса от службы, сделайте адаптер статическим в своей основной деятельности и выполните следующее:

 @Override public void onDestroy() { if (MainActivity.isInFront == true) { if (MainActivity.adapter != null) { MainActivity.adapter.notifyDataSetChanged(); } MainActivity.listView.setAdapter(MainActivity.adapter); } } 

Вам нужно использовать один объект этого списка, в котором данные, которые вы раздуваете, отображаются в виде списка. Если refference is change, функция notifyDataSetChanged () не работает. При удалении элементов из списка также удалите их из списка, в котором вы используете ли Это ArrayList <> или Something else, затем Call notifyDataSetChanged () на объекте вашего класса адаптера.

Итак, посмотрим, как я это сделал в своем адаптере, см. Ниже

 public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{ private Context context; private ArrayList<CountryDataObject> dObj; private ViewHolder holder; private Typeface itemFont; private int selectedPosition=-1; private ArrayList<CountryDataObject> completeList; public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) { this.context = context; this.dObj=dObj; completeList=new ArrayList<CountryDataObject>(); completeList.addAll(dObj); itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf"); } @Override public int getCount() { return dObj.size(); } @Override public Object getItem(int position) { return dObj.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View view, ViewGroup parent) { if(view==null){ holder = new ViewHolder(); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.states_inflator_layout, null); holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator)); holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState); view.setTag(holder); }else{ holder = (ViewHolder) view.getTag(); } holder.textView.setText(dObj.get(position).getCountryName()); holder.textView.setTypeface(itemFont); if(position==selectedPosition) { holder.checkImg.setImageResource(R.drawable.check); } else { holder.checkImg.setImageResource(R.drawable.uncheck); } return view; } private class ViewHolder{ private TextView textView; private ImageView checkImg; } public void getFilter(String name) { dObj.clear(); if(!name.equals("")){ for (CountryDataObject item : completeList) { if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){ dObj.add(item); } } } else { dObj.addAll(completeList); } selectedPosition=-1; notifyDataSetChanged(); notifyDataSetInvalidated(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Registration reg=(Registration)context; selectedPosition=position; reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode()); notifyDataSetChanged(); } 

}

Если вы идете по линиям проводников Android, и вы используете ContentProviders для получения данных из Database и вы показываете их в ListView с помощью CursorLoader и CursorAdapters , то все изменения в связанных данных будут автоматически отображаться в ListView.

getContext().getContentResolver().notifyChange(uri, null); На курсор в ContentProvider будет достаточно, чтобы отразить изменения. Нет необходимости в дополнительной работе.

Но когда вы не используете их все, вам нужно сообщить адаптеру, когда набор данных меняется. Также вам нужно повторно заполнить / перезагрузить свой набор данных (например, список), а затем вам потребуется вызвать notifyDataSetChanged() на адаптере.

notifyDataSetChanged() работает, если изменений в datset нет. Вот комментарий выше метода в документах-

 /** * Notifies the attached observers that the underlying data has been changed * and any View reflecting the data set should refresh itself. */ 

Я смог получить notifyDataSetChanged только, получив новые данные адаптера, а затем сбросив адаптер для представления списка, а затем сделав вызов так:

  expandableAdapter = baseFragmentParent.setupEXLVAdapter(); baseFragmentParent.setAdapter(expandableAdapter); expandableAdapter.notifyDataSetChanged();